You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在注入的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:51:29