我想在我制作的注入的 DLL 的远程进程中调用一个函数。
我已经成功地为我的 DLL 注入了:
CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA"), pLibRemote, 0, NULL);
执行 DllMain 并且 DLL 在待机模式下运行。我想做的是以某种方式调用远程加载的DLL以做一些工作。
我尝试像这样导出函数:
extern "C" __declspec(dllexport) void MyFunc(void)
然后像这样执行函数:
CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("mydll"), "MyFunc"), NULL, 0, NULL);
但它会导致崩溃。
我该如何解决这个问题?
调用 GetModuleHandle
将获得 DLL 的基础,因为它映射到您的进程中(如果有的话)。因此,您需要做的是首先确保在 DLL 中导出函数。您可以按照原样执行操作,也可以创建.def
文件,如下所示。此后:
理论上
- 将 DLL 注入目标进程并获取加载它的基址
- 将 DLL 注入当前进程。使用
GetProcAddress
查找导出的函数与 DLL 基之间的偏移量。 - 将此偏移量添加到从步骤 1 获得的基址。
CreateRemoteThread
这个位置。
实践中
在执行 DLL 注入时,您可以获得将 DLL 加载到目标中的基础。
HMODULE hInjected;
hThread = CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)( GetProcAddress( hMod,
"LoadLibraryW" ) ), lpAddress, 0, NULL );
// Locate address our payload was loaded
if( hThread != 0 ) {
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, ( LPDWORD )&hInjected );
CloseHandle( hThread );
}
hInjected
将是注入的 DLL 的基础。然后我有另一个功能:
void* GetPayloadExportAddr( LPCWSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName ) {
// Load payload in our own virtual address space
HMODULE hLoaded = LoadLibrary( lpPath );
if( hLoaded == NULL ) {
return NULL;
} else {
void* lpFunc = GetProcAddress( hLoaded, lpFunctionName );
DWORD dwOffset = (char*)lpFunc - (char*)hLoaded;
FreeLibrary( hLoaded );
return (DWORD)hPayloadBase + dwOffset;
}
}
这样做是首先将有效负载加载到我们自己的虚拟地址空间中。之后我们可以使用 GetProcAddress
来获取导出函数的地址。由此,我们可以从 DLL 的基中获取函数的偏移量。将此偏移量添加到我们之前获得的hInjected
将告诉我们应该在哪里进行CreateRemoteThread
调用。所以你可以像这样拨打电话:
BOOL InitPayload( HANDLE hProcess, LPCWSTR lpPath, HMODULE hPayloadBase, HWND hwndDlg ) {
void* lpInit = GetPayloadExportAddr( lpPath, hPayloadBase, "Init" );
if( lpInit == NULL ) {
return FALSE;
} else {
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,
lpInit, hwndDlg, 0, NULL );
if( hThread == NULL ) {
return FALSE;
} else {
CloseHandle( hThread );
}
}
return TRUE;
}
这是从我拥有的旧项目中撕下来的所有代码。欢迎你拿走代码并用它做任何你想做的事情,但我知道如果我现在重写代码,我会做很多不同的事情。
如果您将 32 位 DLL 注入 32 位进程,Mike 的答案有效。
如果要将 64 位 DLL 注入64 位进程,则无法从GetExitCodeThread
获取 DLL 的基址,因为它只会为您提供 64 位地址的较低 32 位。
在这种情况下,要获得正确的地址,您必须将代码块写入调用LoadLibrary
的进程(将结果存储在进程内存中的特定位置),执行此代码块(使用 CreateRemoteThread
),然后使用 ReadProcessMemory
从该位置读回地址。
您可以在此处找到更多详细信息(包括PowerShell和ASM代码):http://clymb3r.wordpress.com/2013/05/26/implementing-remote-loadlibrary-and-remote-getprocaddress-using-powershell-and-assembly/
然后,您可以按照 Mike 描述的相同方式计算导出函数的偏移量,但请注意将差值存储在 64 位值中,而不是存储在 DWORD(32 位)中。