在调用栈中挖掘异常指针
原文地址:http://blogs.msdn.com/b/oldnewthing/archive/2006/08/21/710754.aspx 大家也许经常盯着调用栈,寻找捕捉到的异常以及在哪里发生的异常。 ChildEBP RetAddr Args to Child 030c21d0 76df3448 00000000 030c6138 76db6b0d ntdll!DbgBreakPoint 030c21dc 76db6b0d 030c2204 77b8d585 030c220c ole32!PeekMessageExceptionFilter+0x42 030c21e4 77b8d585 030c220c 00000000 030c220c ole32!CCliModalLoop::MyPeekMessage+0x41 030c220c 77f36992 030c25d0 030c6128 030c22e8 msvcrt!_except_handler3+0x61 030c2230 77f36964 030c25d0 030c6128 030c22e8 ntdll!ExecuteHandler2+0x26 030c22d8 77f36884 030c1000 030c22e8 00010007 ntdll!ExecuteHandler+0x24 030c25b8 77f6e0dd 030c25d0 00000000 00000000 ntdll!RtlRaiseException+0x3d 030c262c 77d3c239 77d4a4b6 77d3e2c5 030c3767 ntdll!RtlDeactivateActivationContextUnsafeFast+0x233 030c2630 77d4a4b6 77d3e2c5 030c3767 030c26a0 USER32!UserCallWinProcCheckWow+0x167 030c2634 77d3e2c5 030c3767 030c26a0 77d4a46f USER32!_NLG_Return2 030c265c 77d3e288 030c57b4 ffffffff 030c2688 USER32!__local_unwind2+0x70 030c2688 77f36992 030c26f8 030c57b4 030c27a4 USER32!_except_handler3+0xd5 030c26ac 77f36964 030c26f8 030c57b4 030c27a4 ntdll!ExecuteHandler2+0x26 030c2a74 77b8d36d 030c6128 77b8d36d 00000000 ntdll!ExecuteHandler+0x24 030c2a9c 77b8d59d 030c6128 030c2ac0 00000000 msvcrt!__global_unwind2+0x18 030c2ac0 77f36992 030c2ba4 030c6128 030c2bc0 msvcrt!_except_handler3+0x75 030c2ae4 77f36964 030c2ba4 030c6128 030c2bc0 ntdll!ExecuteHandler2+0x26 030c2b8c 77f36796 030c1000 030c2bc0 030c2ba4 ntdll!ExecuteHandler+0x24 030c2b8c 77b7aa54 030c1000 030c2bc0 030c2ba4 ntdll!KiUserExceptionDispatcher+0xe 030c3300 77b7b4dc 030c3324 7715b1b4 00000000 msvcrt!_woutput_l+0x18 (大家可以从符号服务器上获取到需要的调试符号,如果你的磁盘空间上G的话,也可以一次下载全部的符号信息。不过即便你打包下载了符号信息,你还是得需要符号服务器。因为调试信息会随着系统文件的更新而相应更新。) 这里可以看到,PeekMessageExceptionFilter函数捕捉到了一个异常。那是什么异常?首先,异常过滤函数会被传入一个EXCEPTION_POINTERS结果作为参数。 typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord; } EXCEPTION_POINTERS,*PEXCEPTION_POINTERS; 现在,我们看一下PeekMessageExceptionFilter的参数: 030c21dc 76db6b0d 030c2204 77b8d585 030c220c ole32!PeekMessageExceptionFilter+0x42 0:0000> dd 030c2204 l2 030c2204 030c25d0 030c22e8 ? ? ? ? ?-------- -------- ? ? ? ? ?.exr ? ? .cxr 第一个值是ExceptionRecord的指针,第二个值是ContextRecord的指针。你可以通过输入命令.exr 030c25d0查看异常信息,还可以通过.cxr 030c22e8查看异常的上下文内容(比如,异常发生时在执行什么指令)。这两个值也作为ExecuteHandler2函数的第一和第三个参数出现。 (译注:.cxr实际上会执行切换上下文的操作。) 现在就用.exr 030c25d0看一下异常信息,异常代码是c015000f,正好是STATUS_SXS_EARLY_DEACTIVATION。切换到发生异常的上下文,调用栈显示如下: ChildEBP RetAddr 030c262c 77d3c239 77d4a4b6 77d3e2c5 030c3767 ntdll!RtlDeactivateActivationContextUnsafeFast+0x233 030c2630 77d4a4b6 77d3e2c5 030c3767 030c26a0 USER32!UserCallWinProcCheckWow+0x167 030c2634 77d3e2c5 030c3767 030c26a0 77d4a46f USER32!_NLG_Return2 030c265c 77d3e288 030c57b4 ffffffff 030c2688 USER32!__local_unwind2+0x70 030c2688 77f36992 030c26f8 030c57b4 030c27a4 USER32!_except_handler3+0xd5 030c26ac 77f36964 030c26f8 030c57b4 030c27a4 ntdll!ExecuteHandler2+0x26 030c2a74 77b8d36d 030c6128 77b8d36d 00000000 ntdll!ExecuteHandler+0x24 030c2a9c 77b8d59d 030c6128 030c2ac0 00000000 msvcrt!__global_unwind2+0x18 030c2ac0 77f36992 030c2ba4 030c6128 030c2bc0 msvcrt!_except_handler3+0x75 030c2ae4 77f36964 030c2ba4 030c6128 030c2bc0 ntdll!ExecuteHandler2+0x26 030c2b8c 77f36796 030c1000 030c2bc0 030c2ba4 ntdll!ExecuteHandler+0x24 030c2b8c 77b7aa54 030c1000 030c2bc0 030c2ba4 ntdll!KiUserExceptionDispatcher+0xe 030c3300 77b7b4dc 030c3324 7715b1b4 00000000 msvcrt!_woutput_l+0x18 哇,在处理一个异常的时候又发生了另外一个异常。(碰巧这次在原来的调用栈里一眼就能看出来,但是通常情况下,找到后面的异常可能没那么容易。) 拿这个异常重复一次上面的练习: 0:000> .exr 030c2ba4 ExceptionAddress: 77b7aa54 (msvcrt!_woutput_l+0x00000018) ExceptionCode: c00000fd (Stack overflow) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000001 Parameter[1]: 030c2e88 0:000> .cxr 030c2bc0 eax=030c33b0 ebx=00000000 ecx=0000005c edx=00000000 esi=030c33c4 edi=030c33c4 eip=77b7aa54 esp=030c2e8c ebp=030c3300 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 msvcrt!_woutput_l+0x18: 001b:77b7aa54 53 push ebx哈哈,原来SXS异常是由堆栈溢出引起的。在这个上下文里,你可以使用“k”命令查看是如何引发这个结果的。 这个bug是由于错误的递归导致的栈溢出引发的。当时线程正在为调用一个COM对象,等待它返回。而这个时候又收到了一个新的请求。问题的细节在这里并不重要;本文的目的就是展示如何在调用栈上挖掘异常指针,从中了解引发问题的Win32异常的信息。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |