在调用栈中挖掘异常指针

原文地址: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异常的信息。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值