因此,经过一些精心的测试,我之前的答案似乎不是万无一失的
(或者甚至 100% 的功能,就此而言),并且容易崩溃。经过一番思考,我决定采取完全不同的方法来解决这个问题......使用Interprocess Communication。
注意...此方法使用DllMain 中的代码。
所以不要太过分了,这样做的时候一定要follow safe practices,这样你就不会落入deadlock...
最值得注意的是,Win32 API 提供了以下有用的功能:
通过使用这些,我们可以简单地告诉我们的 Launcher 进程我们的远程初始化函数所在的位置,straight from the injected dll 本身......
dllmain.cpp:
// Data struct to be shared between processes
struct TSharedData
{
DWORD dwOffset = 0;
HMODULE hModule = nullptr;
LPDWORD lpInit = nullptr;
};
// Name of the exported function you wish to call from the Launcher process
#define DLL_REMOTEINIT_FUNCNAME "RemoteInit"
// Size (in bytes) of data to be shared
#define SHMEMSIZE sizeof(TSharedData)
// Name of the shared file map (NOTE: Global namespaces must have the SeCreateGlobalPrivilege privilege)
#define SHMEMNAME "Global\\InjectedDllName_SHMEM"
static HANDLE hMapFile;
static LPVOID lpMemFile;
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
TSharedData data;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
// Get a handle to our file map
hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, SHMEMSIZE, SHMEMNAME);
if (hMapFile == nullptr) {
MessageBoxA(nullptr, "Failed to create file mapping!", "DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
return FALSE;
}
// Get our shared memory pointer
lpMemFile = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (lpMemFile == nullptr) {
MessageBoxA(nullptr, "Failed to map shared memory!", "DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
return FALSE;
}
// Set shared memory to hold what our remote process needs
memset(lpMemFile, 0, SHMEMSIZE);
data.hModule = hModule;
data.lpInit = LPDWORD(GetProcAddress(hModule, DLL_REMOTEINIT_FUNCNAME));
data.dwOffset = DWORD(data.lpInit) - DWORD(data.hModule);
memcpy(lpMemFile, &data, sizeof(TSharedData));
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
// Tie up any loose ends
UnmapViewOfFile(lpMemFile);
CloseHandle(hMapFile);
break;
}
return TRUE;
UNREFERENCED_PARAMETER(lpReserved);
}
然后,从我们的 Launcher 应用程序中,我们将执行通常的
CreateProcess +
VirtualAllocEx +
CreateRemoteThread 技巧来注入我们的 Dll,确保将指向正确
SECURITY_DESCRIPTOR 的指针作为第三个参数传递给
CreateProcess ,以及在第 6 个参数中传递
CREATE_SUSPENDED 标志。
这是为了帮助确保您的子进程将拥有适当的权限来读取和写入全局共享内存命名空间,尽管还有其他方法可以实现这一点(或者您可以完全不使用全局路径进行测试)。
CREATE_SUSPENDED 标志将确保 dllmain 入口点函数在加载其他库之前完成对我们共享内存的写入,这允许以后更轻松地进行本地挂钩...
Injector.cpp:
SECURITY_ATTRIBUTES SecAttr, *pSec = nullptr;
SECURITY_DESCRIPTOR SecDesc;
if (InitializeSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION) &&
SetSecurityDescriptorDacl(&SecDesc, TRUE, PACL(nullptr), FALSE))
{
SecAttr.nLength = sizeof(SecAttr);
SecAttr.lpSecurityDescriptor = &SecDesc;
SecAttr.bInheritHandle = TRUE;
pSec = &SecAttr;
}
CreateProcessA(szTargetExe, nullptr, pSec, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);
将 DLL 注入目标进程后,您需要做的就是使用相同(或多或少)从 DLL 项目到 Launcher 项目的文件映射代码(当然,设置共享内存内容的部分除外) .
然后,调用你的远程函数只是一个简单的问题:
// Copy from shared memory
TSharedData data;
memcpy(&data, lpMemFile, SHMEMSIZE);
// Clean up
UnmapViewOfFile(lpMemFile);
CloseHandle(hMapFile);
// Call the remote function
DWORD dwThreadId = 0;
auto hThread = CreateRemoteThread(hProcess, nullptr, 0, LPTHREAD_START_ROUTINE(data.lpInit), nullptr, 0, &dwThreadId);
然后你可以在目标进程的主线程上ResumeThread,或者从你的远程函数。
作为额外的好处......使用这种通信形式还可以为我们的 Launcher 进程打开几扇门,因为它现在可以直接与目标进程进行通信。
但同样,请确保您不要在
DllMain 中做太多事情,如果可能的话,只需使用远程初始化函数(例如,
use named mutexes 也是安全的)创建一个单独的共享内存映射并从那里继续通信。
希望这对某人有帮助! =)