You need to enable JavaScript to run this app. Windows下如何在PsSetCreateProcessNotifyRoutine回调中获取主线程ID?
优惠活动
大模型
产品
解决方案
定价
更多

Windows下如何在PsSetCreateProcessNotifyRoutine回调中获取主线程ID?

PsSetCreateProcessNotifyRoutine 回调内获取进程主线程ID的简便方案

首先明确实现前提:
旧版进程创建回调 PsSetCreateProcessNotifyRoutine 的标准函数原型为:

VOID CreateProcessNotifyRoutine(
  HANDLE  ParentProcessId,
  HANDLE  CreatedProcessId,
  BOOLEAN Create
);

回调传入的参数仅包含父进程ID、目标进程ID、进程创建/退出标识,没有附带任何线程上下文信息,所有主线程ID的获取都需要基于已拿到的CreatedProcessId做二次查询,不存在参数直接返回的捷径。

按简便程度排序的可行实现

  • 方案1:直接读取EPROCESS内置的主线程指针(性能最优、代码量最少)
    拿到回调传入的CreatedProcessId后,第一步调用PsLookupProcessByProcessId获取目标进程的EPROCESS结构体指针。从Windows NT 4.0开始,EPROCESS结构体内部固定存储了指向进程初始主线程的PETHREAD指针,只需要针对你适配的目标Windows版本,查好这个指针在EPROCESS内的固定偏移,直接读取对应的PETHREAD值,再调用PsGetThreadId就能直接拿到主线程ID。
    注意:因为不同Windows版本的EPROCESS结构体布局不同,不要硬编码跨版本的结构体偏移,需要为每个适配的旧系统版本单独维护偏移映射表。
  • 方案2:匹配系统线程信息的时间戳判定主线程(零硬编码、兼容性最强)
    不想维护结构体偏移表的话,可以在回调内调用ZwQuerySystemInformation,传入SystemProcessInformation类号,获取全系统的进程、线程快照。遍历快照时匹配到和回调传入的CreatedProcessId相同的进程条目,找到该进程下创建时间最早的线程,对应的线程ID就是主线程ID——因为主线程是进程初始化阶段第一个被创建的线程,天然比后续创建的工作线程创建时间更早。
    这个方案不需要依赖任何非公开的结构体偏移,适配Windows 2000到Win10全版本的兼容性极强,代码维护成本低,只是全量快照遍历的性能比直接读EPROCESS字段稍差。

实现注意事项

  • 两种方案都可以在PsSetCreateProcessNotifyRoutine的回调上下文内安全运行,不需要额外切换进程上下文,只要确保操作时不持有自旋锁、不对用户态地址做直接解引用即可。
  • 触发回调时进程的主线程已经完成初始化,两种方法都能稳定拿到主线程ID,不存在时序上的竞态问题。
  • 如果适配的是Windows XP SP1及更早的系统,系统内没有导出PsGetThreadId函数,需要直接从拿到的PETHREAD结构体的对应偏移读取Cid.UniqueThread字段值,就是对应的线程ID。
  • 不要把任意属于目标进程的线程判定为主线程,必须通过上面两种方式拿到的初始线程才是真正的进程主线程。

内容的提问来源于stack exchange,提问作者user7225528

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.03 04:15:02