【问题标题】:How to handle "End Task" from Windows Task Manager on a background process?如何在后台进程上从 Windows 任务管理器处理“结束任务”?
【发布时间】:2013-08-26 23:39:51
【问题描述】:

我编写了一个简单的测试程序 (TestProgram.exe) 来学习如何处理 CTRL_CLOSE_EVENT,以下是我的观察和我的问题:

1) 当我双击 TestProgram.exe 启动它时,如果我现在转到任务管理器,TestProgram.exe 列在“应用程序”下。当我在 TestProgram.exe 上执行“结束任务”时,我的 CTRL_CLOSE_EVENT 处理程序被调用。

但是

2) 当我打开命令提示符并启动 TestProgram.exe 时,它​​会列在任务管理器下的“后台进程”下,并且在相同的情况下执行“结束任务”不会导致 CTRL_CLOSE_EVENT。

我的真实应用程序按照上面案例 2) 中的描述使用。当用户在我的应用程序(在任务管理器的后台进程下列出)执行结束任务时,我想进行一些清理。

谢谢, 克里希纳

【问题讨论】:

  • 哪个版本的 Windows?
  • 你能在应用重启时进行清理吗?一般来说,如果用户从任务管理器中杀死你的应用程序,它就死了。对于安全性而言,没有其他方法可以接受 - 拥有足够权限的用户必须能够在不通知的情况下停止应用。
  • 我明白了。但我在某处读到应用程序有一些时间,大约 10 秒来进行任何必要的清理。另外,正如我最初提到的,为什么我能够在上面的情况 1 中处理它而不是在上面的情况 2 中处理它?

标签: c++ c windows


【解决方案1】:

一般来说,当一个进程被列为“应用程序”时,意味着任务管理器检测到该进程有一个 GUI,GUI 上的“结束任务”将首先尝试通过标准 @987654321 优雅地关闭 GUI @ 和/或WM_QUIT 消息,然后通过TerminateProcess() 暴力终止GUI 进程。另一方面,对“后台进程”执行“结束任务”将立即执行暴力终止。

因此,在您的情况下,双击 .exe 文件会生成一个新的专用控制台进程,该进程只运行您的应用程序,因此控制台的 GUI 被标记为“应用程序”,但是当您打开控制台时首先窗口并通过命令行执行您的 .exe,您的应用程序在现有控制台中运行并共享控制台的原始 GUI,因此您的应用程序没有自己的 GUI,因此被标记为“后台进程”。

【讨论】:

    【解决方案2】:

    当一个进程被终止(未关闭)时,除非你开始做一些挂钩,或者通过在任务管理器进程中挂钩TerminateProcessNtTerminateProcess,否则实际上什么都做不了,它是如何工作的示例:

    #include <windows.h>
    #include <assert.h>
    
    BOOL WINAPI MyTerminateProcess(HANDLE hProcess, UINT uExitCode ) {
        MessageBox(NULL, TEXT("Do some cleanup"), NULL, MB_OK);
        ExitProcess(0);
        return TRUE;
    }
    
    #pragma pack(1)
    typedef struct __PATCHDATA {
        BYTE push;
        DWORD address;
        BYTE ret;
    } PATCHDATA;
    #pragma pack()
    
    int main(int argc, char **argv) {
        HMODULE hModule;
        DWORD written;
        // This struct contains assembly instruction that do:
        //  push address ; 0x68 MyTerminateProcess
        //  ret          ; 0xc3
        // so the execution will return to our hook
        PATCHDATA patch = {0x68, (DWORD) MyTerminateProcess, 0xc3};
    
        // remove this code, the program will terminate itself.
        // TODO: check the memory protection and modify it.
        WriteProcessMemory(GetCurrentProcess(),
                           TerminateProcess,
                           &patch,
                           sizeof(PATCHDATA),
                           &written);
    
        TerminateProcess(NULL, 0);
    
        return 0;
    }
    

    这个钩子TerminateProcess在同一个进程中,你需要把它放在一个DLL中,inject它在Task Maneger进程中,没有测试它。但这种方法工作量大,不安全,一些AV产品可能会将其检测为有害程序。

    一个简单的解决方案是按照@Martin James 的建议清理程序启动。在您的程序启动时创建一个文件或使用注册表来存储一些值,例如0,如果程序已关闭,则收到WM_CLOSE(如果它是 GUI)或CTRL_CLOSE_EVENT(如果您关闭命令提示符),则执行清理-up 并存储1

    在下一次启动时,您检查该值是否仍为0,这意味着程序未正确关闭,请进行清理,如果是1,则无需清理,存储 0 并继续前进。

    很多程序使用这种方法来检测程序是否正确关闭。

    【讨论】:

    • 非常感谢!非常有用的回应。一个关于挂钩 TerminateProcess 的代码的问题。 0x68 和 0xc3 这两个地址是怎么选的?
    • @KrisRetroVirus 这些不是地址,它们是汇编指令的操作码,特别是push imm = 0x68retn = 0xc3
    • 啊!请原谅我的无知。
    • 有趣。不幸的是,通过任务管理器结束任务时不会调用处理程序。至少在我的小控制台测试应用程序中没有。在这种情况下,显然使用了 TerminateProcess 以外的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    • 1970-01-01
    • 2010-11-25
    • 1970-01-01
    • 1970-01-01
    • 2014-04-10
    相关资源
    最近更新 更多