![]() |
About Handling Nmi |
本文基于Windows2003以上32位系统,因为XP处理NMI很弱,我们后面再说。 Dumping IDT: 相关的中断可以查阅详细资料[1],我们只关心下面两个: 1)首先是为NMI建立任务门,这样我们就能捕捉到NMI了: 2)然后是建立堆栈: 这里涉及一些结构,用Windbg可以清晰地看到: 如 果想在XP里设置Handler处理NMI,那么也该仿造以上过程,设置处理例程KiTrap02,把NMI-TSS里的EIP指向你的处理代码,然后 建立堆栈,最后还要在iret后面添加jmp回到你的处理代码.原因简单地说,就是每当接受一个NMI中断,处理器会在内部屏蔽再次响应NMI,这一屏蔽 过程直到执行中断返回指令IRET后才结束。还是直接看反汇编的KiTrap02代码吧:
大 致的流程是更新TSS,屏蔽中断响应,保存CPU状态,调用用户注册的NMI处理例程;如果不能处理,则交给默认的Nmi Handler,也就是HalHandleNMI(x),但是它并不能做什么实质性的解决工作,只是准备BugCheck...在XP下,NMI一旦触 发,都是交给HalHandleNMI,然后直接crash掉系统.Win2003下提供了一个很好的机制KeRegisterNmiCallback, 你可以注册Nmi的处理例程,这样发生Nmi中断的时候,你可以进行一些收尾工作,但是注意在回调函数里有很多的限制,不能有系统函数调用,不能去获取 SpinLock,必须使用Interlocked系列进行数据操作等等.. 函数原型: 还原出来的源代码如下:
以上是全局变量和结构体的声明.
其中,gKiNmiCallbackListHead是系统的一个全局变量,它指向最后注册的CallBackRecord,以反向的链表把所有的CallBack连接起来.正如MSDN中描述的: 正如前面的反汇编代码,NMI处理的流程大致为KiSystemStartup -> _KiTrap02 -> KiHandleNmi -> HalHandleNMI.把其他两个函数的代码也还原如下:
HalHandleNMI比较长,加了点注释:
有 意思的是HalpNMIDumpFlag这个全局标志,它决定了NMI触发而且要Crash系统的时候是否生成 dump[3].其实就是注册表的这个位置:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control \CrashControl,CrashDumpEnabled设置为2,NMICrashDump设置为1.这个可以为服务器生成dump以调试,比如 DELL PE2850,设置好BISO允许使用NMI Button后,你只要按下机器上的NMI按钮就可以了. todo... |