我想将一个函数create()
注入目标进程(在本例中为notepad.exe)。这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <TlHelp32.h>
void create(wchar_t wRemoteBuffer[2][60]) //the function to be injected to the target proccess (notepad.exe)
{
LPCWSTR lpFileName = wRemoteBuffer[0];
//wchar_t *lpFileName = L"C:\\CodeInjectTest.txt";
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, NULL, NULL);
//BYTE bBuffer[] = "if you see this file,then the CodeInjectTest has succeed\n";
LPCWSTR lpBuffer = wRemoteBuffer[1];
DWORD dNumberOfByteToWrite;
WriteFile(hFile, lpBuffer, sizeof(lpBuffer), &dNumberOfByteToWrite, NULL);
}
int GetTargetProcessId()
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
DWORD ProcessId;
HANDLE hHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hHandle == INVALID_HANDLE_VALUE)
{
printf("fail to call\n");
exit(-1);
}
int TargetProcessId;
const wchar_t *target = L"notepad.exe";
BOOL bMore = ::Process32First(hHandle, &pe32);
while(bMore)
{
printf("the name is %ws ,the id is %d\n", pe32.szExeFile, pe32.th32ProcessID);
if (wcscmp(pe32.szExeFile, target) == 0)
{
TargetProcessId = pe32.th32ProcessID;
return TargetProcessId;
}
bMore = ::Process32Next(hHandle, &pe32);
}
return -1;
}
int main()
{
wchar_t wBuffer[2][60] = {L"C:\\CodeInjectTest.txt", L"if you see this file,then the CodeInjectTest has succeed\n"};
HANDLE hTargetHandle;
int TargetProcessId = GetTargetProcessId();
hTargetHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TargetProcessId);
if (hTargetHandle == INVALID_HANDLE_VALUE)
{
DWORD d = GetLastError();
printf("openprocess fail\n");
printf("the TargetProcessId is: %d\n", TargetProcessId);
printf("the result of getlast is: %d\n", d);
system("PAUSE");
exit(-1);
}
DWORD dwBufferSize = (DWORD)GetTargetProcessId - (DWORD)create;
LPVOID lpProcBaseAddress = VirtualAllocEx(hTargetHandle, NULL, dwBufferSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (lpProcBaseAddress == NULL)
{
DWORD d = GetLastError();
printf("virtualallocex has fail\n");
printf("the last error is:%d\n", d);
system("PAUSE");
exit(-1);
}
BOOL a = WriteProcessMemory(hTargetHandle, lpProcBaseAddress, create, dwBufferSize, NULL); //write the function to be injected to the target process
if (a == 0)
{
DWORD d = GetLastError();
printf("writeprocessmemory has fail\n");
printf("the last error is %d\n", d);
system("PAUSE");
exit(-1);
}
LPVOID lpRemoteBuffer = ::VirtualAllocEx(hTargetHandle, NULL, sizeof(wBuffer[0]) + sizeof(wBuffer[1]), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (lpRemoteBuffer == NULL)
{
DWORD d = GetLastError();
printf("buffer virtualallocex has fail\n");
printf("the last error is:%d\n", d);
system("PAUSE");
exit(-1);
}
BOOL b = ::WriteProcessMemory(hTargetHandle, lpRemoteBuffer, wBuffer, sizeof(wBuffer[0]) + sizeof(wBuffer[1]), NULL); //write the parameter the create function needs to the target process
if (b == 0)
{
DWORD d = GetLastError();
printf("buffer writeprocessmemory has fail\n");
printf("the last error is:%d\n", d);
system("PAUSE");
exit(-1);
}
DWORD dwThreadId;
HANDLE hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)lpProcBaseAddress, lpRemoteBuffer, 0, &dwThreadId);
if (hRemoteThreadHandle != NULL)
{
WaitForSingleObject(hRemoteThreadHandle, 5000);
printf("succeed\n");
system("PAUSE");
return 0;
}
system("PAUSE");
return 1;
}
但是,调用CreateRemoteThread()
时目标进程崩溃,并且未调用create()
函数。但CreateRemoteThread()
的返回值不是NULL。
我在Windows XP Pro SP3上运行此程序。首先,在运行此程序时,它会显示一个对话框,显示DEP已关闭notepad.exe以保护系统,并且notepad.exe崩溃。但我已将PAGE_EXECUTE_READWRITE
传递给VirtualAllocEx()
的最后一个参数。然后我关闭DEP对话框,然后再次运行程序。这次它没有显示DEP对话框,但notepad.exe仍然崩溃。
谁能告诉我问题出在哪里?
答案 0 :(得分:3)
这是实现代码注入的一种非常糟糕的方法。
首先,您对create()
函数的字节大小的计算无法保证以您期望的方式工作。它依赖GetTargetProcessId()
在create()
之后立即位于内存中,但编译器/链接器无法保证这一点。它们可能是相反的顺序,或者它们之间可能有其他代码。
其次,即使您可以像尝试一样将整个函数的原始字节复制到远程进程中,create()
函数仍可能会失败,因为:
开头宣称它是错误的。它与LPTHREAD_START_ROUTINE
期望的签名不匹配。特别是,它的返回值和调用约定都是错误的,这将导致调用堆栈管理不善。
create()
静态调用CreateFileW()
和WriteFile()
,因此它依赖于您的PROCESS的地址空间中kernel32.dll
的加载内存地址,这可能与TARGET PROCESS的地址空间中加载的地址。如果启用了Address Space Layout Randomization,这一点尤为重要。
有much cleaner and safer ways来实现代码注入,例如将create()
代码移动到单独的DLL中,然后使用CreateRemoteThread()
到load that DLL into the target process。当DLL加载到目标进程时,其DLL_PROCESS_ATTACH
处理程序可以正常调用其create()
代码而不使用丑陋的黑客。
使用LoadLibrary()
作为远程线程过程注入DLL,并使用(远程分配的)指向DLL文件名的指针作为线程参数。
在您自己的流程中使用GetProcAddress()
获取指向您流程中LoadLibrary()
的指针。如果未启用ASLR,并且kernel32.dll
未在目标进程中重新基于,则可以将该LoadLibrary()
指针直接传递给CreateRemoteThread()
作为线程过程,因为{的加载地址为{ {1}},因此kernel32.dll
的地址在两个流程中都是相同的。
但是,如果涉及ALSR或变基,则必须先调整LoadLibrary()
指针,然后再将其传递给LoadLibrary()
。您可以使用GetModuleHandle()
在自己的流程中获取CreateRemoteThread()
的加载地址。要在目标流程中获取kernel32.dll
的加载地址,请使用CreateToolhelp32Snapshot(TH32CS_SNAPMODULE)
/ Module32First()
/ Module32Next()
或EnumProcessModules()
或EnumProcessModulesEx()
。如果两个地址不同,请在将kernel32.dll
指针传递给LoadLibrary()
之前按差异进行调整。
以这种方式使用CreateRemoteThread()
是有效的,因为LoadLibrary()
的签名与LoadLibrary()
兼容。这还有一个额外的好处,即LPTHREAD_START_ROUTINE
的返回值成为线程的退出代码,因此您的应用可以调用LoadLibrary()
,等待线程在CreateRemoteThread()
退出时终止,然后抓取用于确定注入是否成功的退出代码(但如果LoadLibrary()
失败,则无法访问实际的错误代码)。
尝试这样的事情:
DLL:
LoadLibrary()
EXE:
BOOL create()
{
HANDLE hFile = CreateFileW(L"C:\\CodeInjectTest.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
const char *lpBuffer = "if you see this file, then the CodeInjectTest has succeed\n";
DWORD dNumberOfByteToWrite;
WriteFile(hFile, lpBuffer, strlen(lpBuffer), &dNumberOfByteToWrite, NULL);
CloseHandle(hFile);
return TRUE;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
// your injected code here...
return create();
}
return TRUE;
}
要注意的另一件事是32位对64位。如果您的应用程序在64位系统上运行,您将必须编译DLL的单独版本,然后检测目标进程的实际位数(您可以使用IsWow64Process()
),以便您可以决定DLL的哪个版本注入。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <shlwapi.h>
DWORD GetTargetProcessId(const wchar_t *target)
{
DWORD TargetProcessId = 0;
PROCESSENTRY32 pe32 = {0};
pe32.dwSize = sizeof(pe32);
HANDLE hHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hHandle == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot failed with error %u\n", GetLastError());
return 0;
}
if (!Process32First(hHandle, &pe32))
{
if (GetLastError() != ERROR_NO_MORE_FILES)
printf("Process32First failed with error %u\n", GetLastError());
}
else
{
do
{
printf("[%u] %ws\n", pe32.th32ProcessID, pe32.szExeFile);
if (wcscmp(pe32.szExeFile, target) == 0)
{
TargetProcessId = pe32.th32ProcessID;
break;
}
if (!Process32Next(hHandle, &pe32))
{
if (GetLastError() != ERROR_NO_MORE_FILES)
printf("Process32Next failed with error %u\n", GetLastError());
break;
}
}
while (true);
}
CloseHandle(hHandle);
return TargetProcessId;
}
LPVOID GetTargetProcAddress(HANDLE hProcess, const wchar_t *szWantedModule, const char *szProcName)
{
// note, there is a very interesting gotcha in a comment to this answer:
//
// Would ASLR cause friction for the address with DLL injection?
// http://stackoverflow.com/a/8569008/65863
//
// "The address of the module may not change but that does not make
// what the OP is doing safe! Consider the case where your app is
// running with shims enabled (something which you do not control!)
// or even the case where some other pieces of software which also
// performs EAT hooks is running in your process (again, not something
// you control). In that case, GetProcAddress could return a pointer
// to a function in another module to what you're expecting/asking,
// including one which is not loaded in the process which you're going
// to call CreateRemoteThread on, in that case the target will crash."
//
// you probably won't run into this very often, if ever, but you should
// be aware of it nonetheless!
HANDLE hLocalMod = GetModuleHandleW(szWantedModule);
LPVOID lpProcAddress = GetProcAddress(hLocalMod, szProcName);
if (!lpProcAddress)
{
printf("GetProcAddress failed with error %u\n", GetLastError());
return NULL;
}
// return lpProcAddress;
HANDLE hRemoteMod = NULL;
wchar_t szModName[MAX_PATH];
HMODULE hMods[1024];
DWORD cbNeeded;
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
printf("EnumProcessModules failed with error %u\n", GetLastError());
return NULL;
}
cbNeeded /= sizeof(HMODULE);
for (DWORD i = 0; i < cbNeeded; ++i)
{
if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(wchar_t)))
{
if (wcscmp(PathFindFileNameW(szModName), szWantedModule) == 0)
{
hRemoteMod = hMods[i];
break;
}
}
}
if (!hRemoteMod)
{
printf("Cannot find %ws in remote process\n", szWantedModule);
return NULL;
}
if (hLocalMod != hRemoteMod)
lpProcAddress = (LPVOID)((INT_PTR)hRemoteMod - (INT_PTR)hLocalMod);
return lpProcAddress;
}
int main()
{
DWORD TargetProcessId = GetTargetProcessId(L"notepad.exe");
if (TargetProcessId == 0)
{
system("PAUSE");
return -1;
}
printf("TargetProcessId: %u\n", TargetProcessId);
HANDLE hTargetHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, TargetProcessId);
if (!hTargetHandle)
{
printf("OpenProcess failed with error %u\n", GetLastError());
system("PAUSE");
return -1;
}
LPVOID lpRemoteProcAddress = GetTargetProcAddress(hTargetHandle, L"kernel32.dll", "LoadLibraryW");
if (!lpRemoteProcAddress)
{
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
const wchar_t lpFilename[] = L"C:\\CodeInjectTest.dll";
DWORD dwBufferSize = sizeof(lpFilename);
LPVOID lpRemoteBuffer = VirtualAllocEx(hTargetHandle, NULL, dwBufferSize, MEM_COMMIT, PAGE_READWRITE);
if (!lpRemoteBuffer)
{
printf("VirtualAllocEx failed with error %u\n", GetLastError());
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
if (!WriteProcessMemory(hTargetHandle, lpRemoteBuffer, lpFilename, dwBufferSize, NULL);
{
printf("WriteProcessMemory failed with error %u\n", GetLastError());
VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
DWORD dwThreadId;
HANDLE hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, lpRemoteProcAddress, lpRemoteBuffer, 0, &dwThreadId);
if (!hRemoteThreadHandle)
{
printf("CreateRemoteThread failed with error %u\n", GetLastError());
VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
WaitForSingleObject(hRemoteThreadHandle, INFINITE);
DWORD dwExitCode;
GetExitCodeThread(hRemoteThreadHandle, &dwExitCode);
CloseHandle(hRemoteThreadHandle);
VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
if (dwExitCode == 0)
{
printf("Remote LoadLibrary failed\n");
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
/* optional, depending on your DLL's needs...
lpRemoteProcAddress = GetTargetProcAddress(hTargetHandle, L"kernel32.dll", "FreeLibrary");
if (lpRemoteProcAddress)
{
hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, lpRemoteProcAddress, (void*)dwExitCode, 0, &dwThreadId);
if (hRemoteThreadHandle)
{
WaitForSingleObject(hRemoteThreadHandle, INFINITE);
CloseHandle(hRemoteThreadHandle);
}
}
*/
CloseHandle(hTargetHandle);
printf("success\n");
system("PAUSE");
return 0;
}
答案 1 :(得分:0)
首先,没有ALSR问题,几乎所有进程都具有相同的kernel32.dll基地址。我们可以通过尝试Remy Lebeau所说的方法来确认这一点,它使用CreateRemoteThread()创建一个名为的远程线程的LoadLibrary。
其次,如果你想以这种方式使用CreateRemoteThread(),你应该确保你的create()函数中没有其他模块(比如dll)的功能。但是为什么呢?
有关在Windows中调用函数的一些细节。如果使用Olly调试.exe文件,您可能会注意到在调用函数时,Olly可能会显示如下代码:
二进制代码|装配说明
因此,因为您在目标进程中复制了create()函数,换句话说,您在目标进程中复制了二进制代码,所以在操作跳转到第三行的第二行时可能存在访问冲突然而,这并没有复制到目标进程中。也许目标进程已经损坏了。
有一张描述这种情况的照片:
我们还应该注意另外一件事,那就是.exe文件中的导入表。我们知道当我们调用DLL导出的函数时,系统将在.idata中的导入表中查找函数地址但是在上面的情况下,当Olly跳转到0x0040100C时,系统将使用原始进程的导入表的地址查找目标的导入表,这也可能会导致每个进程的导入表的地址不同。破坏。
所以,如果你想使用CreateRemoteThread(),你可以像Remy Lebeau所说的那样使用DLL注入技术,或者直接使用你所做的方法,但一定不要在create中调用其他DLL导出的函数( )功能。
答案 2 :(得分:-1)
我不知道你的想法:
DWORD dwBufferSize=(DWORD)GetTargetProcessId-(DWORD)create;
将要做,但它可能不会。 a)进程ID不是指针; b)如果你编译了64位应用程序,那么无论如何指针都不适合DWORD。
此外,您看起来正在尝试复制create
函数的可执行文件 - 但是数据段呢? (比如CreateFile的地址)。