进程终止
描述了一些终止失控进程或恶意软件的方法。
引言
本文描述了如何停止一个失控的进程。这样做有几个常见的原因:设备驱动程序中有无法正常关闭的错误,禁用恶意软件足够长的时间以更新并运行防病毒工具,第三方应用程序编写得太差以至于出现故障且无法从任务管理器关闭。
在 Windows 下,进程有一个消息泵,可以接收命令。当它表现良好时,如果收到某些命令,应用程序将关闭。在极端情况下,它可能不响应,或者在恶意软件的情况下,被设计为忽略这些命令。当进程不再有线程时,它将结束,这是一种相对极端的关闭进程的方式。
本文探讨了作者已知的一些方法,可能还有更多,但这应该作为该主题的一个很好的介绍。
风险回报
进程运行有其原因。突然停止它们可能导致磁盘扇区损坏或电脑行为异常或死机。当电脑由于恶意软件或编写不当的软件而行为异常时,选择合适方法的范围从“礼貌地要求应用程序停止”到“在极端情况下所做的操作”之间有一个滑动刻度。
关闭方法
CloseMainWindow
System.Diagnostics.Process
对象有一个 CloseMainWindow()
方法,它向应用程序的消息泵发送一个窗口管理器关闭消息。这是一种“礼貌地”要求应用程序关闭的方式。应用程序可能已经或尚未编写为自动关闭,或者在尝试关闭时卡住,或者忽略消息。应用程序可能尚未拥有 MainWindowHandle
,在应用程序创建过程中有一段时间它正在运行但尚未请求窗口句柄。某些没有 UI 的进程可能没有窗口句柄。
终止
使用 System.Diagnostics.Process Kill()
函数类似于使用 TaskManager
结束进程。它是结束没有用户界面的进程的唯一方法。如果进程正在访问数据,则该进程可能没有机会在关闭前进行清理。终止进程可能会导致数据丢失。例如,在重写关键系统 DLL 的过程中终止恶意软件可能会导致 DLL 损坏,或者它可以阻止恶意软件删除所有文件。如果进程已将自身转换为系统关键进程(至少有两种方法可以通过 ntdll.dll
中的 RtlSetProcessIsCritical
或 NtSetInformationProcess
来实现),则 Kill 将不起作用。
Terminate Process
这可能比 Kill 更极端,MSDN 描述它可能会使具有全局数据的共享 DLL 处于中间状态,因为进程突然结束。它被描述为暂停线程并请求取消所有 I/O。理论上,除非进程进行了不取消的阻塞内核调用,否则进程将停止。在少数情况下(API 阻塞在不会返回的内核调用上),此调用将不起作用。
SuspendThread
此调用实际上不会停止进程,但它会相当安全地将其变成一个无法执行的进程,然后可以将其终止。System.Diagnostics.Process
API 可用于获取线程 ID,该 ID 可用于获取线程句柄(具有适当的权限),然后用于挂起线程。此技术的优点是进程仍然存活但已变得无害。在恶意软件的情况下,多个进程相互监视并根据需要重新启动,这可能足以禁用恶意软件,然后尝试终止并删除或更新防病毒保护。
优先级和亲和性
每个处理器都有一个与之关联的优先级,以及对处理器核心的亲和性。就当前主题而言,这意味着进程可能被设置为在所有处理核心上一直以非常快的速度运行,如果要求它转到单个核心并以非常低的优先级运行,则更容易禁用/终止它。进程优先级类是一个数字,提升值 + 线程值 + 操作系统设置决定了调度进程时使用的下一个优先级值。亲和性数字是一个位掩码,表示可以使用哪些处理器(1 将其设置为第一个核心)。这对于消耗大量 CPU 资源导致不响应用户输入的有缺陷的应用程序,以及将恶意软件推到可以移除的状态非常有用。演示代码提供了一个选项,可以在尝试终止进程之前尝试设置这些参数。恶意应用程序仍然可以通过始终在关键的非睡眠线程循环中设置其优先级和亲和性来规避这一点。
- http://msdn.microsoft.com/en-us/library/system.diagnostics.process.priorityclass.aspx
- http://msdn.microsoft.com/en-us/library/system.diagnostics.process.processoraffinity.aspx
系统关键(未文档化)
操作系统会跟踪哪些进程是“关键”的,哪些不是,终止“关键”进程可能要么不可能,要么导致电脑“蓝屏”。要使进程变为非关键,有两种技术:NtSetInformationProcess
和 RtlSetProcessIsCritical
。两者都是 ntDLL.dll 调用,未文档化/不受支持。有一些信息,但不多。演示代码尝试使用 NtSetInformationProcess
来移除应用程序的关键标志。
TerminateThread
这是一个以不安全的方式终止线程的调用。通过终止进程的所有线程,一旦所有待处理的 IO 完成,进程将退出。由于线程以非常低且不干净的级别死亡,因此使用此调用是最后的手段。根据作者的经验,使用此调用是一个糟糕的,非常非常糟糕的主意。它会产生一些不可预测的影响。进程可能进入如此不稳定的状态,以至于操作系统无法调度进程运行,也无法关闭它。它可能导致待处理的 I/O 行为异常,并且它还可能使电脑“蓝屏”。作者被迫调试的一个 DLL 中使用了它,该 DLL 用于终止失控的 USB 驱动器读取,并偶尔导致电脑崩溃/锁定,变得无法停止,并蓝屏。重启后,电脑恢复正常。
面对磁盘加密、Rootkit、注册表修改、磁盘分支中的病毒、在控制后更改管理员权限等情况,有时选择是使用此调用,或者几个月不使用电脑,直到反病毒/反恶意软件供应商提供解决方案。
这是在演示中提供该调用的唯一原因,也是作者认为该调用合理的唯一情况。
演示应用程序的软件架构
该软件采用散弹枪式方法针对一个或多个进程。通常,第一次不奏效的方法,在尝试了不同的技术并且操作系统试图重新调度进程之后可能会奏效。一些不良进程会相互监控,并且可能只有在其他进程被快速终止后才能停止。该软件在一个循环中运行,每次迭代都会对每个进程进行多次尝试。完成了一些关键的事情。
Try…Catch
每个主要调用都被包装起来,大多数影响进程或线程的调用都可能抛出异常,而应用程序的目标是继续运行,并尝试不同的技术。
Permute
每种技术都进行了尝试,但每次迭代的起始顺序都不同,这使得每种技术都能在新的进程列表上获得新的开始。
按进程名称或模块全名
有些进程具有唯一的名称,而另一些进程可能恰好与所需进程具有相同的名称。有些进程的路径可以输入,而另一些则可能无法输入(资源路径、非常长的路径、带有奇怪字符的路径)。应用程序可以运行以处理这两种情况。
简单
这是一个简单的演示应用程序,用户界面简洁但功能齐全。它足以满足需求,但绝不具有艺术性,也无法提供所有理论情况下所需的精细功能。如果需要更多功能,很可能还需要涉及注册表、代码注入等其他技术或专用代码来处理特定的恶意软件威胁或高度不寻常的应用程序。