Loading... 调用 TerminateProcess 终结进程时,在 nt!NtTerminateProcess 中会依次对目标进程的所有线程插入内核 APC,在每一个 内核 APC 得到执行时杀死自身线程。 注:下述代码参考自 WRK。 NtTerminateProcess: ![图片.png](http://47.117.131.13/usr/uploads/2021/12/3009596185.png) PspTerminateThreadByPointer: ![图片.png](http://47.117.131.13/usr/uploads/2021/12/988543525.png) 在 PsExitThread 中先调每一个线程内核回调: ![图片.png](http://47.117.131.13/usr/uploads/2021/12/3320368827.png) 内核 APC 的执行时机有两种,看 https://blog.csdn.net/qq_38474570/article/details/104326170: ### 1、KiSwapThread 线程切换时 注意并不是所有线程切换都会投递 apc,只有调 KiSwapThread 才会。KiSwapThrea 调 KiSwapContext->SwapContext 完成线程切换,并通过 KiSwapContext 的返回值决定要不要投递内核 apc: KiSwapThread: ![图片.png](http://47.117.131.13/usr/uploads/2021/12/4015725434.png) KiAttachProcess、KeDelayExecutionThread、KeWaitForMultipleObjects、KeWaitForSingleObjects 等会调用 KiSwapThread。这是通过主动调用 api 的方式来切换线程。 线程也会被动切换,即时间片耗尽也会发生线程切换,但不会投递 apc。如下只通过 KiDispatchInterrupt->SwapContext 来切换线程,不判断 SwapContext 返回值。参考:https://blog.csdn.net/lifeshave/article/details/87431654 KiDispatchInterruput: ![图片.png](http://47.117.131.13/usr/uploads/2021/12/3442524941.png) ### 2、内核调用返回,中断或异常时,_KiServiceExit 如下即在内核调用返回,检查 apc 列表并投递内核 APC 杀线程时的栈: ``` nt!PspExitProcess+0x156 // 杀最后一个线程时会调用 PspExitProcess nt!PspExitThread+0x4e9 nt!PsExitSpecialApc+0x1d nt!KiDeliverApc+0x2ca nt!KiInitiateUserApc+0x70 nt!KiSystemServiceExit+0x9c ``` ### 场景一:目标线程正在用户态死循环跑着,怎么终结? 在 KiInsertQueueApc 最后会判断目标线程状态,如果 ThreadState == running,则会通过 KiRequestApcInterrupt 向目标处理器发出一个软中断 apc_interrupt,中断例程是 nt!KiApcInterrupt: ![图片.png](http://47.117.131.13/usr/uploads/2021/12/1974937546.png) 一个可能的栈如下: ``` nt!PspExitProcess nt!PspExitThread+0x4e9 nt!PsExitSpecialApc+0x1d nt!KiDeliverApc+0x2ca nt!KiInitiateUserApc+0x70 nt!KiApcInterrupt+0x10b tmp!main+0x17 ``` ### 场景二:目标线程正在内核跑着,怎么终结? 只要该线程内核部分代码不主动调 api 切换线程(睡眠、等待等函数),也没有发生异常,则只会在线程返回到用户态前夕时,终结线程的 apc 才有机会执行。 但上述场景一的 apc_interrupt 为啥不会起作用,还不明白,后面补充吧。 最后修改:2021 年 12 月 21 日 03 : 35 PM © 允许规范转载