线程挂起和恢复

date
Sep 25, 2023
slug
suspend-and-resume-thread
status
Published
tags
Windows Internals
Thread
summary
这篇内容讲述了在Windows操作系统中挂起和恢复线程的过程。挂起线程通过插入一个APC(异步过程调用)来阻止线程执行,然后等待SuspendCount变为0才会继续执行。恢复线程则是通过发送信号量和调用相关函数来唤醒线程,最终等待CPU调度。整个过程涉及到多个内核函数和数据结构的操作,以实现线程的挂起和恢复。
type
Post

SuspendThread 挂起线程

调用链

SuspendThread KERNEL32!SuspendThreadStubKERNELBASE!SuspendThreadntdll!NtSuspendThreadPsSuspendThreadKeSuspendThreadKiSuspendThreadKiInsertQueueApc

过程分析

未挂起前:
  • 查看线程的 apc 列表:(空的,表示目前没有 apc)
notion image
  • 查看线程的结构,看看 SuspendCount,通过 dt _KTHREAD ffffd3071fedd080
notion image
 
通过 SuspendThread 挂起线程后,再查看一下状态:
  • 线程的 apc 列表,增加了一个 ffffd3071fedd308 的 APC 对象
notion image
  • KTHREAD 中的 SuspendCount 也增加了
notion image
  • 再详细的看下增加的 APC 中包含什么内容
notion image
基本发现,就是塞进去了一个 nt!KiSchedulerApc 的方法。猜测这个方法是一直卡住,执行不完的。(Win2003中KiSuspendThread 逻辑就是等待 SuspendCount 变成0,KiSchedulerApc 中应该也是类似的逻辑)
💡
KiSchedulerApc 是在 Win10 22H2 上看到的,在 KiInsertQueueApc 中通过 Thread→SchedulerApc 塞进去的KiSchedulerApc (结合UAC的 token 校验不过,直接终止,猜测其中包含 token 的判断逻辑);查看 Win2003 的代码(reactos也是)是通过 Thread→SuspendApc,塞进去的 KiSuspendThread 。代码如下:

总结

可以看到其实挂起线程相对比较简单,只是插入了一个 APC,让此线程一直无法执行而已。
关于 APC 可以看这里:

ResumeThread 恢复线程

调用链

ResumeThread KERNEL32!ResumeThreadStubKERNELBASE!ResumeThreadntdll!NtResumeThreadPsResumeThread KeResumeThread KiWaitTest KiUnwaitThreadKiReadyThreadKiDeferredReadyThread

详细过程

KeResumeThread 代码如下:
KeResumeThread 核心做了几件事:
  • 根据 SuspendCount 和 FreezeCount 来判断是否需要恢复线程
  • 通过发送信号量,调用 KiWaitTest 来恢复线程

KiWaitTest 的代码如下:
KiWaitTest 主要做了以下事情:
  • 通过等待对象找到对象的等待队列
  • 将等待队列中的每一个线程通过 KiUnwaitThread 来进行唤醒
  • 其中 KiSatisfyObjectWait 就是通过设置信号状态,唤醒等待线程、切换上下文来响应等待的线程(这里详述看

KiUnwaitThread 的代码如下:
KiUnwaitThread 主要分两块:
  • KiUnlinkThread:主要将线程从它正在等待的内核对象队列中移除,即不等待任何对象
  • KiReadyThread:主要根据线程的状态(优先级、是否被分页了等),将线程置于 ready 状态,待 CPU 调度

总结

所谓的恢复线程,就是将线程的各种状态修改成 Ready 状态,然后告诉系统,我可以被调度了,然后等待 CPU 调度。这里的核心在于线程的调度。

参考链接:

  1. Anatomy of the thread suspension mechanism in Windows (Windows Internals)
  1. Suspending-Techniques
    diversenokUpdated Jan 2, 2024
  1. https://github.com/reactos/reactos/tree/master
 

© Frend Guo 2022 - 2024