我可以向 Windows 上的应用程序发送 ctrl-C (SIGINT) 吗?

问题描述:

我(过去)编写了跨平台(Windows/Unix)应用程序,当从命令行启动时,处理用户输入的 Ctrl-C 以相同的方式组合(即干净地终止应用程序).

I have (in the past) written cross-platform (Windows/Unix) applications which, when started from the command line, handled a user-typed Ctrl-C combination in the same way (i.e. to terminate the application cleanly).

是否可以在 Windows 上发送 Ctrl-C/SIGINT/等价于另一个(无关)进程的进程,以请求它干净地终止(给它有机会整理资源等)?

Is it possible on Windows to send a Ctrl-C/SIGINT/equivalent to a process from another (unrelated) process to request that it terminate cleanly (giving it an opportunity to tidy up resources etc.)?

我最接近解决方案的是 SendSignal 3rd 方应用.作者列出了源代码和一个可执行文件.我已经验证它在 64 位 Windows 下工作(作为 32 位程序运行,杀死另一个 32 位程序),但我还没有想出如何将代码嵌入到 Windows 程序(32 位或 64 位).

The closest that I've come to a solution is the SendSignal 3rd party app. The author lists source code and an executable. I've verified that it works under 64-bit windows (running as a 32-bit program, killing another 32-bit program), but I've not figured out how to embed the code into a windows program (either 32-bit or 64-bit).

工作原理:

在调试器中进行大量挖掘后,我发现实际执行与 ctrl-break 等信号相关的行为的入口点是 kernel32!CtrlRoutine.该函数与 ThreadProc 具有相同的原型,因此可以直接与 CreateRemoteThread 一起使用,无需注入代码.但是,这不是导出的符号!它在不同版本的 Windows 上位于不同的地址(甚至有不同的名称).怎么办?

After much digging around in the debugger I discovered that the entry point that actually does the behavior associated with a signal like ctrl-break is kernel32!CtrlRoutine. The function had the same prototype as ThreadProc, so it can be used with CreateRemoteThread directly, without having to inject code. However, that's not an exported symbol! It's at different addresses (and even has different names) on different versions of Windows. What to do?

这是我最终想出的解决方案.我为我的应用程序安装了一个控制台 ctrl 处理程序,然后为我的应用程序生成一个 ctrl-break 信号.当我的处理程序被调用时,我回头查看堆栈顶部以找出传递给 kernel32!BaseThreadStart 的参数.我抓取第一个参数,它是线程所需的起始地址,也就是 kernel32!CtrlRoutine 的地址.然后我从我的处理程序返回,表明我已经处理了信号并且我的应用程序不应该被终止.回到主线程,我一直等到 kernel32!CtrlRoutine 的地址被检索到.一旦我得到它,我就会在目标进程中创建一个远程线程,并使用发现的起始地址.这会导致目标进程中的 ctrl 处理程序被评估,就像按下了 ctrl-break 一样!

Here is the solution I finally came up with. I install a console ctrl handler for my app, then generate a ctrl-break signal for my app. When my handler gets called, I look back at the top of the stack to find out the parameters passed to kernel32!BaseThreadStart. I grab the first param, which is the desired start address of the thread, which is the address of kernel32!CtrlRoutine. Then I return from my handler, indicating that I have handled the signal and my app should not be terminated. Back in the main thread, I wait until the address of kernel32!CtrlRoutine has been retrieved. Once I've got it, I create a remote thread in the target process with the discovered start address. This causes the ctrl handlers in the target process to be evaluated as if ctrl-break had been pressed!

好消息是只有目标进程受到影响,任何进程(甚至是窗口进程)都可以成为目标.一个缺点是我的小应用程序不能在批处理文件中使用,因为它会在发送 ctrl-break 事件以发现 kernel32!CtrlRoutine 的地址时杀死它.

The nice thing is that only the target process is affected, and any process (even a windowed process) can be targeted. One downside is that my little app can't be used in a batch file, since it will kill it when it sends the ctrl-break event in order to discover the address of kernel32!CtrlRoutine.

(如果在批处理文件中运行它,则在它前面加上 start.)

(Precede it with start if running it in a batch file.)