周旋久 2025-10-14 18:02 上海
看雪论坛作者ID:周旋久
各大论坛、博客讲利用NMI插中断检查堆栈的时候普遍有一个错误:在NMI回调中直接调用了RtlWalkFrameChain或者RtlCaptureStackBackTrace。比如R0g大佬这里百密一疏:2024鹅厂游戏安全技术竞赛决赛题解-PC客户端https://bbs.kanxue.com/thread-281459.htmUNWIND_INFO来解析栈帧信息,此时如果触发Page Fault那就蓝屏GG。NMI中断正确的思路是在KPCR里找到NMI的异常栈地址,解析MACHINE_FRAME去拿RIP和RSP,再来判断是否rootkit。IST Index是记录在IDT表项中,触发中断/异常时的异常栈指针索引号IST Index,说明NMI触发时使用了Ist3这个栈指针//0x68 bytes (sizeof)struct _KTSS64{ ULONG Reserved0; //0x0 ULONGLONG Rsp0; //0x4 ULONGLONG Rsp1; //0xc ULONGLONG Rsp2; //0x14 ULONGLONG Ist[8]; //0x1c ULONGLONG Reserved1; //0x5c USHORT Reserved2; //0x64 USHORT IoMapBase; //0x66};
TSS在KPCR中:struct _KPCR{ union { struct _NT_TIB NtTib; //0x0 struct { union _KGDTENTRY64* GdtBase; //0x0 struct _KTSS64* TssBase; // <--- TSS //0x8 ... } ... } ...}
触发中断时操作系统会把返回地址的结构MACHINE_FRAME压入栈中以便IRETQ,从这里拿到RIP && RSPtypedef struct _MACHINE_FRAME{ UINT64 rip; UINT64 cs; UINT64 eflags; UINT64 rsp; UINT64 ss;} MACHINE_FRAME, *PMACHINE_FRAME;BOOLEANNmiCallback(_In_ BOOLEAN Handled){ UNREFERENCED_PARAMETER(Handled); UINT64 kpcr = 0; TASK_STATE_SEGMENT_64* tss = NULL; PMACHINE_FRAME machineFrame = NULL; kpcr = __readmsr(IA32_GS_BASE);// 0xC0000101 tss = *(TASK_STATE_SEGMENT_64**)(kpcr + KPCR_TSS_BASE_OFFSET);// 0x8 machineFrame = tss->Ist3 - sizeof(MACHINE_FRAME); { // 在这里对堆栈检查,比如是否在有效模块内 // 不在的话则认为是内核shellcode CheckStack(machineFrame->rip,machineFrame->rsp); }}
如果查到返回地址不在任何驱动模块内,则可以判断是类似Kdmapper这样的手动映射加载驱动,或者是内核shellcode个人感觉这种方法用在反作弊上效果是比较好的;因为外挂会高频读取游戏数据,用中断的方法很容易命中外挂的线程。*本文为看雪论坛优秀文章,由 周旋久原创,转载请注明来自看雪社区
看雪·第九届安全开发者峰会(SDC 2025)
# 往期推荐
无"痕"加载驱动模块之傀儡驱动 (上)
为 CobaltStrike 增加 SMTP Beacon
隐蔽通讯常见种类介绍
buuctf-re之CTF分析
物理读写/无附加读写实验
