TerminateProcess流程追踪、PspTerminateThreadByPointer的实现

TerminateProcess通过本机系统服务接口进入核心态,随后调用ntoskrnl的NtTerminateProcess。这部分细节我就不详述了,大家可以去看我写的《重要》从用户模式切换到内核模式的完整过程分析

大概的流程是这样的:

①判断句柄是否为空

②通过句柄得到进程对象

③判断是否为临界进程

④锁住进程

⑤遍历进程的线程链表

⑥调用PspTerminateThreadByPointer结束线程

⑦解锁进程

⑧如果处于调试状态,则取出Debugger的DebugPort字段对调试对象的引用。

⑨如果没有线程存在进程,或处于调试态,则清空进程的句柄表

下面来看看具体实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
NTSTATUS
NtTerminateProcess(
    __in_opt HANDLE ProcessHandle,
    __in NTSTATUS ExitStatus
    )

/*++

Routine Description:

    This function causes the specified process and all of
    its threads to terminate.

Arguments:

    ProcessHandle - Supplies a handle to the process to terminate.

    ExitStatus - Supplies the exit status associated with the process.

Return Value:

    NTSTATUS - Status of operation

--*/


{

    PETHREAD Thread, Self;
    PEPROCESS Process;
    PEPROCESS CurrentProcess;
    NTSTATUS st;
    BOOLEAN ProcessHandleSpecified;
    PAGED_CODE();
    //得到当前线程
    Self = PsGetCurrentThread();
    //根据当前线程得到当前进程
    CurrentProcess = PsGetCurrentProcessByThread (Self);

    //利用ARGUMENT_PRESENT宏对句柄是否为空进行了判断
    if (ARGUMENT_PRESENT (ProcessHandle)) {
        ProcessHandleSpecified = TRUE;
    } else {
        ProcessHandleSpecified = FALSE;
        //为空得到当前的进程句柄
        ProcessHandle = NtCurrentProcess();
    }

    //通过句柄得到进程对象
    st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_TERMINATE,
                                    PsProcessType,
                                    KeGetPreviousModeByThread(&Self->Tcb),
                                    &Process,
                                    NULL);

    if (!NT_SUCCESS (st)) {
        return(st);
    }
    //判断是否为临界进程
    if (Process->Flags & PS_PROCESS_FLAGS_BREAK_ON_TERMINATION) {
        //导致蓝屏
        PspCatchCriticalBreak ("Terminating critical process 0x%p (%s)\n",
                               Process,
                               Process->ImageFileName);
    }

    //
    // Acquire rundown protection just so we can give the right errors
    //
    //锁住进程
    if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
        //锁住进程失败,减少引用次数
        ObDereferenceObject (Process);
        return STATUS_PROCESS_IS_TERMINATING;
    }

    //
    // Mark process as deleting except for the obscure delete self case.
    //

    if (ProcessHandleSpecified) {
        PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_PROCESS_DELETE);
    }

    st = STATUS_NOTHING_TO_TERMINATE;

    //遍历进程的线程链表
    for (Thread = PsGetNextProcessThread (Process, NULL);
         Thread != NULL;
         Thread = PsGetNextProcessThread (Process, Thread)) {
        //至少有一个线程
        st = STATUS_SUCCESS;
        //确保线程不是本线程
        if (Thread != Self) {
            //结束线程
            PspTerminateThreadByPointer (Thread, ExitStatus, FALSE);
        }
    }

    //解锁进程
    ExReleaseRundownProtection (&Process->RundownProtect);

    //如果进程是当前进程
    if (Process == CurrentProcess) {
        //并且句柄不为空
        if (ProcessHandleSpecified) {

            ObDereferenceObject (Process);

            //
            // Never Returns
            //
            //结束自己
            PspTerminateThreadByPointer (Self, ExitStatus, TRUE);
        }
    } else if (ExitStatus == DBG_TERMINATE_PROCESS) {
        //进程处于调试状态
        //去除Debugger的DebugPort字段对调试对象的引用,并将其设置为NULL。
        DbgkClearProcessDebugObject (Process, NULL);
    }

    //
    // If there are no threads in this process then clear out its handle table.
    // Do the same for processes being debugged. This is so a process can never lock itself into the system
    // by debugging itself or have a handle open to itself.
    //
    if (st == STATUS_NOTHING_TO_TERMINATE || (Process->DebugPort != NULL && ProcessHandleSpecified)) {
        ObClearProcessHandleTable (Process);
        st = STATUS_SUCCESS;
    }
    //减少引用次数
    ObDereferenceObject(Process);

    return st;
}

