DUMP文件分析2:一个最简单的DUMP分析示例

来源:互联网 发布:java核心技术卷一下载 编辑:程序博客网 时间:2024/06/16 09:31

本节开始,我将在示例中给大家讲述基本的DUMP文件分析方法。读者应该对Windows系统比较了解,同时比较熟悉Windbg。
本节的示例非常简单,也非常经典,就是常常会遇到的访问空指针。Windows将进程内存空间的一段范围设置为NULL(64KB),这段空间禁止访问。一旦我们在程序中不小心访问了这段地址(通常是指针未初始化),就会引起访问越界异常,造成程序崩溃,例如本示例:

这是一个简单的MFC程序,在Crash Me!的消息响应函数中,写有以下代码:

int* p = NULL;int a = *p;

即刻意让程序读取空指针而崩溃。
首先,我们使用第1节中编程方法保存DUMP,事实上,第1节中的示例就是本节的程序。我们将自动保存的mini.dmp拖入Windbg中(直接拖入即可,当然也可以通过菜单“File”——“Open Crash Dump…”来打开),打开之后,可以看到一些信息:

可以看到详细的DUMP类型,同时,Windbg还对DUMP文件进行了初步分析,找到了异常。事实上,这正是示例程序发生的异常。(访问违例,异常代码0xC0000005)。然而,仅仅知道发生异常的类型是不够的,我们还需要更详细的信息,那么,从什么地方入手呢?
首先,我们总是期望调试器足够智能,我们什么都不用做不用想,调试器就可以把问题详细的揭露出来,Windbg能够做到吗?答案是肯定的。Windbg带有自动的异常分析命令(!analyze),我们输入 !analyze -v,其中 -v 选项是尽可能输出详细的信息。然后,我们得到了一大堆输出。我们来详细看一下这些输出的含义,由于输出比较长,我将分段来介绍。

0:000> !analyze -v*********************************************************************                                                                             *                        Exception Analysis                                                                                                          *********************************************************************Failed calling InternetOpenUrl, GLE=12029

微软维护有在线的崩溃解决方案,这里是尝试连接在线数据库,然而失败。这个功能你应该已经看到过:

如果你的电脑没有连网,那么你可能看不到该窗口或者窗口一闪而过。

FAULTING_IP: dump1+1310d00f7310d 8b08            mov     ecx,dword ptr [eax]

FAULTING_IP,很重要的信息,IP就是EIP,即出错的指令地址。

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)ExceptionAddress: 00f7310d (dump1+0x0001310d)   ExceptionCode: c0000005 (Access violation)  ExceptionFlags: 00000000NumberParameters: 2   Parameter[0]: 00000000   Parameter[1]: 00000000Attempt to read from address 00000000

EXCEPTION_RECORD,很重要的信息,记录崩溃的信息。可以看到异常地址,异常代码以及具体的异常原因“尝试读取地址00000000”。

DEFAULT_BUCKET_ID:  NULL_POINTER_READ

异常分类,这里异常类别很明确NULL_POINTER_READ。

PROCESS_NAME:  dump1.exe

发生异常的进程名。

STACK_TEXT:  WARNING: Stack unwind information not available. Following frames may be wrong.0045ef80 579f7072 00000000 00000176 00000004 dump1+0x1310d0045efc4 579f77ba 0045f8dc 000003e8 00000000 mfc90d!_AfxDispatchCmdMsg+0xb2 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp @ 82]0045f028 579cd5f3 000003e8 00000000 00000000 ...

STACK_TEXT很重要,显示异常发生时的函数调用栈。最后一列容易理解,是模块+函数,那前面还有5列数字,表示什么呢?第一列表示函数调用时的EBP,第二列表示函数的返回地址,后三列则是函数的参数(不完全)。

LAST_CONTROL_TRANSFER:  from 579f7072 to 00f7310d

LAST_CONTROL_TRANSFER表示最后一次控制流的转移,即最后一次函数调用,可以将后面的地址与调用栈比较。

FOLLOWUP_IP: dump1+1310d00f7310d 8b08            mov     ecx,dword ptr [eax]

与FAULTING_IP类似。

SYMBOL_STACK_INDEX:  0SYMBOL_NAME:  dump1+1310dFOLLOWUP_NAME:  MachineOwnerMODULE_NAME: dump1IMAGE_NAME:  dump1.exe

表示崩溃指令所在的模块,符号等等。其中SYMBOL_NAME可以明确的指出函数。

好,!analyze -v命令输出的说明到此为止。经过上面的说明,我们已经知道了问题的原因,以及具体的位置。但你是否发现了一些问题,与dump1相关的内容都只有相对于dump1模块的地址(如dump1+1310d),并不知道具体是哪一个函数。这是因为Windbg没有加载相应的pdb调试文件,我们可以在Windbg的“Symbol File Path”中添加dump1的pdb文件路径,然后重新载入。你可能会问,如果是release版本,没有生成pdb文件怎么办?这样的话对于调试来说就比较麻烦了,此时,你只能通过逆向错误指令附近的代码,来推测错误的位置。一般来说,为了调试方便,最好在生成Release版本时也生成调试文件。加载pdb文件之后,dump1内部的符号也解析了出来:

FAULTING_IP: dump1!Cdump1Dlg::OnBnClickedButton1+2d [d:\projects\dump\dump1\dump1\dump1dlg.cpp @ 160]0017310d 8b08            mov     ecx,dword ptr [eax]

如果有源代码的话,还可以直接定位源代码:

FAULTING_SOURCE_CODE:     156: void Cdump1Dlg::OnBnClickedButton1()   157: {   158:     int* p = NULL;   159: >  160:     int a = *p;   161: }

这样的话,错误就一目了然了。
[ PS:写示例的时候没有注意,如果你以Release编译,并开启优化的化,会发现不成功。这是由于代码中 a 变量并没有被使用,编译的时候被优化掉了,你可以把 int a= *p 改为 printf(“%d\n”, *p); ]

0 0