PsTerminateProcess结束进程
PsTerminateProcess函数用于结束一个进程,其声明如下
NTSTATUS
NTAPI
PsTerminateProcess(IN PEPROCESS Process, IN NTSTATUS ExitStatus)
其中第一个参数是PEPROCESS的指针,如果你只知道pid,可以通过PsLookupProcessByProcessId 获得,而第二个参数指定退出状态码。
PsTerminateProcess函数的实现非常简单,就是调用了PspTerminateProcess
NTSTATUS
NTAPI
PsTerminateProcess(IN PEPROCESS Process,
IN NTSTATUS ExitStatus)
{
/* Call the internal API */
return PspTerminateProcess(Process, ExitStatus);
}
PsTerminateProcess是一个导出函数,而PspTerminateProcess是一个内部函数。也就是说,你只要包含的正确的头文件,就可以使用PsTerminateProcess杀掉一个进程,而想用PspTerminateProcess你就得多费点时间把它的函数地址找出来。所以为安全起见,大多数的杀毒软件都有hook住PsTerminateProcess,PspTerminateProcess就不一定了。
再看PspTerminateProcess的实现:
PETHREAD Thread;
NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE;
PAGED_CODE();
PSTRACE(PS_KILL_DEBUG,
"Process: %p ExitStatus: %p\n", Process, ExitStatus);
PSREFTRACE(Process);
/* Check if this is a Critical Process */
if (Process->BreakOnTermination)
{
/* Break to debugger */
PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
Process,
Process->ImageFileName);
}
先是检查是否有调试器挂在上面并且要求退出进程时中断到调试器,如果需要则调用PspCatchCriticalBreak中断过去。PspCatchCriticalBreak的实现我总觉得应该是ReactOS里调内核的临时做法,NT内核应该不是这么干的,所以暂时略过不看。(其实逻辑很简单,一眼就能扫明白),接着看PspTerminateProcess:
/* Set the delete flag */InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT);
然后调用如下方法在Flags域里置位,标明自己已经结束
/* Get the first thread */
Thread = PsGetNextProcessThread(Process, NULL);
while (Thread)
{
/* Kill it */
PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
Thread = PsGetNextProcessThread(Process, Thread);
/* We had at least one thread, so termination is OK */
Status = STATUS_SUCCESS;
}
接着就是最关键的一步:先找到进程下的第一个线程,然后顺着线程列表访问所有的线程,调用PspTerminateThreadByPointer结束它。等所有的线程都退出的时候,进程自然也就消亡了(最后一个线程退出时负责干掉进程)。
PsGetNextProcessThread是在EPROCESS结构ThreadListHead指向的线程列表里遍历,程序很简单。有趣的是PspTerminateThreadByPointer函数,这是结束线程的核心程序。程序主体在我手头的ReactOS里没有,泄露的nt4代码看过但又不便贴出,所以只能略说:大致的做法就是生成一个APC插入到目标线程里,而该APC所作的事情就是调用PspExitThread退出。关于APC的详细内容请看这里。相信有心的筒子完全有能力自己山寨一个PspTerminateThreadByPointer出来。
PspTerminateProcess所作的最后一件事情呢,就是检测是否真有线程被结束,如果根本就没有这样的线程,那还得自己负责把句柄表里表示自己的那一项清掉:
/* Check if there was nothing to terminate or if we have a debug port */
if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort))
{
/* Clear the handle table anyway */
ObClearProcessHandleTable(Process);
}
但凡有追求一点的杀软呢,这里面的绝大多数函数比如PsTerminateProcess,PspTerminateProcess和PspTerminateThreadByPointer等都会有hook住,防止有人干坏事。强势一点的连APC相关的操作也要hook住,这样想杀掉某进程就相当困难了。当然,不那么有追求的杀软会漏掉几个(包括但不限于以上所列函数),那些漏掉的当然就可以拿来大作文章了。
至此PsTerminateProcess函数就分析完毕,顺着这个函数可以学到无数知识,基本上进程管理和线程调度的内容都能顺杆提出来。并且这部分内容是各安全论坛讨论的热点,本身也十分有趣,是个不错的切入点。