然后我们再来看看PspTerminateThreadByPointer是怎么结束线程的。

大概流程如下:

①判断是否为临界线程

②结束线程本身,直接PspExitThread

③如果是系统线程,返回STATUS_ACCESS_DENIED

④申请APC,插入APC队列,结束线程

下面来看看具体实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
NTSTATUS
PspTerminateThreadByPointer(
    IN PETHREAD Thread,
    IN NTSTATUS ExitStatus,
    IN BOOLEAN DirectTerminate
    )

/*++

Routine Description:

    This function causes the specified thread to terminate.

Arguments:

    ThreadHandle - Supplies a referenced pointer to the thread to terminate.

    ExitStatus - Supplies the exit status associated with the thread.

    DirectTerminate - TRUE is its ok to exit without queing an APC, FALSE otherwise

--*/


{
    NTSTATUS Status;
    PKAPC    ExitApc=NULL;
    ULONG    OldMask;

    PAGED_CODE();

    //判断是否为临界线程
    if (Thread->CrossThreadFlags
    & PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION) {
      //导致蓝屏
      PspCatchCriticalBreak("Terminating critical thread 0x%p (in %s)\n",
                Thread,
                THREAD_TO_PROCESS(Thread)->ImageFileName);
    }

    //结束线程本身
    if (DirectTerminate && Thread == PsGetCurrentThread()) {
        //判断当前中断级是否小于APC
        ASSERT (KeGetCurrentIrql() < APC_LEVEL);

        //设置结束标志
        PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_TERMINATED);
 
        //直接结束线程本身(自杀)
        PspExitThread (ExitStatus);

        //
        // Never Returns
        //

    } else {
        //
        // Cross thread deletion of system threads won't work.
        //线程是系统线程则不会继续执行
        if (IS_SYSTEM_THREAD (Thread)) {
            return STATUS_ACCESS_DENIED;
        }

        Status = STATUS_SUCCESS;

        while (1) {
            //申请APC
            ExitApc = (PKAPC) ExAllocatePoolWithTag (NonPagedPool,
                                                     sizeof(KAPC),
                                                     'xEsP');
            if (ExitApc != NULL) {
                break;
            }

            //延迟继续申请
            KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
        }

        //
        // Mark the thread as terminating and call the exit function.
        //设置结束标志
        OldMask = PS_TEST_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_TERMINATED);

        //
        // If we are the first to set the terminating flag then queue the APC
        //

        if ((OldMask & PS_CROSS_THREAD_FLAGS_TERMINATED) == 0) {
            //初始化内核APC去结束线程
            KeInitializeApc (ExitApc,
                             PsGetKernelThread (Thread),
                             OriginalApcEnvironment,
                             PsExitSpecialApc,
                             PspExitApcRundown,
                             PspExitNormalApc,
                             KernelMode,
                             ULongToPtr (ExitStatus));
            //插入APC队列
            if (!KeInsertQueueApc (ExitApc, ExitApc, NULL, 2)) {
                //已经在队列中
                // If APC queuing is disabled then the thread is exiting anyway
                //
                ExFreePool (ExitApc);
                Status = STATUS_UNSUCCESSFUL;
            } else {
                //
                // We queued the APC to the thread. Wake up the thread if it was suspended.
                //
                KeForceResumeThread (&Thread->Tcb);

            }
        } else {
            ExFreePool (ExitApc);
        }
    }

    return Status;
}

本文链接:http://www.alonemonkey.com/terminateprocess-flow-path.html