Windows内核中引入了 Silo(服务隔离对象,Service Isolation Object) 机制,用于实现细粒度资源隔离。Silo 最初在 Windows Server 2019 和 Windows 10 后期版本中逐步引入,并在后续版本(如 Windows 11 和 Windows Server 2022)中进一步完善。全局变量 PspHostSiloGlobals 指向一个类型为 _ESERVERSILO_GLOBALS 的结构体,描述主操作系统 Silo(即传统 Windows 环境)。其中,PspHostSiloGlobals::EtwSiloState 为描述 ETW 子系统的 Silo。_ETW_SILODRIVERSTATE 用于记录Silo(Windows 容器/Job 隔离环境)中的 ETW 全局状态,包含该 Silo 内的所有 GUID 注册表、Logger 列表等。Logger 是一个 ETW tracing session(跟踪会话),负责收集来自一个或多个 Provider 的事件,实现日志的收集与存储。Logger 对象的关键成员为 LoggerId/InstanceGuid/SiloState。其中,LoggerId描述当前 Logger 位于 EtwpLoggerContext 中的索引——EtwpHostSiloStat::EtwpLoggerContext[LoggerId];SiloState 指向当前所属 Silo 对象。_WMI_LOGGER_CONTEXT 用于记录ETW 日志会话(Logger Session),负责收集事件、管理缓冲区、连接消费者等。Provider 是事件的来源,通常是驱动程序、内核组件或用户态应用程序,它通过 ETW API 注册自己,并在运行时发出事件。每个 Provider 由一个唯一的 GUID(全局唯一标识符) 标识。在内核中,驱动程序通常使用 EtwRegister 注册为 Provider,使用 EtwUnregister 注销。_ETW_GUID_ENTRY 表示一个 Provider GUID 的全局注册信息(如 {69079C8B-...}),包含所有对该 GUID 的注册(即多个 _ETW_REG_ENTRY)。_ETW_REG_ENTRY 表示 某一会话/进程对某个 GUID 的一次启用(Enable)操作,记录谁启用了、启用级别、回调等,提供 Provider 与 Logger 之间的绑定记录。全局变量 PspHostSiloGlobals 虽然是未导出函数,但可通过硬编码计算直接获取。
相关函数,如 PsGetServerSiloServiceSessionId、PsGetServerSiloGlobals、PsGetServerSiloActiveConsoleId,可直接在函数硬编码中获取。
PspHostSiloGlobals+0x360/EtwpHostSiloState -> +0x1b0(假设为LoggerContextTable) -> LoggerContextTable[LoggerId] -> _WMI_LOGGER_CONTEXT
PspHostSiloGlobals+0x360(记录与ETW相关数据的指针)与全局变量EtwpHostSiloState一致,这是在 EtwInitializeSiloState 函数中完成赋值的。其中,rdi 指向 _ETW_SILO_STATE 结构体(未导出,暂且如此称呼吧...)。
LoggerContextTable[LoggerId]的相关来源可参见函数 EtwpAcquireLoggerContextByLoggerId,反汇编伪代码如下:系统调用服务例程的执行位于 KiSystemServiceCopyEnd 函数中,通过 call rax 实现。图示可以看到,在执行系统调用前进行了两次全局标记的判断,即 KiDynamicTraceMask 和 PerfGlobalGroupMask。前者用于动态调试信息记录,而后者用于 ETW 日志记录。
KiDynamicTraceMask__systemcall 代码段内部在“call rax”前后分别调用 KiTrackSystemCallEntry/KiTrackSystemCallExit 进行事件追踪。事实上,这两个函数内部分别调用 PerfInfoLogSysCallEntry/PerfInfoLogSysCallExit 进行 ETW 记录。
类似的,PerfGlobalGroupMask__systemcall 内部采用了同样的策略。
值得注意的是,ETW 的触发依赖于 test dword ptr cs:PerfGlobalGroupMask+8, 40h。我所参考的 InfinityHook 项目中,ckcl logger context 的定位依赖于 EtwpDebuggerData 特征码扫描定位,而栈帧中存储系统调用服务例程地址的定位依赖于 PerfInfoLogSysCallEntry 函数中的压入栈帧中的 magic。事实上,函数 PerfInfoLogSysCallExit 也同样存在 magic。可以通过两个函数的 magic 差异以判断当前栈帧是 call rax 前/后被触发,可进行定制化的操作。此外,由于记录 system__call 的 LoggerContext->GetCpuLock 触发路径是固定的,可通过计算调用栈来进行快速定位,以尽可能降低延迟。path1 (test dword ptr cs:PerfGlobalGroupMask+8, 40h): PerfGlobalGroupMask__systemcall ==> PerfInfoLogSysCallEntry
path2 (test cs:KiDynamicTraceMask, 1): KiDynamicTraceMask__systemcall ==> KiTrackSystemCallEntry ==> PerfInfoLogSysCallEntry可通过 trap_frame 指针进行快速定位,_kthread::trap_frame 指针指向 trap_frame+0x00,即 PerfGlobalGroupMask__systemcall/KiDynamicTraceMask__systemcall 的栈顶。需要注意的是,如果系统调用参数个数大于4,函数 KiSystemServiceGdiTebAccess 中将会进行栈扩展以复制来自用户态的参数。etw_hacker 运行截图及其调用栈如下所示:
nt!_ESERVERSILO_GLOBALS
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-289632.htm
[原创]ETW机制与InfinityHook的新姿势
398 浏览
5 回复
学到了,又多了一种if hook姿势
感谢大佬,学习了
感谢大佬 ,希望大佬能出一个写好的代码
厉害的,其实还有很多姿势
saloyun
厉害的,其实还有很多姿势
请问是有其他可以代替loggerContext->getcpulock进行回调触发吗?我所知道的可通过etw日志记录的全局变量PerfGlobalGroupMask来控制,可实现在内核相关位置触发回调。loggerContext有一个回调例程项,但还没研究。
厉害的,其实还有很多姿势
请问是有其他可以代替loggerContext->getcpulock进行回调触发吗?我所知道的可通过etw日志记录的全局变量PerfGlobalGroupMask来控制,可实现在内核相关位置触发回调。loggerContext有一个回调例程项,但还没研究。