x86 DLL注入32位进程后DllMain未被调用的排查求助
我有一个编译为DLL的Qt GUI程序,x64编译时注入正常,但x86编译后注入32位进程时,其DllMain入口从未被调用。经过排查和重编译Qt源码,发现这可能不是Qt的问题——我用一个无依赖的纯DLL项目也复现了该问题:
#include <Windows.h> BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } OutputDebugString(L"DllMain"); return TRUE; }
我的注入代码如下:
void injectDLL(DWORD processID, const QString& dllPath) { // Open the target process. HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); if (!processHandle) { qDebug() << "Failed to open process"; return; } // Allocate memory in the target process. LPVOID remoteMemory = VirtualAllocEx(processHandle, NULL, dllPath.length(), MEM_COMMIT, PAGE_READWRITE); if (!remoteMemory) { qDebug() << "Failed to allocate memory in the remote process"; CloseHandle(processHandle); return; } // Write the path of the DLL to be injected into the allocated memory in the target process. if (!WriteProcessMemory(processHandle, remoteMemory, dllPath.toStdWString().c_str(), dllPath.length() * sizeof(wchar_t), NULL)) { qDebug() << "Failed to write path to the remote process memory"; VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE); CloseHandle(processHandle); return; } // Get the address of the LoadLibraryW function. LPVOID loadLibraryAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); if (!loadLibraryAddress) { qDebug() << "Failed to get the address of LoadLibraryW"; VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE); CloseHandle(processHandle); return; } // Create a remote thread in the target process and pass the address of LoadLibraryW and the address of the allocated memory as arguments. HANDLE remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddress, remoteMemory, 0, NULL); if (!remoteThread) { qDebug() << "Failed to create a remote thread in the target process"; VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE); CloseHandle(processHandle); return; } // Wait for the remote thread to finish. WaitForSingleObject(remoteThread, INFINITE); // Cleanup. CloseHandle(remoteThread); VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE); CloseHandle(processHandle); } int main() { // wmplayer.exe pid injectDLL(51836, "D:/repos/Dll/Debug/Dll.dll"); return 0; }
测试注入的目标进程是32位的C:\Program Files (x86)\Windows Media Player\wmplayer.exe。使用VS22调试该DLL,但注入后DllMain从未被调用,OutputDebugString的内容也未输出。推测Qt DLL的问题与测试用纯DLL一致,请问哪里遗漏或操作错误?
以下是几个关键问题点和修复方案:
1. 注入器与目标进程位数不匹配
注入器必须和目标进程位数完全一致:如果目标是32位进程,注入器必须编译为x86。x64编译的注入器无法正确调用32位进程的系统接口,内存地址空间和调用逻辑的差异会导致注入失败。
修复:将注入器项目的编译平台改为x86,重新编译后再执行注入操作。
2. 内存分配未预留字符串终止符
LoadLibraryW需要接收以NULL结尾的宽字符串,但当前VirtualAllocEx仅分配了等于路径长度的内存,没有预留sizeof(wchar_t)的空间存放终止符,会导致LoadLibraryW读取到垃圾数据,无法定位DLL路径。
修复:调整内存分配和写入的参数:
// 分配内存时预留终止符空间 LPVOID remoteMemory = VirtualAllocEx(processHandle, NULL, (dllPath.length() + 1) * sizeof(wchar_t), MEM_COMMIT, PAGE_READWRITE); // 写入时包含终止符 size_t totalPathSize = (dllPath.length() + 1) * sizeof(wchar_t); if (!WriteProcessMemory(processHandle, remoteMemory, dllPath.toStdWString().c_str(), totalPathSize, NULL)) { // 错误处理逻辑 }
3. 过高的进程权限请求
PROCESS_ALL_ACCESS权限要求过高,多数系统环境下无法获取,会导致OpenProcess失败或后续操作受限。建议使用最小必要权限集合。
修复:修改OpenProcess的权限参数:
HANDLE processHandle = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, processID);
4. 未检查DLL加载结果
当前代码没有验证LoadLibraryW的执行结果,无法确认DLL是否被成功加载。可以通过GetExitCodeThread获取远程线程的返回值:
WaitForSingleObject(remoteThread, INFINITE); DWORD exitCode = 0; GetExitCodeThread(remoteThread, &exitCode); if (exitCode == NULL) { qDebug() << "LoadLibraryW调用失败,DLL未加载"; }
若返回值为NULL,说明LoadLibraryW执行失败,可进一步排查DLL路径是否正确、DLL是否存在缺失的依赖库。
5. 调试方式错误
调试DLL时,需将VS调试器附加到**目标进程(wmplayer.exe)**而非注入器。在DLL_PROCESS_ATTACH分支添加断点,注入完成后断点会触发。另外,OutputDebugString的输出需要通过DebugView等工具捕获,确保工具已监听目标进程的调试输出。
内容的提问来源于stack exchange,提问作者Cesar

