EndTask 函数用于结束任务,它会强制终止目标窗口的进程,因此不建议用于常规操作。
这种终止方式属于暴力手段,不会允许进程资源的释放。某些软件可能会使用此方法来强制结束顽固的 GUI 进程。此功能在 taskmgr.exe(任务管理器)中的实现形式是进程列表中的“结束任务”功能。在最新的 Windows 11 中,用户可以通过开发者选项启用它,这会在任务栏右键菜单中添加“结束任务”选项。
通过我的测试发现,EndTask 函数没有进行任何安全边界检查,允许低权限用户终止受保护的进程和系统关键进程。终止这些关键进程会导致系统蓝屏。
例如,GetDesktopWindow() 函数可以获取桌面顶级窗口的句柄(#32769),这在我之前的动态壁纸制作研究文章中有提到。这个窗口属于 CSRSS 进程,而 CSRSS 是一个系统关键进程。终止该进程会导致系统立即蓝屏。
以下代码会导致 CSRSS 进程停止工作:
EndTask(GetDesktopWindow(), FALSE, TRUE);
通过分析 EndTask 函数的实现细节,我发现该函数最终通过与 CSRSS 进程通信,请求调用 CSR 中的服务接口来终止目标窗口(进程)。换句话说,以上代码会导致 CSRSS 自己终止自己,由于它是系统关键进程,这会导致系统蓝屏。
那么是否除了 GetDesktopWindow() 外,我们就没有其他跟特权进程 CSRSS 进程有关的窗口了?答案是否定的,我们可以轻松通过多种方式胁迫 CSRSS 生成交互式窗口。
比如 MessageBox 函数,我们知道它可以生成消息框,用于显示一些需要立即通知的错误消息。但你是否知道它有一些文档中从未提及的特性?
MessageBox 函数的定义为:
int MessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
);
当调用者(仅需普通权限的进程)尝试在 uType 中指定:MB_DEFAULT_DESKTOP_ONLY 或者 MB_SERVICE_NOTIFICATION 时,将生成交互式消息框。此类消息框由 CSRSS 进程的线程创建,提供给非交互式进程(主要为服务)快捷地向交互式用户显示重要信息。

注意:在包括 Windows NT 4.0 在内的早期版本系统中,只有在 SYSTEM 特权进程中设置 MB_DEFAULT_DESKTOP_ONLY、MB_SERVICE_NOTIFICATION、MB_TOPMOST 或 MB_SERVICE_NOTIFICATION_NT3X 中的任意一个标志位,才会出现此现象。而在 Windows Vista 及更高版本中,MB_TOPMOST 和 MB_SERVICE_NOTIFICATION_NT3X 标志位则不会导致消息框由 CSRSS 进程生成,无论是特权还是非特权。
调用 MessageBox 消息框的底层有两个分支:一个是 SoftModalMessageBox 另一个就是 ServiceMessageBox。ServiceMessageBox 是服务消息框, SoftModalMessageBox 是普通消息框。
ServiceMessageBox 内检测当前调用线程的会话是否是活动的交互式用户会话,如果是,则直接调用 NtRaiseHardError (ExpRaiseHardError)。而这个函数内部其实是通信 CSRSS。
-
Windows 2000 (NT 5.0) 至 Windows Server 2003 (NT 5.1) ExpRaiseHardError 调用 LPC 函数 LpcRequestWaitReplyPortEx 通知 CSRSS,并传入 HARDERROR_MSG 结构。目标LPC 端口是本进程的 EPROCESS.ExceptionPort。
-
Windows Vista (NT 6.0) 以后 Vista 的内核是一次史诗级更新,换成了 ALPC。为了保持兼容性,内核导出的 LPC API 依然存在,但它们仅简单地调用 ALPC 的相关函数。 ExpRaiseHardError 调用的是 LpcSendWaitReceivePort 函数,这个函数只是进入临界区之后调用 ALPC 组件的 AlpcpProcessSynchronousRequest 函数而已,目标端口保持不变。
如果会话不同,则通过 WinStationSendMessage 将消息先发送至目标会话的 CSRSS。然后由目标活动会话的 CSRSS 拉起窗口。
总结(不一定最正确):
(1)尽量不要在应用中使用 assert() 等断言,减少 CRT 的使用;
(2)MessageBox 函数不要使用 MB_SERVICE_NOTIFICATION / MB_DEFAULT_DESKTOP_ONLY;
(3)不要使用 WinStationSendMessage 和 WTSSendMessage 等函数;
(4)主进程由启动器进程启动,在启动器进程中关闭硬件错误消息弹窗;
(5)通过 Hook 挂钩 NtRaiseHardError,拦截程序运行过程中可能由内部代码错误引起的任何弹窗,修改成自己的实现。