zoukankan      html  css  js  c++  java
  • 查找非托管异常的来源

    有时,您会查看异常抛出后处理程序中的调用堆栈。如果附加到弹出watson对话框的未处理异常,这是非常常见的。
    它可能看起来像:

    kernel32!WaitForSingleObject+0xf

    devenv!DwCreateProcess+0xbb

    devenv!fExceptionHandling+0x1cb

    devenv!DwExceptionFilter+0x8b

    0x535ef48

    那没什么用。您真正想要的是查看抛出异常时的调用堆栈。在x86上执行此操作有一个技巧。(这可以调整为在64位平台上工作。)
    它可以在实时调试和小型转储中工作,甚至在没有任何符号的情况下也可以工作。我将首先给出如何做到这一点的快速步骤,然后我将解释它的工作原理。

    我怎么找到它?

     

    使用这些Windbg指令,但是任何带有内存搜索('s')和set context('.cxr')命令的本机调试器也可以执行此操作。

    1. 转到感兴趣的线程。
    2. 在包含dword 0x1003f的堆栈上搜索第一个地址。在Windbg中,键入“s-ds esp L1000 1003f”。异常有效地将0x1003f推送到堆栈上,因此这将有效地在线程的当前堆栈上查找异常的上下文。
    3. 至少应显示一个结果,如下所示: 0535ef48  0001003f 00000000 00000000 00000000 ?...............
      每行中的第一个数字(0535ef48)是地址,行的其余部分是该地址的上下文。原来第一个dword 0x1003f是发生异常上下文结构,那么0535ef48将是上下文的地址。如果得到多个条目,请使用第一行,因为这将对应于最新的异常。
    4. 将当前上下文设置为指向步骤3的结果中的第一个数字(本例中为0535ef48)。在windbg中,键入“.cxr 0535ef48”。

    现在应该多检查一下你的调用堆栈。在我的示例中,它看起来像:

    mscordbi!CordbHashTable::GetBase

    mscordbi!CordbThread::RefreshStack+0x349

    mscordbi!CordbProcess::DispatchRCEvent+0x1291

    mscordbi!CordbRCEventThread::ThreadProc+0x9

    eax=00000000 ebx=04700168 ecx=00000048 edx=0535f230 esi=00000000 edi=0535f254

    eip=636ac786 esp=0535f214 ebp=0535f228 iopl=0 nv up ei pl zr na po nc

    cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246

    mscordbi!CordbHashTable::GetBase:

    636ac786 80791400 cmp byte ptr [ecx+0x14],0x0 ds:0023:0000005c=??

    我的程序正在取消引用0x5c(=0x48+0x14)。难怪它坠毁了。
    请注意,我们不需要为原始堆栈上的任何模块添加任何符号来找到它。即使我们根本没有任何符号,我们仍然可以对崩溃的代码进行反汇编。

    为什么有用?

    这里有一些关键数据。
    1) 当操作系统抛出异常时,它将原始抛出站点的上下文推送到堆栈上。(这是异常指针的一部分)。
    2) 在x86上,上下文结构中的第一个字段是一个标志字段,它总是设置为0x1003f。由于其他原因,此值不太可能随机出现在堆栈顶部。
    3) 在x86上,堆栈增长。因此,当前堆栈指针(esp)表示0x1003f将出现的范围的高端。因此,如果我们要搜索接近堆栈顶部的内容(0x1000字节以内),我们可以在范围内(esp,esp-0x1000)进行搜索。
    4) 搜索dw的内存。格式为“s–d<范围><值>”。<range>的格式可以是“<address>L<length>”,它将搜索范围(address,address length)中的“value”。因此,“s-d esp L1000 1003f”意味着“搜索范围内的dword 0x1003f(esp,esp-0x1000)”。0x1000是一个任意的数字,在这里似乎足够了。
    5) 调试器可以从任何上下文执行stackwalk。大多数调试器只是自动使用线程的当前上下文(通过kernel32!GetThreadContext),但调试器没有理由不能使用任意上下文。Windbg提供了一个很好的命令,“.cxr”,它可以让您这样做。可以设置要检查的上下文。(VS也在添加此命令)。

    因此,如果您回顾最初的步骤,可以看到步骤2在线程堆栈中搜索异常所推送的上下文,然后步骤4告诉调试器查看当前上下文。如果步骤3给出了多行,则可能表示存在嵌套异常。也可能是一个局部变量“inti=0x1003f”。在这两种情况下,都可以对所有值(从最新的值开始)尝试.cxr,以找到有意义的调用堆栈。

    对于kicks,检查我们提供给.cxr的上下文指针,您可以自己看到它与register命令的输出相匹配:

    0:015> dt _CONTEXT 0535ef48

       +0x000 ContextFlags : 0x1003f

       +0x004 Dr0 : 0

       +0x008 Dr1 : 0

       +0x00c Dr2 : 0

       +0x010 Dr3 : 0

       +0x014 Dr6 : 0

       +0x018 Dr7 : 0

       +0x01c FloatSave : _FLOATING_SAVE_AREA

       +0x08c SegGs : 0

       +0x090 SegFs : 0x3b

       +0x094 SegEs : 0x23

       +0x098 SegDs : 0x23

       +0x09c Edi : 0x535f254

       +0x0a0 Esi : 0

       +0x0a4 Ebx : 0x4700168

       +0x0a8 Edx : 0x535f230

       +0x0ac Ecx : 0x48

       +0x0b0 Eax : 0

       +0x0b4 Ebp : 0x535f228

       +0x0b8 Eip : 0x636ac786

       +0x0bc SegCs : 0x1b

       +0x0c0 EFlags : 0x210246

       +0x0c4 Esp : 0x535f214

       +0x0c8 SegSs : 0x23

       +0x0cc ExtendedRegisters : [512] "???"

    0:015> r

    Last set context:

    eax=00000000 ebx=04700168 ecx=00000048 edx=0535f230 esi=00000000 edi=0535f254

    eip=636ac786 esp=0535f214 ebp=0535f228 iopl=0 nv up ei pl zr na po nc

    cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246

    这主要适用于本机异常;但也适用于托管异常+SOS。
    从经验上讲,我已经使用这种技术很长时间了,而且每次都非常有效。我还没有找到一个值为0x1003f的杂散局部。

  • 相关阅读:
    python3 day02 大纲
    python3 练习题 day02
    python3 练习题(购物车)
    python3 练习题(多级菜单)
    python3 day01 大纲
    python3 练习题 day01
    vuex 的基本使用
    jquery中Ajax使用Promise指定成功回调函数
    使用Promise 解决回调地狱
    Promise 概念及操作
  • 原文地址:https://www.cnblogs.com/yilang/p/13906051.html
Copyright © 2011-2022 走看看