为了让某个进程运行我们的Dll,可以使用远程线程来达到目的。
为了达到这个目的,我们必须让该进程调用我们的Dll。怎样才能让目标进程来调用我们的DLL呢?首先不可能是隐式的,因为隐式调用Dll的名字必须要在进程的导入表中出现,修改程序的导入表将非常麻烦(有空偶再研究一下)。最直接的方式就是显式的调用LoadLibrary函数来调用我们的Dll。好主意!但是我们怎么样才能让目标进程去执行该动作呢,也就是去调用LoadLibrary函数呢?我们不可能去修改源代码来增加一个线程来执行吧。幸好,windows提供了一个API——CreateRemoteThread,他可以让目标进程创建一个线程!太神奇了!也就是说只要获取到了目标进程的进程句柄(这个容易实现)就可以给他创建一个线程。Good!然后在线程函数中执行调用我们Dll的代码,像这样
CreateRemoteThread(hRemoteProcess, NULL, 0, LoadLibraryW, "C:\\MyDll.dll", 0, NULL) ;慢着,有两个小问题。首先,目标进程真的会执行LoadLibraryW吗?不会!!!在编译和链接一个程序的时候,生成的二进制文件中会包含一个导入段,这个导入段由一些列转换函数(thunk)构成,这些转换函数用来跳转到导入的函数。因此,在代码调用诸如LoadLibraryW之类的函数时,链接器会生成一个调用,来调用我们模块中导入段中的一个转换函数,这个转换函数然后会跳到实际的函数,所以调用CreateRemoteThread时线程函数的地址是我们模块导入段的LoadLibraryW转换函数的地址,我们必须通过下面的方式来得到LoadLibraryW的地址。
GetProcAddress(GetModuleHandle(_T("Kernel32")), "LoadLibraryW")。
因为每个程序到需要调用Kernel32.dll,所以这个方法是可以的。还一个问题是你给线程函数的参数传入"C:\\MyDll.dll"就行啦?当然不行!"C:\\MyDll.dll"是这个进程中的字符串地址,而不是目标进程中的地址!我们必须传递目标进程中该字符串的地址。这两个函数VirtualAllocEx,WriteProcessMemory可以解决问题,一个用来在目标进程中分配地址,一个用来给地址赋值,正好,呵呵。然后调用CreateRemoteThread就OK了,下面给出完整代码:
#include "stdafx.h" #include <Windows.h> int _tmain(int argc, _TCHAR* argv[]) { HWND hwndTarget = FindWindow(NULL, _T("TestDllInj")) ; if(hwndTarget == NULL){ printf("could not find the window!!!\n") ; return 1 ; } DWORD dwTargetProId ; GetWindowThreadProcessId(hwndTarget, &dwTargetProId) ; HANDLE hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetProId) ; PVOID pMemRomote = VirtualAllocEx(hRemoteProcess, 0, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE) ; if(WriteProcessMemory(hRemoteProcess, pMemRomote, _T("C:\\MyDll.dll"), 1024, NULL) == NULL){ printf("can not write to mem of the remote process!!!\n") ; return 3; } PTHREAD_START_ROUTINE pfnAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")), "LoadLibraryW") ; HANDLE thrd = CreateRemoteThread(hRemoteProcess, NULL, 0, pfnAddr, pMemRomote, 0, NULL) ; if(thrd == NULL){ printf("create remote thread failed!!!\n") ; } else{ printf("Inject dll sucessed!!!\n") ; } return 0; }