在TerminateProcess流程追踪、PspTerminateThreadByPointer的实现中讲到,进程的终止,最终都是通过PspTerminateThreadByPointer终止线程来实现的。而PspTerminateThreadByPointer不管是通过终止本身,还是通过插APC最终都是调用PspExitThread来完成终止过程。
那来分析下PspExitThread的执行流程。
//通过线程来获得进程对象。
if (Process != PsGetCurrentProcessByThread (Thread)) {
KeBugCheckEx (INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->Tcb.ApcState.Process,
(ULONG)Thread->Tcb.ApcStateIndex,
(ULONG_PTR)Thread);
}
KeLowerIrql(PASSIVE_LEVEL);
//判断线程是否允许终止
if (Thread->ActiveExWorker) {
KeBugCheckEx (ACTIVE_EX_WORKER_THREAD_TERMINATION,
(ULONG_PTR)Thread,
0,
0,
0);
}
if (Thread->Tcb.CombinedApcDisable != 0) {
KeBugCheckEx (KERNEL_APC_PENDING_DURING_EXIT,
(ULONG_PTR)0,
(ULONG_PTR)Thread->Tcb.CombinedApcDisable,
(ULONG_PTR)0,
1);
}
ExWaitForRundownProtectionRelease (&Thread->RundownProtect);
PoRundownThread(Thread);
//通知哪些已注册的线程删除事件接受者
PERFINFO_THREAD_DELETE(Thread);
if (PspCreateThreadNotifyRoutineCount != 0) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_THREAD_NOTIFY_ROUTINE Rtn;
for (i=0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]);
if (CallBack != NULL) {
Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
Rtn (Process->UniqueProcessId,
Thread->Cid.UniqueThread,
FALSE);
ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i],
CallBack);
}
}
}
LastThread = FALSE;
DerefThread = NULL;
PspLockProcessExclusive (Process, Thread);
//进程的活动线程计数减1
Process->ActiveThreads—;
//如果这是最后一个线程,则必须等到该进程的线程链表中所有的线程都退出才能继续往下进行。
if (Process->ActiveThreads == 0) {
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_PROCESS_DELETE);
LastThread = TRUE;
if (ExitStatus == STATUS_THREAD_IS_TERMINATING) {
if (Process->ExitStatus == STATUS_PENDING) {
Process->ExitStatus = Process->LastThreadExitStatus;
}
} else {
Process->ExitStatus = ExitStatus;
}
for (Entry = Process->ThreadListHead.Flink;
Entry != &Process->ThreadListHead;
Entry = Entry->Flink) {
WaitThread = CONTAINING_RECORD (Entry, ETHREAD, ThreadListEntry);
if (WaitThread != Thread &&
!KeReadStateThread (&WaitThread->Tcb) &&
ObReferenceObjectSafe (WaitThread)) {
PspUnlockProcessExclusive (Process, Thread);
KeWaitForSingleObject (WaitThread,
Executive,
KernelMode,
FALSE,
NULL);
if (DerefThread != NULL) {
ObDereferenceObject (DerefThread);
}
DerefThread = WaitThread;
PspLockProcessExclusive (Process, Thread);
}
}
} else {
if (ExitStatus != STATUS_THREAD_IS_TERMINATING) {
Process->LastThreadExitStatus = ExitStatus;
}
}
PspUnlockProcessExclusive (Process, Thread);
if (DerefThread != NULL) {
ObDereferenceObject (DerefThread);
}
如有必要发送调试信息
if (Process->DebugPort != NULL) {
if (!IS_SYSTEM_THREAD (Thread)) {
if (LastThread) {
DbgkExitProcess (Process->ExitStatus);
} else {
DbgkExitThread (ExitStatus);
}
}
}
if (KD_DEBUGGER_ENABLED) {
if (Thread->CrossThreadFlags & PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION) {
PspCatchCriticalBreak (“Critical thread 0x%p (in %s) exited\n”,
Thread,
Process->ImageFileName);
}
} // End of critical thread/process exit detect
if (LastThread &&
(Process->Flags & PS_PROCESS_FLAGS_BREAK_ON_TERMINATION)) {
if (KD_DEBUGGER_ENABLED) {
PspCatchCriticalBreak (“Critical process 0x%p (%s) exited\n”,
Process,
Process->ImageFileName);
} else {
KeBugCheckEx (CRITICAL_PROCESS_DIED,
(ULONG_PTR)Process,
0,
0,
0);
}
}
ASSERT(Thread->Tcb.CombinedApcDisable == 0);
处理TerminationPort,向所有已经登记过要接受终止通知的线程发送终止消息
TerminationPort = Thread->TerminationPort;
if (TerminationPort != NULL) {
CdMsg.PortMsg.u1.s1.DataLength = sizeof(LARGE_INTEGER);
CdMsg.PortMsg.u1.s1.TotalLength = sizeof(LPC_CLIENT_DIED_MSG);
CdMsg.PortMsg.u2.s2.Type = LPC_CLIENT_DIED;
CdMsg.PortMsg.u2.s2.DataInfoOffset = 0;
do {
CdMsg.CreateTime.QuadPart = PS_GET_THREAD_CREATE_TIME (Thread);
while (1) {
Status = LpcRequestPort (TerminationPort->Port, (PPORT_MESSAGE)&CdMsg);
if (Status == STATUS_NO_MEMORY || Status == STATUS_INSUFFICIENT_RESOURCES) {
KeDelayExecutionThread (KernelMode, FALSE, &ShortTime);
continue;
}
break;
}
ObDereferenceObject (TerminationPort->Port);
NextPort = TerminationPort->Next;
ExFreePoolWithTag (TerminationPort, ‘pTsP’|PROTECTED_POOL);
TerminationPort = NextPort;
} while (TerminationPort != NULL);
} else {
if ((ExitStatus == STATUS_THREAD_IS_TERMINATING &&
(Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD)) ||
!(Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD)) {
CdMsg.PortMsg.u1.s1.DataLength = sizeof (LARGE_INTEGER);
CdMsg.PortMsg.u1.s1.TotalLength = sizeof (LPC_CLIENT_DIED_MSG);
CdMsg.PortMsg.u2.s2.Type = LPC_CLIENT_DIED;
CdMsg.PortMsg.u2.s2.DataInfoOffset = 0;
if (Process->ExceptionPort != NULL) {
//向异常端口发送终止消息
CdMsg.CreateTime.QuadPart = PS_GET_THREAD_CREATE_TIME (Thread);
while (1) {
Status = LpcRequestPort (Process->ExceptionPort, (PPORT_MESSAGE)&CdMsg);
if (Status == STATUS_NO_MEMORY || Status == STATUS_INSUFFICIENT_RESOURCES) {
KeDelayExecutionThread (KernelMode, FALSE, &ShortTime);
continue;
}
break;
}
}
}
}
通知Windows子系统当前线程退出,如果这是进程的最后一个线程,还要通知Windows子系统当前进程退出。
if (Thread->Tcb.Win32Thread) {
(PspW32ThreadCallout) (Thread, PsW32ThreadCalloutExit);
}
if (LastThread && Process->Win32Process) {
(PspW32ProcessCallout) (Process, FALSE);
}
取消I/O,取消定时器,清除任何尚未完成的注册表通知请求,释放当前线程所拥有的突变体内核对象(mutant)
IoCancelThreadIo (Thread);
ExTimerRundown ();
CmNotifyRunDown (Thread);
KeRundownThread ();
释放TEB。
Teb = Thread->Tcb.Teb;
if (Teb != NULL) {
Peb = Process->Peb;
try {
if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) {
SIZE_T Zero;
PVOID BaseAddress;
if (Teb->FreeStackOnTermination) {
Zero = 0;
BaseAddress = Teb->DeallocationStack;
ZwFreeVirtualMemory (NtCurrentProcess (),
&BaseAddress,
&Zero,
MEM_RELEASE);
}
#if defined(_WIN64)
if (Process->Wow64Process != NULL) {
PTEB32 Teb32;
Teb32 = WOW64_GET_TEB32_SAFE (Teb);
if (Teb32->FreeStackOnTermination) {
Zero = 0;
BaseAddress = UlongToPtr (Teb32->DeallocationStack);
ZwFreeVirtualMemory (NtCurrentProcess (),
&BaseAddress,
&Zero,
MEM_RELEASE);
}
}
#endif
}
if (Teb->DbgSsReserved[1] != NULL) {
ObCloseHandle (Teb->DbgSsReserved[1], UserMode);
}
} except (EXCEPTION_EXECUTE_HANDLER) {
}
MmDeleteTeb (Process, Teb);
Thread->Tcb.Teb = NULL;
}
调用LpcExitThread以便让LPC组件来处理LPC应答消息。
LpcExitThread (Thread);
设置线程的退出时间和退出状态
Thread->ExitStatus = ExitStatus;
KeQuerySystemTime (&Thread->ExitTime);
如果是进程的最后一个线程,还要调用PspExitProcess以退出进程,并设置进程退出的相关属性,以及销毁句柄表和解除对进程的内存区对象的引用。
if (LastThread) {
Process->ExitTime = Thread->ExitTime;
PspExitProcess (TRUE, Process);
ProcessToken = PsReferencePrimaryToken (Process);
if (SeDetailedAuditingWithToken (ProcessToken)) {
SeAuditProcessExit (Process);
}
PsDereferencePrimaryTokenEx (Process, ProcessToken);
#if defined(_X86_)
if (Process->VdmObjects != NULL) {
VdmRundownDpcs (Process);
}
#endif
ObKillProcess (Process);
if (Process->SectionObject != NULL) {
ObDereferenceObject (Process->SectionObject);
Process->SectionObject = NULL;
}
if (Process->Job != NULL) {
PspExitProcessFromJob (Process->Job, Process);
}
}
强制恢复线程运行,以便处理可能有的APC,遍历用户模式APC,如果此APC有一个终止函数(RundownRountine),则执行此函数,否则直接扔掉此APC。
KeForceResumeThread (&Thread->Tcb);
KeLeaveCriticalRegionThread (&Thread->Tcb);
//
// flush user-mode APC queue
//
FirstEntry = KeFlushQueueApc (&Thread->Tcb, UserMode);
if (FirstEntry != NULL) {
Entry = FirstEntry;
do {
Apc = CONTAINING_RECORD (Entry, KAPC, ApcListEntry);
Entry = Entry->Flink;
//
// If the APC has a rundown routine then call it. Otherwise
// deallocate the APC
//
if (Apc->RundownRoutine) {
(Apc->RundownRoutine) (Apc);
} else {
ExFreePool (Apc);
}
} while (Entry != FirstEntry);
}
如果是该进程最后一个线程,则清除当前进程的地址空间。
if (LastThread) {
MmCleanProcessAddressSpace (Process);
}
将进程对象的状态设置为有信号状态,以唤醒那些可能在等待此进程对象的线程。
if (LastThread) {
KeSetProcess (&Process->Pcb, 0, FALSE);
}
最后调用KeTerminateThread,设置线程状态为有信号状态,并设置线程的调度状态为“已终止”。
处理将执行新选择的线程,以后调度算法再也不会选择这一已终止的线程。
本文链接:http://www.alonemonkey.com/pspexitthread-flow-path.html