前面介绍了用Windbg截取1st chance exception进行分析的方法。
但是好多情况下,程序并没有运行在调试器下。崩溃发生后留在桌面上的是红色的框框,这时候已经错过了第一现场,但还是有机会找到对应exception的信息。
前面介绍过,红色的框框是通过UnhandledExceptionFilter函数显示出来的,而UnhandledExceptionFilter的参数就包含了异常信息。这个时候检查UnhandledExceptionFilter的参数,就可以找到异常信息和异常上下文的地址,然后通过.exr和.cxr就可以在Windbg中把对应信息打印出来。
(注意:在Vista和Windows 2008中,系统改良了Error Reporting功能。程序崩溃后,系统会在Error Reporting的时候从内核直接挂起出错的进程。这个时候如果用调试器检查,会看到出错进程就停在发生问题的指令上,不再需要在调试器中手动恢复exception context。
详细信息可以参考:
拿案例2中的第2个例子做一个实验。直接运行,崩溃后看到弹出的框框。这个时候不要点击确定,而是启动Windbg,attach到这个进程,然后用kb命令打印出call stack,找到UnhandledExceptionFilter的参数:
0:000> kb ChildEBP RetAddr Args to Child 0012f74c 7c821b74 77e999ea d0000144 00000004 ntdll!KiFastSystemCallRet 0012f750 77e999ea d0000144 00000004 00000000 ntdll!ZwRaiseHardError+0xc 0012f9bc 004339be 0012fa08 7ffdd000 0044c4d8 kernel32!UnhandledExceptionFilter+0x4b4 |
第一个参数0012fa08保存的就是异常信息和异常上下文的地址:
0:000> dd 0x0012fa08 0012fa08 0012faf4 0012fb10 0012fa34 7c82eeb2 |
接下来用.exr加上异常信息地址打印出异常的信息:
0:000> .exr 0012faf4 ExceptionAddress: 0041a5a8 (release_crash!main+0x00000028) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000001 Parameter[1]: 00000000 Attempt to write to address 00000000 |
然后可以用.cxr加上异常上下文地址来切换上下文:
0:000> .cxr 0012fb10 eax=00000000 ebx=7ffde000 ecx=00000000 edx=00000001 esi=00000000 edi=0012fedc eip=0041a5a8 esp=0012fddc ebp=0012fedc iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
release_crash!main+0x28: 0041a5a8 c60000 mov byte ptr [eax],0x0 ds:0023:00000000=?? |
上下文切换完成后,可以用kb命令重新打印出该上下文上的call stack,就可以看到异常发生时候的状态:
0:000> kb *** Stack trace for last set context - .thread/.cxr resets it ChildEBP RetAddr Args to Child 0012fedc 00427c90 00000001 00361748 003617d0 release_crash!main+0x28 [c:\documents and settings\lixiong\desktop\amobrowser\release_crash.cpp @ 51] 0012ffc0 77e523cd 00000000 00000000 7ffde000 release_crash!mainCRTStartup+0x170 0012fff0 00000000 00418b18 00000000 78746341 kernel32!BaseProcessStart+0x23 |
这里可以直接看到问题发生在release_crash.cpp文件的第51行。