注入DLL的DllMain执行代码导致注入超时问题求助
我来帮你拆解下这个问题——你遇到的超时情况其实是DLL注入里非常典型的新手坑,核心问题出在DllMain的执行上下文和你注入代码的等待逻辑上。
为什么空DllMain正常,加MessageBox就超时?
当你用CreateRemoteThread调用目标进程的LoadLibraryA时,这个远程线程会负责加载你的DLL,并触发DllMain以DLL_PROCESS_ATTACH的原因执行。如果此时你在DllMain里调用MessageBox,这个模态对话框会阻塞当前的远程线程——也就是那个正在执行LoadLibraryA的线程。而你的注入代码里用WaitForSingleObject(thread, 5000)等待这个线程结束,线程被对话框卡住没法返回,自然就会触发超时。
另外,微软的官方文档明确警告过:DllMain只能执行极其简单的初始化操作(比如设置全局变量、注册简单回调),绝对不能调用任何可能阻塞、依赖用户交互,或者会触发其他DLL加载/卸载的API(比如User32、GDI系列API,或者LoadLibrary/FreeLibrary)。你在DllMain里调用MessageBox本身就违反了这个规则,容易引发各种死锁或异常。
正确的解决方法
把需要执行的逻辑(比如MessageBox)放到一个独立的线程里,让DllMain立刻返回,这样远程线程(执行LoadLibraryA的那个)就能正常结束,你的注入代码就不会超时,而新线程可以安全地执行你的业务逻辑。
修改后的DLL代码示例:
#include <windows.h> // 把初始化逻辑放到独立线程函数里 DWORD WINAPI InitThread(LPVOID lpParam) { MessageBox(NULL, "Hello, world!", "Hello!", MB_OK); // 这里可以添加其他你需要的初始化操作 return 0; } extern "C" BOOL WINAPI DllMain(HINSTANCE hDllHandle, DWORD dwReason, LPVOID lpreserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: // 创建新线程执行初始化,不阻塞DllMain所在的远程线程 CreateThread(NULL, 0, InitThread, NULL, 0, NULL); break; case DLL_PROCESS_DETACH: break; }; return TRUE; }
额外的代码优化建议
你的注入代码里有个小问题:VirtualFreeEx的位置不对,现在不管VirtualAllocEx是否成功都会调用它,容易引发错误。应该把它移到baseAddress分配成功的代码块里:
bool InjectDLL(PROCESS_INFORMATION* pInfo, const char* dllPath) { bool result = false; HANDLE nmsProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pInfo->dwProcessId); if (nmsProcess) { LPVOID baseAddress = VirtualAllocEx(nmsProcess, NULL, strlen(dllPath) + 1, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (baseAddress) { LPVOID loadLibraryAddress = (LPVOID)GetProcAddress(LoadLibraryA("kernel32.dll"), "LoadLibraryA"); WriteProcessMemory(nmsProcess, baseAddress, dllPath, strlen(dllPath) + 1, NULL); HANDLE thread = CreateRemoteThread(nmsProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddress, baseAddress, 0, 0); if (thread != NULL) { switch (WaitForSingleObject(thread, 5000)) { case WAIT_OBJECT_0: cout << "Injected" << endl; result = TRUE; break; case WAIT_ABANDONED: cout << "Abandoned" << endl; break; case WAIT_TIMEOUT: cout << "Timed out" << endl; break; case WAIT_FAILED: cout << "Failed"<< endl; break; } CloseHandle(thread); } else { cout << "Error: \n" << GetLastError() << endl; } // 只有当baseAddress分配成功时才释放 VirtualFreeEx(nmsProcess, baseAddress, 0, MEM_RELEASE); } else { cout << "Error: \n" << GetLastError() << endl; } CloseHandle(nmsProcess); } return result; }
最后再提醒一句:确保你的DLL和目标进程的位数一致(32位DLL对应32位进程,64位对应64位),否则也会出现各种奇怪的注入失败或异常。
内容的提问来源于stack exchange,提问作者Ben Spencer

