从栈回溯来看,(1)在WinGUI进程中,xerces模块内执行的浮点类指令触发了浮点异常(#NM);(2)在系统的浮点异常处理函数KiTrap07中,fxsave指令用来把浮点计算的上下文状态先保存起来;(3)保存的位置是当前线程的内核态栈,也就是目前esi所指向的内存,起始地址为bcf4fdf0;(4)在向以上地址保存数据时,触发了页错误异常(#PF);(5)负责处理页错误异常的KiTrap0E被执行,这是很复杂的一个异常处理函数,当这个函数认为当前是在高级别触发的一个页错误时,根据系统的规则,此情况下不可以执行虚拟内存交换机制,于是发起蓝屏(Bug Check),蓝屏的错误码就是IRQL_NOT_LESS_OR_EQUAL,意思是当前的IRQL没有小于或者等于(not less or equal)DISPATCH_LEVEL这个要求。
上面1、2两点属于正常的现象,第3点时已经有问题了,把用来保存浮点上下文的目标地址bcf4fdf0和当时的EBP寄存器做比较,二者相减:
lkd> ? bd00bd64-bcf4fdf0Evaluate expression: 769908 = 000bbf74
加除以1024,换算成KB:
lkd> ? (bd00bd64-bcf4fdf0)/0n1024Evaluate expression: 751 = 000002ef
哇,有700多KB,内核态栈默认是12KB,大内核态栈的大小记录在nt!MmLargeStackSize变量中,通常也就在几十KB。现在看来,上面计算出的目标地址显然“溢出了”,不在有效的栈空间上了,因此FXSAVE指令在使用这个内存时发生Page Fault异常了。
KiTrap07是从当前线程的InitialStack字段来读取栈顶的位置的,然后做减法来从栈上分配保存浮点状态的空间,大小为0x210字节。
lkd> dt _KTHREAD -y Inint!_KTHREAD +0x018 InitialStack : Ptr32 Void +0x0df InitialNode : UChar
这样看了,可能是当前线程的KTHREAD结构被损坏了。
如果能够重现这个问题,那么建议使用双机内核调试,在崩溃时观察线程的KTHREAD结构。如果确认是这个结构被破坏,那么可以设置变量监视断点寻找破坏者的“黑手”。