如何在注入的DLL中拦截程序强制退出并执行清理操作?
注入DLL在宿主进程终止前执行清理的解决方案
为什么__attribute__((destructor))没生效
__attribute__((destructor))是GCC针对Linux程序的语法,Windows平台下不管是MSVC还是MinGW编译的DLL,宿主进程终止时都不会触发这个属性标记的函数——Windows的DLL生命周期完全依赖DllMain中的事件通知,和Linux共享库的析构逻辑完全不同。
可行的实现方案
方案1:利用DllMain的DLL_PROCESS_DETACH事件
这是最直接的方案,当宿主进程正常终止时,系统会自动调用所有已加载DLL的DllMain,并传入DLL_PROCESS_DETACH参数,此时可以执行清理逻辑:
#include <windows.h> // 提前在DLL加载时保存默认值(比如原前台锁定超时、原分辨率) void ZetaLoaderExit() { // 恢复SPI_SETFOREGROUNDLOCKTIMEOUT默认值(0x3E8即1000ms) SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (PVOID)0x3E8, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); // 重置显示分辨率为默认值 DEVMODE dm = {0}; dm.dmSize = sizeof(DEVMODE); EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &dm); ChangeDisplaySettings(&dm, 0); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 在这里保存默认的前台锁定超时和显示分辨率,供清理时调用 break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: // 执行清理操作 ZetaLoaderExit(); break; } return TRUE; }
注意:如果宿主进程被强制终止(比如任务管理器直接结束进程),DLL_PROCESS_DETACH可能不会触发,这种极端情况属于Windows系统限制,无法处理。
方案2:挂钩ExitProcess和TerminateProcess
如果要覆盖外部调用TerminateProcess终止宿主的场景,需要挂钩这两个系统函数,确保进程终止前先执行清理:
#include <windows.h> #include <detours.h> // 保存原函数指针 typedef VOID(WINAPI* PFN_EXITPROCESS)(UINT uExitCode); PFN_EXITPROCESS OriginalExitProcess = NULL; typedef BOOL(WINAPI* PFN_TERMINATEPROCESS)(HANDLE hProcess, UINT uExitCode); PFN_TERMINATEPROCESS OriginalTerminateProcess = NULL; void ZetaLoaderExit() { // 同方案1的清理逻辑 SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (PVOID)0x3E8, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); DEVMODE dm = {0}; dm.dmSize = sizeof(DEVMODE); EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &dm); ChangeDisplaySettings(&dm, 0); } // ExitProcess钩子函数 VOID WINAPI HookedExitProcess(UINT uExitCode) { ZetaLoaderExit(); OriginalExitProcess(uExitCode); } // TerminateProcess钩子函数 BOOL WINAPI HookedTerminateProcess(HANDLE hProcess, UINT uExitCode) { // 仅当终止当前宿主进程时执行清理 if (hProcess == GetCurrentProcess()) { ZetaLoaderExit(); } return OriginalTerminateProcess(hProcess, uExitCode); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 安装钩子 DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); // 挂钩ExitProcess OriginalExitProcess = ExitProcess; DetourAttach(&(PVOID&)OriginalExitProcess, HookedExitProcess); // 挂钩TerminateProcess OriginalTerminateProcess = TerminateProcess; DetourAttach(&(PVOID&)OriginalTerminateProcess, HookedTerminateProcess); DetourTransactionCommit(); break; case DLL_PROCESS_DETACH: // 卸载钩子 DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)OriginalExitProcess, HookedExitProcess); DetourDetach(&(PVOID&)OriginalTerminateProcess, HookedTerminateProcess); DetourTransactionCommit(); break; } return TRUE; }
说明:这里用Detours库简化挂钩逻辑,你也可以手动实现Inline Hook,但Detours更稳定且不易出错。
总结
- 优先用方案1,实现简单,覆盖绝大多数常规终止场景;
- 如果需要处理外部调用
TerminateProcess的情况,再叠加方案2; - 极端强制终止场景(如进程被直接杀死)无法处理,这是Windows系统的固有限制。
内容的提问来源于stack exchange,提问作者Aetopia
相关产品推荐
相关产品推荐

