如何防止C++编写的DLL被重复注入以避免程序崩溃?
检测C++注入DLL是否已存在于目标进程中
当重复注入DLL导致目标进程崩溃时,你可以通过以下两种常用方法实现DLL自检测,避免重复加载:
方法一:使用全局互斥量(推荐,可靠)
利用Windows内核对象的唯一性,在DLL加载时创建一个唯一命名的互斥量,若创建失败且错误码为ERROR_ALREADY_EXISTS,则说明DLL已注入当前进程。
代码示例
#include <windows.h> #include <string> // 定义唯一互斥量名称,建议使用GUID或包含DLL专属标识避免冲突 #define MUTEX_NAME L"Global\\MyInjectedDLL_5F4B3A2C-1D6E-7G8H-9I0J-KL1M2N3O4P5Q" BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 禁用线程通知,减少不必要的回调 DisableThreadLibraryCalls(hModule); // 创建互斥量,Global前缀确保跨用户会话生效(按需选择) HANDLE hMutex = CreateMutexW(NULL, TRUE, MUTEX_NAME); if (!hMutex) { MessageBoxW(NULL, L"互斥量创建失败", L"错误", MB_ICONERROR); return FALSE; } // 检查互斥量是否已存在 if (GetLastError() == ERROR_ALREADY_EXISTS) { MessageBoxW(NULL, L"DLL已注入当前进程,无需重复操作", L"提示", MB_ICONINFORMATION); CloseHandle(hMutex); return FALSE; // 返回FALSE阻止DLL加载 } break; case DLL_PROCESS_DETACH: // 进程卸载DLL时释放互斥量 HANDLE hMutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME); if (hMutex) { CloseHandle(hMutex); } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return TRUE; }
方法二:枚举进程模块检查
通过枚举当前进程已加载的模块,对比模块文件名判断自身是否已注入。
代码示例
#include <windows.h> #include <psapi.h> #include <string> #pragma comment(lib, "psapi.lib") BOOL IsDllAlreadyLoaded(HMODULE hCurrentModule) { HMODULE hModules[1024]; DWORD cbNeeded; if (!EnumProcessModules(GetCurrentProcess(), hModules, sizeof(hModules), &cbNeeded)) return FALSE; // 获取当前DLL的文件名(仅取文件名,忽略路径) WCHAR szCurrentDll[MAX_PATH]; GetModuleFileNameW(hCurrentModule, szCurrentDll, MAX_PATH); std::wstring strCurrentName = szCurrentDll; size_t slashPos = strCurrentName.find_last_of(L"\\/"); if (slashPos != std::wstring::npos) strCurrentName = strCurrentName.substr(slashPos + 1); // 遍历所有加载的模块 DWORD moduleCount = cbNeeded / sizeof(HMODULE); for (DWORD i = 0; i < moduleCount; i++) { if (hModules[i] == hCurrentModule) continue; // 跳过当前正在加载的实例 WCHAR szModule[MAX_PATH]; if (GetModuleFileNameW(hModules[i], szModule, MAX_PATH)) { std::wstring strModuleName = szModule; slashPos = strModuleName.find_last_of(L"\\/"); if (slashPos != std::wstring::npos) strModuleName = strModuleName.substr(slashPos + 1); // 不区分大小写对比文件名 if (_wcsicmp(strCurrentName.c_str(), strModuleName.c_str()) == 0) return TRUE; } } return FALSE; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hModule); if (IsDllAlreadyLoaded(hModule)) { MessageBoxW(NULL, L"DLL已注入当前进程", L"提示", MB_ICONINFORMATION); return FALSE; } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
注意事项
- 互斥量命名必须唯一,推荐使用GUID生成,避免与其他程序冲突;若无需跨用户会话,可去掉
Global\\前缀。 - 若目标进程无UI线程,弹窗可能无法显示,此时可直接返回
FALSE退出DLL加载,无需弹窗。 - 枚举模块的方法存在轻微性能开销,适合对跨会话检测无需求的场景。
内容的提问来源于stack exchange,提问作者NoobDev
相关产品推荐
相关产品推荐

