看雪学院 10月14日
NMI中断处理栈检查的正确姿势
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文纠正了在NMI回调中直接调用RtlWalkFrameChain或RtlCaptureStackBackTrace的常见错误。作者指出,在64位系统下,这些函数会扫描UNWIND_INFO,若触发Page Fault将导致蓝屏。文章详细阐述了正确的NMI中断处理思路:通过KPCR获取NMI异常栈地址,解析MACHINE_FRAME获取RIP和RSP,进而判断是否存在rootkit。文中还提供了TSS段和KPCR的结构定义,并给出了一个实际的NMI回调函数示例,演示了如何正确地获取栈帧信息以进行堆栈检查,尤其强调了此方法在反作弊领域的应用潜力。

⚠️ **NMI中断处理的常见误区与纠正**:许多安全研究者在NMI回调中直接调用栈回溯函数(如RtlWalkFrameChain、RtlCaptureStackBackTrace),这在64位系统下存在风险,因为它们会解析UNWIND_INFO,一旦触发Page Fault即可能导致系统崩溃。正确的做法是利用KPCR获取NMI异常栈地址,解析MACHINE_FRAME结构来获取当前的RIP和RSP,以此进行栈帧信息分析。

⚙️ **NMI中断与栈指针解析机制**:文章详细解释了NMI中断如何映射到IDT表中的特定向量号(通常是2),并通过IST Index找到对应的异常栈指针(如Ist3)。该指针的具体值保存在TSS段中,而TSS段的基地址则存储在KPCR结构体里。通过`__readmsr(IA32_GS_BASE)`可获取KPCR,进而定位到TSS,最终通过`Ist3 - sizeof(MACHINE_FRAME)`获取栈顶的`MACHINE_FRAME`,其中包含了关键的RIP和RSP信息。

🛡️ **基于NMI栈检查的反作弊应用**:作者认为,通过NMI中断检查栈帧信息,特别是判断返回地址是否落在已知模块内,是一种有效的反作弊手段。当检测到返回地址不在任何驱动模块内时,可以推断出存在手动映射的加载驱动(如Kdmapper)或内核shellcode。这种方法对于频繁读取游戏数据的外挂尤其有效,因为中断的触发很容易命中外挂线程。

周旋久 2025-10-14 18:02 上海

看雪论坛作者ID:周旋久

各大论坛、博客讲利用NMI插中断检查堆栈的时候普遍有一个错误:在NMI回调中直接调用了RtlWalkFrameChain或者RtlCaptureStackBackTrace。比如R0g大佬这里百密一疏:2024鹅厂游戏安全技术竞赛决赛题解-PC客户端https://bbs.kanxue.com/thread-281459.htm

64位下,这两个函数会扫描PE文件的UNWIND_INFO来解析栈帧信息,此时如果触发Page Fault那就蓝屏GG。

NMI中断正确的思路是在KPCR里找到NMI的异常栈地址,解析MACHINE_FRAME去拿RIPRSP,再来判断是否rootkit。

IST Index是记录在IDT表项中,触发中断/异常时的异常栈指针索引号

NMI中断在机器上映射的中断向量号总是2:

找到对应的IDT表项,这里的03就是IST Index,说明NMI触发时使用了Ist3这个栈指针

Ist3具体的值保存在TSS段中,如下:

//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 && RSP

typedef 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分析

物理读写/无附加读写实验

球分享

球点赞

球在看

点击阅读原文查看更多

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

NMI中断 栈检查 rootkit检测 内核安全 反作弊 KPCR TSS MACHINE_FRAME Windows内核 NMI interrupt stack inspection rootkit detection kernel security anti-cheat Windows kernel
相关文章