Windbg dump分析 学习总结

来源:互联网 发布:算法 编辑:程序博客网 时间:2024/06/05 22:39

Windbg核心调试之dump分析

http://www.pediy.com/kssd/pediy08/pediy8-428.htm
标 题: Windbg核心调试之dump分析
作 者:Lvg
时 间:2006-11-17 12:56 
链 接:http://bbs.pediy.com/showthread.php?threadid=35044
调试环境:winxp sp2+windbg ver:6.6.0007.5+vmware 5.5.2
附件:点击下载

一.Dump文件的产生,意义和类型

    当系统发生错误是,最常见的就是蓝屏(Blue screen),这时就会在系统目录下产生一个Dump文件

,如MEMORY.DMP 。这个文件的主要意义在于分析系统错误发生的原因,以作出解决的方法。
   它可分为三种类型:
   1.完全内存转储。这个文件比较大,和物理内存相当,包含了程序崩溃前系统及用户模式下的所有信
息。
   2.核心内存转储。这个文件大小约物理内存的三分之一,主要包含崩溃前系统内核的运行情况。一般
为了分析内核错误,就选用这种文件。
   3.小内存转储。这个文件小,只有64k,刚好一个页面文件大小。它包含了相对比较少的信息,主要
可用于微软的在线分析。
   以上三种形式的文件可以在我的电脑——〉鼠标右键——〉属性——〉高级——〉故障及恢复中设置
。如下图:

二 Dump文件的强迫产生

   由于我们也不知道何时会产生一个系统错误,从而得到dump文件,所以当练习分析时,可认为强迫产

生一个。一般有以下两个办法。
   1.双机联调。这里的双机可以是物理上的两台电脑,也可以是用虚拟机模拟。我想这里的大多数人应

该选择后者,为啥?还不是money的问题~_^。当用windbg把被调试机联上以后,就可以用.crash命令产

生一个蓝屏,当然之前要在被调试机里把dump产生的路径和类型设定好。还有另外一张办法,是通过修

改注册表后,用键盘产生dump,但这种方法哪有第一种来的快,所以就不说了,感兴趣的可以查查

windbg帮助文档看看。
   2.单机驱动产生。这种方法,不必用双机联调,在本机上就可以办到。由于驱动深入到了内核,它的

要求非常苛刻,一个简单的除零操作就可引发蓝屏。但是驱动的编写与普通win32 api是有很大不同的,

为了减轻负担,我直接运用一个现成的程序,是《Microsoft Windows Internals》作者写的Notmyfault

(见附件)。它由Notmyfault.exe和Myfault.sys两部分组成。正如名字一样,引发蓝屏的不是

Notmyfault.exe而是由他加载到内核中的Myfault.sys。如图:
 
   我在这里两种方法都同时用了,先在虚拟机里执行Notmyfault,接着windbg立刻检测到了系统崩溃,

并输出相关信息。 

三 Dump文件的分析

    当按上面的方法运行后,windbg输出了以下内容:
*** Fatal System Error: 0x000000d1
                       (0xE1147008,0x0000001C,0x00000000,0xFBE93403)

  Break instruction exception - code 80000003 (first chance)

  A fatal system error has occurred.
  Debugger entered on first try; Bugcheck callbacks have not been   invoked.

  A fatal system error has occurred.

*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Use !analyze -v to get detailed debugging information.

2.BugCheck D1, {e1147008, 1c, 0, fbe93403}

*** ERROR: Module load completed but symbols could not be loaded for myfault.sys
3.Probably caused by : myfault.sys ( myfault+403 )

Followup: MachineOwner
---------
nt!RtlpBreakWithStatusInstruction:
80527da8 cc              int     3
Kd:>  

上面这一段,有用的信息,如1和2两段,说明的是一个问题,都指明了BugCheck是D1,并给了四个参数

,这里的D1可以在windbg文档的Bug Check Code Reference中查出其具体含义,也可用!analyze –show 

D1命令查出。3说明引起的原因是myfault.sys模块。
接着在kd后输入!analyze –v命令,这个命令是详细列出dump文件的信息。

windbg输出如下:
kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)  //指明Bugcheck D1,我们已看见过了
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.      //解释了错误的原因
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: e1147008, memory referenced
Arg2: 0000001c, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: fbe93403, address which referenced memory
                 //给出了相应的四个参数,第二列是代号,第三列是解释
Debugging Details:
------------------
READ_ADDRESS:  e1147008 Paged pool       //上面的Arg1.

CURRENT_IRQL:  1c       //上面的Arg2

FAULTING_IP:     //指出发生错误时所执行的指令
myfault+403
fbe93403 8b06            mov     eax,dword ptr [esi]

DEFAULT_BUCKET_ID:  DRIVER_FAULT   //指出错误类型,是驱动错误

BUGCHECK_STR:  0xD1   //bugcheck索引,可查windbg文档,也可!analyze –show D1

PROCESS_NAME:  NotMyfault.exe  //错误所属进程

TRAP_FRAME:  f9357b80 --(trap fffffffff9357b80)//错误时各寄存器的内容
ErrCode = 00000000
eax=00000000 ebx=8111f330 ecx=000000d1 edx=0000001c esi=e1147008 edi=00000000
eip=fbe93403 esp=f9357bf4 ebp=f9357c58 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
myfault+0x403:
fbe93403 8b06            mov     eax,dword ptr [esi]  ds:0023:e1147008=????????
Resetting default scope

LAST_CONTROL_TRANSFER:  from 804f880d to 80527da8

STACK_TEXT: //反映了错误前堆栈中函数调用情况,最下面的0x7c801671处函数调用ntdll中的

ZwDeviceIoControlFile,接着调用了ntdll中的KiFastSystemCallRet,再接着调用了nt(这里的nt指

Ntoskrnl)中的KiFastCallEntry,一直到myfault+0x403,发生异常。
f9357734 804f880d 00000003 f9357a90 00000000 nt!RtlpBreakWithStatusInstruction
f9357780 804f93fa 00000003 e1147008 fbe93403 nt!KiBugCheckDebugBreak+0x19
f9357b60 80540853 0000000a e1147008 0000001c nt!KeBugCheck2+0x574
f9357b60 fbe93403 0000000a e1147008 0000001c nt!KiTrap0E+0x233
WARNING: Stack unwind information not available. Following frames may be wrong.
f9357c58 805759d1 ffb5c3b0 8111f318 811d9130 myfault+0x403
f9357d00 8056e33c 00000090 00000000 00000000 nt!IopXxxControlFile+0x5e7
f9357d34 8053d808 00000090 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f9357d34 7c92eb94 00000090 00000000 00000000 nt!KiFastCallEntry+0xf8
0012f9f0 7c92d8ef 7c801671 00000090 00000000 ntdll!KiFastSystemCallRet
0012f9f4 7c801671 00000090 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0012fa54 004018c2 00000090 83360018 00000000 0x7c801671

STACK_COMMAND:  kb

FOLLOWUP_IP: //反汇编了发生错误指令的代码
myfault+403
fbe93403 8b06            mov     eax,dword ptr [esi]

SYMBOL_STACK_INDEX:  4
FOLLOWUP_NAME:  MachineOwner
MODULE_NAME: myfault
IMAGE_NAME:  myfault.sys
DEBUG_FLR_IMAGE_TIMESTAMP:  43774e1d
SYMBOL_NAME:  myfault+403
FAILURE_BUCKET_ID:  0xD1_myfault+403
BUCKET_ID:  0xD1_myfault+403
Followup: MachineOwner
//以上几段看名字就知道了,是以上信息的重复没有多大价值。

四 总结

    通过以上的分析,知道了蓝屏的原因是Bugcheck D1引起的,是由于驱动程序读操作了过高的IRQL引

起的。也知道了这个引发蓝屏的驱动程序是myfault.sys,属于notmyfaulf.exe的进程。还知道了蓝屏前

bug程序myfault.sys的调用情况等多个有用信息,接着就可以在myfault.sys源程序中进行bug修改了。

========

利用windbg分析dump文件  

http://blog.163.com/crazywolf_/blog/static/195231413201061794619624/
这里主要记录利用windbg来分析windows蓝屏
时所产生的内存转储文件*.dmp。
1,下载:
http://www.microsoft.com/whdc/devtools/debugging/default.mspx
2,配置symbol path:
windows程序在编译生成后,会产生一些.exe,dll文件。同时也会用到一些symbol文件,这些文件包含全

局变量,局部变量等信息。在调试不同的系统的时候,用到的symbol是不同的,而且这些文件会很大,

如果下载安装会占用很大的硬盘空间。如果下载,在上面提供的地址也可以下载。微软还提供了一个网

络上的symbol服务器。其网络地址是:http://msdl.microsoft.com/download/symbols,设置symbol时

可以在打开windbg后,file->symbol file path 设置如下:其d:\temp 是本地缓存的目录:

SRV*d:/temp/*http://msdl.microsoft.com/download/symbols。也可以用命令如下设置:
set _NT_SYMBOL_PATH=srv*DownstreamStore*http://msdl.microsoft.com/download/symbols

利用windbg分析dump文件(二)基本调试
1,打开dump文件,在正确设置了symbol路径后,会有如下的显示:
Microsoft (R) Windows Debugger Version 6.5.0003.7
Copyright (c) Microsoft Corporation. All rights reserved.

Loading Dump File [D:\important\document\win系统\debug\Mini121605-01.dmp]
Mini Kernel Dump File: Only registers and stack trace are available
Symbol search path is: SRV*d:/temp/*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows 2000 Kernel Version 2195 (Service Pack 4) UP Free x86 compatible
Kernel base = 0x80400000 PsLoadedModuleList = 0x8046e8f0
Debug session time: Fri Dec 16 13:30:21.203 2005 (GMT+8)
System Uptime: not available
Loading Kernel Symbols
...........................................................................................

.........................
Loading unloaded module list
...................
Loading User Symbols
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
Use !analyze -v to get detailed debugging information.
BugCheck 7F, {8, 0, 0, 0}
c0000005 Exception in ext.Analyze debugger extension.
 PC: 77c16fa3 VA: 01fe8000 R/W: 0 Parameter: 0001003f
2,关于调试窗口:view菜单下面有详细的列表:可以调出对应的窗口,默认的打开窗口是command窗口
3,基本调试命令:
r 可以显示系统崩溃时的寄存器,和最后的命令状态。
dd 显示当前内存地址,dd 参数:显示参数处的内存。
u 可以显示反汇编的指令
!analyze -v 显示分析的详细信息。
kb 显示call stack 内容
kv.bugcheck 可以显示出错的代码
========

 WinDBG 技巧:如何生成Dump 文件(.dump 命令)

http://wingeek.blog.51cto.com/1226974/273964

程序崩溃(crash)的时候, 为了以后能够调试分析问题, 可以使用WinDBG要把当时程序内存空间数据
都保存下来,生成的文件称为dump 文件。 步骤:
1) 打开WinDBG并将之Attach 到crash的程序进程
2) 输入产生dump 文件的命令
WinDBG产生dump 文件的命令是 .dump ,可以选择不同的参数来生成不同类型的dump文件。
选项(1): /m
命令行示例:.dump /m C:\dumps\myapp.dmp
注解: 缺省选项,生成标准的minidump, 转储文件通常较小,便于在网络上通过邮件或其他方式传输

。 这种文件的信息量较少,只包含系统信息、加载的模块(DLL)信息、 进程信息和线程信息。
选项(2): /ma
命令行示例:.dump /ma C:\dumps\myapp.dmp
注解: 带有尽量多选项的minidump(包括完整的内存内容、句柄、未加载的模块,等等),文件很大,

但如果条件允许(本机调试,局域网环境), 推荐使用这中dump。
选项(3):/mFhutwd
命令行示例:.dump /mFhutwd C:\dumps\myapp.dmp
注解:带有数据段、非共享的读/写内存页和其他有用的信息的minidump。包含了通过minidump能够得到

的最多的信息。是一种折中方案。
========

 WINDBG调试DUMP文件

http://blog.csdn.net/vah101/article/details/5916384

    对于windows程序员来说,程序运行时蓝屏是最郁闷的事情,如何找到蓝屏的原因则是首要解决的事

情,好在微软提供了一系列的方法,为我们调试蓝屏提供了便利。

    首先要用的工具是windbg,可以到微软的官方网站下载

    http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.11.1.402.msi

    再需要下载并安装一个符号链接库,微软官方网站也有提供,这个要根据你所调试系统的版本来选



    http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx#d

    也可以不下载这个符号库,直接让windbg自己去网上下符号链接信息。

    工具软件准备好了,就可以开始设置了,首先进入 “我的电脑”->“属性”->“高级”,选择“启

动和故障恢复”选项卡,在“写入调试信息”一栏选择dump文件的转储方式,在“转储文件”中填入

dump文件的保存路径。当出现蓝屏时,系统就会保存现场,将dump时的运行信息保存起来,以便我们用


windbg来分析。有一条要注意,如果选择了完全内存转储,系统将会把内存中的所有信息都存进文件中

,这会相当缓慢,所以你能看到蓝色屏幕上有一个跳动的数字,那就是保存的进度,必须耐心等待它保

存到100%。


      当蓝屏再次发生的时候,我们手上就有了dump文件了,通常在windows目录下的MEMORY.DMP,或者

在windows下的miniDump文件夹中,以*.dmp的形式保存。把.dmp文件拷贝出来,就可以用windbg来调试


      首先,需要设置一下windbg的符号库,进入windbg的"File"->"Symbol File Path",在对话框的

“symbol path”里面输入刚才下载的符号库的安装目录,最省心的方法是在这里填入

SRV*c:/temp*http://msdl.microsoft.com/download/symbols就可以让windbg自动去下载所需要的符号

信息。

      将这里设置完,就可以开始调试.dmp文件了,打开“File”->“Open Crase Dump”,选择一

个.dmp文件,windbg就开始下载符号库并进行初步的分析,当出现

*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Use !analyze -v to get detailed debugging information.

BugCheck 1000007F, {8, f772ffe0, 0, 0}

Probably caused by : bxnd52x.sys ( bxnd52x+365f )

Followup: MachineOwner
---------

就可以在下面的输入框中敲入

!analyze -v;r;kv;lmtn;.logclose;

回车后就可以看到结果,比如我这里看到


BUGCHECK_STR:  0x7f_8

CUSTOMER_CRASH_COUNT:  3

DEFAULT_BUCKET_ID:  DRIVER_FAULT_SERVER_MINIDUMP

CURRENT_IRQL:  2

LAST_CONTROL_TRANSFER:  from 00000000 to f759a65f

STACK_TEXT:  
f78dc000 00000000 00000000 00000000 00000000 bxnd52x+0x365f

STACK_COMMAND:  kb

FOLLOWUP_IP: 
bxnd52x+365f
f759a65f 53              push    ebx

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  bxnd52x+365f

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: bxnd52x

IMAGE_NAME:  bxnd52x.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  44a55446

FAILURE_BUCKET_ID:  0x7f_8_bxnd52x+365f

BUCKET_ID:  0x7f_8_bxnd52x+365f

Followup: MachineOwner
---------

  说明蓝屏可能是由于bxnd52x.sys驱动的问题造成的,上网百度了一下,这个 bxnd52x.sys是网卡驱动

,下载了一个更高版本的驱动重新安装,蓝屏的问题就解决了。  
========

使用Windbg解析dump文件

http://blog.csdn.net/xuleilx/article/details/17622627

第一章 常用的Windbg指令

①!analyze -v 
②kP                                               可以看函数的入参
③!for_each_frame dv /t                            可以看函数中的局部变量
④dc , db                                          产看某一内存中的值    可以直接接变量名 

不过可能需要回溯栈
⑤!threads                                         显示所有线程

⑥~0s , ~1s                                       进入某个线程
⑦!frame ProcessA!FunctionA                        查看某一变量有时需要。 回溯栈  
⑧!uniqstack                                       扩展命令显示当前进程中所有线程的调用堆栈

,除开重复的那些。   
⑨!teb                                             扩展以的格式化后的形式显示线程环境块

(TEB)的信息。 
⑩s-sa 和 s-su                                     命令搜索未指定的 ASCII 和 Unicode 字符串

。这在检查某段内存是否包含可打印字符时有用。
⑪dds、dps 和 dqs 命令显示给定范围内存的内容。     该内存被假定为符号表中的一连串地址。相应

的符号也会被显示出来。命令显示给定范围内存的内容,它们是把内存区域转储出来,并把内存中每个元

素都视为一个符号对其进行解析,dds是四字节视为一个符号,dqs是每8字节视为一个符号,dps是根据

当前处理器架构来选择最合适的长度
⑫.kframes                                        命令设置堆栈回溯显示的默认长度。默认20
⑬k, kb, kd, kp, kP, kv (Display Stack Backtrace) k*命令显示给定线程的调用堆栈,以及其他相

关信息。通常要结合12)使用否则显示出来的东西很少
⑭.reload /i xxx.dll                              忽略.pdb 文件版本不匹配的情况。

第二章 Symbol的设置方法

2.1 将远程的系统函数的PDB文件拷贝到本地「D:\mysymbol」目录下
    SRV*D:\mysymbol*http://msdl.microsoft.com/download/symbols

2.2 加载设置的符号文件
    .reload
    可以使用菜单中的 Debug -> Modules 查看有没有加载进来

第三章 实例


实例1 如何调查堆被破坏问题。

    错误代码:0xc0000374
    错误含义:ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun


第一步、先用「!analyze -v」分析出错误的地方以及由于什么原因导致程序Dump掉的。
       无非是内存溢出,访问非法地址等几种。


0:009> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************
GetPageUrlData failed, server returned HTTP status 404
URL requested: 

http://watson.microsoft.com/StageOne/ProcessA_exe/1_0_0_1/5134aefd/ntdll_dll/6_1_7601_18229

/51fb164a/c0000374/000c4102.htm?Retriage=1

FAULTING_IP: 
ntdll!RtlReportCriticalFailure+62
00000000`777b4102 eb00            jmp     ntdll!RtlReportCriticalFailure+0x64 

(00000000`777b4104)

EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00000000777b4102 (ntdll!RtlReportCriticalFailure+0x0000000000000062)
   ExceptionCode: c0000374
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 000000007782b4b0

PROCESS_NAME:  ProcessA.exe

ERROR_CODE: (NTSTATUS) 0xc0000374 - <Unable to get error code text>

EXCEPTION_CODE: (NTSTATUS) 0xc0000374 - <Unable to get error code text>

EXCEPTION_PARAMETER1:  000000007782b4b0

MOD_LIST: <ANALYSIS/>

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

FAULTING_THREAD:  0000000000002f8c

DEFAULT_BUCKET_ID:  ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun

PRIMARY_PROBLEM_CLASS:  ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun

BUGCHECK_STR:  APPLICATION_FAULT_ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun

LAST_CONTROL_TRANSFER:  from 00000000777b4746 to 00000000777b4102

STACK_TEXT:  
00000000`0548e170 00000000`777b4746 : 00000000`00000002 00000000`00000023 00000000`00000000 

00000000`00000003 : ntdll!RtlReportCriticalFailure+0x62
00000000`0548e240 00000000`777b5952 : 00000000`00000000 00000000`00000000 00000000`00000000 

00000000`1c01001d : ntdll!RtlpReportHeapFailure+0x26
00000000`0548e270 00000000`777b7604 : 00000000`00c50000 00000000`00c50000 00000000`0000000a 

00000000`00000000 : ntdll!RtlpHeapHandleError+0x12
00000000`0548e2a0 00000000`777b79e8 : 00000000`00c50000 00000000`00000000 00000000`00100000 

00000000`00000000 : ntdll!RtlpLogHeapFailure+0xa4
00000000`0548e2d0 00000000`7774fad6 : 00000000`00c50000 00000000`00c59e50 00000000`00c50000 

00000000`00000000 : ntdll!RtlpAnalyzeHeapFailure+0x3a8
00000000`0548e330 00000000`777434d8 : 00000000`00c50000 00000000`00000003 00000000`000006cc 

00000000`000006e0 : ntdll!RtlpAllocateHeap+0x1d2a
00000000`0548e8d0 00000000`777247ea : 00000000`00000003 00000000`00c5ee80 00000000`00c50278 

00000000`000006cc : ntdll!RtlAllocateHeap+0x16c
00000000`0548e9e0 00000000`77723ff2 : 00000000`00c50000 00000000`00000003 00000000`00c5ee90 

00000000`000006cc : ntdll!RtlpReAllocateHeap+0x648
00000000`0548eca0 00000000`750c712f : 00000000`0548fbe8 00000000`00c5ee90 00000000`00000000 

00000000`000005ac : ntdll!RtlReAllocateHeap+0xa2
00000000`0548edb0 00000001`40010f6f : 00000000`00000000 00000000`0548fbe8 00000000`00000000 

00000000`00000661 : msvcr80!realloc+0x6f [f:\dd\vctools\crt_bld\self_64_amd64\crt\src

\realloc.c @ 332]
00000000`0548ede0 00000001`4000f63c : ffffffff`ffffffff 00000000`0548ff10 00000000`00c97fd0 

00000000`0548fe48 : ProcessA!FunctionA_AnalyzeEventData+0xfcf [e:\ProcessA

\FunctionA_sockserv.cpp @ 1666]
00000000`0548f8a0 00000000`774e652d : 00000000`000002a0 00000000`00000000 00000000`00000000 

00000000`00000000 : ProcessA!FunctionA_SockWork+0xe1c [e:\ProcessA\FunctionA_sockserv.cpp @ 

1102]
00000000`0548ff60 00000000`7771c541 : 00000000`00000000 00000000`00000000 00000000`00000000 

00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0548ff90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 

00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

STACK_COMMAND:  !heap ; ~9s; .ecxr ; kb

FOLLOWUP_IP: 
msvcr80!realloc+6f [f:\dd\vctools\crt_bld\self_64_amd64\crt\src\realloc.c @ 332]
00000000`750c712f 4885c0          test    rax,rax

SYMBOL_STACK_INDEX:  9

SYMBOL_NAME:  msvcr80!realloc+6f

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: msvcr80

IMAGE_NAME:  msvcr80.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  4ec3407e

FAILURE_BUCKET_ID:  

ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun_c0000374_msvcr80.dll!realloc

BUCKET_ID:  

X64_APPLICATION_FAULT_ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun_msvcr80!

realloc+6f

WATSON_STAGEONE_URL:  

http://watson.microsoft.com/StageOne/ProcessA_exe/1_0_0_1/5134aefd/ntdll_dll/6_1_7601_18229

/51fb164a/c0000374/000c4102.htm?Retriage=1

Followup: MachineOwner
---------

第二步、使用「!heap」找出出错的堆。分析出错的原因。

       0000000000c59c80
       0000000000c59e50  ←出错的堆地址。
       0000000000c59fd0

大家应该有这样的常识,在使用malloc()或者realloc()分配出来的空间的前面都有
相应的管理情报,用来记录这块分配的内存的大小以及返回的时候用的情报。

从这里很自然的猜想到,在写往0000000000c59c80里面写数据的时候写过了,
写到0000000000c59e50上去了,导致它的管理情报被覆盖了。从而程序dump掉了。

0:009> !heap
**************************************************************
*                                                            *
*                  HEAP ERROR DETECTED                       *
*                                                            *
**************************************************************

Details:


Error address: 0000000000c59e50
Heap handle: 0000000000c50000
Error type heap_failure_buffer_overrun (6)
Parameter 1: 000000000000000a
Last known valid blocks: before - 0000000000c59c80, after -0000000000c59fd0
Stack trace:
                00000000777b79e8: ntdll!RtlpAnalyzeHeapFailure+0x00000000000003a8
                000000007774fad6: ntdll!RtlpAllocateHeap+0x0000000000001d2a
                00000000777434d8: ntdll!RtlAllocateHeap+0x000000000000016c
                00000000777247ea: ntdll!RtlpReAllocateHeap+0x0000000000000648
                0000000077723ff2: ntdll!RtlReAllocateHeap+0x00000000000000a2
                00000000750c712f: msvcr80!realloc+0x000000000000006f
                0000000140010f6f: ProcessA!FunctionA_AnalyzeEventData+0x0000000000000fcf
                000000014000f63c: ProcessA!FunctionA_SockWork+0x0000000000000e1c
                00000000774e652d: kernel32!BaseThreadInitThunk+0x000000000000000d
                000000007771c541: ntdll!RtlUserThreadStart+0x000000000000001d
Index   Address  Name      Debugging options enabled
  1:   001f0000                
  2:   00010000                
  3:   00020000                
  4:   00670000                
  5:   00950000                
  6:   00c50000                
  7:   00910000                
  8:   00bc0000                
  9:   010e0000                
 10:   01220000                
 11:   01420000                
 12:   00c30000                
 13:   03660000                
 14:   00ba0000                
 15:   037b0000                
 16:   01340000                
 17:   039a0000                

第三步、使用「!for_each_frame dv /t」打印出错函数的局部变量,找出元凶。
       从下面的变量里面找到距离0000000000c59c80地址最近的变量,对了就是它:

       char * pData_n = 0x00000000`00c59c90 "SE:Security: ???"

       ※注意如果变量值指针的指针需要先用dc看一下该指针指向的地址。

       之后看代码知道,程序在读取pData_n的数据的时候如果遇到是0a(Windos换行符)就自动在后面

加上
       0d变成0a0d。导致pData_n内存越界了。

0:009> !for_each_frame dv /t
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
12 00000000`0548edb0 00000001`40010f6f msvcr80!realloc+0x6f [f:\dd\vctools\crt_bld

\self_64_amd64\crt\src\realloc.c @ 332]
void * pBlock = 0x00000000`00000000
unsigned int64 newsize = 0x548fbe8
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
13 00000000`0548ede0 00000001`4000f63c ProcessA!FunctionA_AnalyzeEventData+0xfcf [e:

\ProcessA\FunctionA_sockserv.cpp @ 1666]
void * cd = 0xffffffff`ffffffff
struct _MpEvsHead * Head = 0x00000000`0548ff10
char * pEventData = 0x00000000`00c97fd0 "???"
char ** pNewData = 0x00000000`0548fe48
char * SiteName = 0x00000000`0548fe18 ""
int oval_check = 0n0
char * pszHostIp = 0x00000000`0548fbf0 "192.168.1.1"
int j = 0n469
int NodeName_check = 0n0
char [2068] eventtext = char [2068] "SE:Security: ???"
unsigned long err = 0
int NL_henkan = 0n1
int Evttxt_check = 0n1
char [129] nameWork = char [129] "`_???"
int ret = 0n0
struct NameObject_t * pNameObj_n = 0x00000000`00c5eee8
char * pData_n = 0x00000000`00c59c90 "SE:Security: ???"
long lWork = 0n9
char [257] szTrcBuff = char [257] "safely divided text.([453]bytes --> [469]bytes)"
long nNameNum = 0n44
long nNewLen = 0n1740
struct NameObject_t * pNameObj_o = 0x00000000`00c98028
char * pData_o = 0x00000000`00c984c6 "SE:Security: ???"
char * pt = 0x00000000`00c59e55 "[???"
long i = 0n20
int IpAddr_check = 0n0
int res = 0n1
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
14 00000000`0548f8a0 00000000`774e652d ProcessA!FunctionA_SockWork+0xe1c [e:\ProcessA

\FunctionA_sockserv.cpp @ 1102]
void * ns = 0x00000000`000002a0
char * pRead_str = 0x00000000`00c562f0 ","
int bTableRegisterd = 0n0
unsigned long err = 0
char [3] traceflg = char [3] ""
int ret = 0n0
short sWork = 0n2
int oval_check = 0n0
char * pNewData = 0x00000000`00c5ee90 "???"
char * wk = 0x00000000`0548f930 "192.168.1.1"
char [33] SiteName = char [33] ""
long lWork = 0n2032
char [257] szTrcBuff = char [257] "recv event OK"
int iLastSerchedIndex = 0n0
char [256] HostIp = char [256] "192.168.1.1"
int ret2 = 0n0
struct _MpEvsHead Head = struct _MpEvsHead
long nDataLen = 0n3
char [257] szTrcBuff2 = char [257] ""
char [20] szSendData = char [20] "OK"
struct addrinfo hinst = struct addrinfo
int conv_disc_set = 0n1
long lRc = 0n0
void * conv_disc = 0xffffffff`ffffffff
int res = 0n1
char * pData = 0x00000000`00c97fd0 "???"
long nRead = 0n3726
char [16] evttype = char [16] "Alarm.sys"
char * lpszEventid = 0x00000000`00c5f180 ""
long nSend = 0n12
char [256] ipTmp = char [256] "192.168.1.1"
char [20] szToCode = char [20] "sjis"
char [20] szFromCode = char [20] "sjis"
int bWriteEvent = 0n1
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
实例2  无效参数(STATUS_INVALID_PARAMETER)。

    错误代码:0xc000000d
    错误含义:STATUS_INVALID_PARAMETER

第一步、先用「!analyze -v」分析出错误的地方以及由于什么原因导致程序Dump掉的。
 
0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for user32.dll - 
Unable to load image C:\Windows\Odsv.dll, Win32 error 0n2
*** WARNING: Unable to verify timestamp for Odsv.dll
*** ERROR: Module load completed but symbols could not be loaded for Odsv.dll
GetPageUrlData failed, server returned HTTP status 404
URL requested: 

http://watson.microsoft.com/StageOne/ProcessB_exe/1_0_0_1/4e362265/msvcr80_dll/8_0_50727_61

95/4dcdd833/c000000d/0001d5fa.htm?Retriage=1

FAULTING_IP: 
msvcr80!strncpy_s+10a [f:\dd\vctools\crt_bld\self_64_amd64\crt\src\tcsncpy_s.inl @ 62]
00000000`74e6d5fa b822000000      mov     eax,22h

EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 0000000074e6d5fa (msvcr80!strncpy_s+0x000000000000010a)
   ExceptionCode: c000000d
  ExceptionFlags: 00000000
NumberParameters: 0

PROCESS_NAME:  ProcessB.exe

ERROR_CODE: (NTSTATUS) 0xc000000d - <Unable to get error code text>

EXCEPTION_CODE: (NTSTATUS) 0xc000000d - <Unable to get error code text>

MOD_LIST: <ANALYSIS/>

NTGLOBALFLAG:  0


APPLICATION_VERIFIER_FLAGS:  0

LAST_CONTROL_TRANSFER:  from 0000000000124250 to 0000000074e5b0ec

FAULTING_THREAD:  ffffffffffffffff

DEFAULT_BUCKET_ID:  STATUS_INVALID_PARAMETER

PRIMARY_PROBLEM_CLASS:  STATUS_INVALID_PARAMETER

BUGCHECK_STR:  APPLICATION_FAULT_STATUS_INVALID_PARAMETER

IP_ON_STACK: 
+2e32faf01dedf58
00000000`00124250 60              ???

FRAME_ONE_INVALID: 1

STACK_TEXT:  
00000000`00124220 00000000`00124250 : 00000000`00000006 00000000`00000000 00000000`00000001 

00000000`00000000 : msvcr80!_invalid_parameter+0x6c [f:\dd\vctools\crt_bld

\self_64_amd64\crt\src\invarg.c @ 88]
00000000`00124228 00000000`00000006 : 00000000`00000000 00000000`00000001 00000000`00000000 

00000000`00000000 : 0x124250
00000000`00124230 00000000`00000000 : 00000000`00000001 00000000`00000000 00000000`00000000 

00000000`00124260 : 0x6

STACK_COMMAND:  ~0s; .ecxr ; kb


FOLLOWUP_IP: 

msvcr80!strncpy_s+10a [f:\dd\vctools\crt_bld\self_64_amd64\crt\src\tcsncpy_s.inl @ 62]
00000000`74e6d5fa b822000000      mov     eax,22h

FAULTING_SOURCE_CODE:  
No source found for 'f:\dd\vctools\crt_bld\self_64_amd64\crt\src\tcsncpy_s.inl'

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  msvcr80!strncpy_s+10a

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: msvcr80

IMAGE_NAME:  msvcr80.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  4dcdd833

FAILURE_BUCKET_ID:  STATUS_INVALID_PARAMETER_c000000d_msvcr80.dll!strncpy_s

BUCKET_ID:  X64_APPLICATION_FAULT_STATUS_INVALID_PARAMETER_msvcr80!strncpy_s+10a

WATSON_STAGEONE_URL:  

http://watson.microsoft.com/StageOne/ProcessB_exe/1_0_0_1/4e362265/msvcr80_dll/8_0_50727_61

95/4dcdd833/c000000d/0001d5fa.htm?Retriage=1

Followup: MachineOwner
---------
这次运气很不好,从「!analyze -v」打出来的结果来看看不出啥东西来,只知道
在调用strncpy_s的时候dmp掉了,无法定位具体是哪个函数出错的原因很多,有可能
客户采集的不是全dmp文件或者dmp文件中的栈被破坏了。   

这的确很伤脑筋,就针对这个我可是花了3个星期一行行的解析栈里面的内容 才解决的。

第二步、先用「!teb」看一下这个程序的栈是从哪里到哪里的。

0:000>!teb
TEB at 000007ffffeee000
    ExceptionList:        0000000000000000
    StackBase:            0000000008d50000
    StackLimit:           0000000008d4d000
    SubSystemTib:         0000000000000000
    FiberData:            0000000000001e00
    ArbitraryUserPointer: 0000000000000000
    Self:                 000007ffffeee000
    EnvironmentPointer:   0000000000000000
    ClientId:             0000000000001bdc . 0000000000001868
    RpcHandle:            0000000000000000
    Tls Storage:          000007ffffeee058
    PEB Address:          000007fffffd6000
    LastErrorValue:       87
    LastStatusValue:      c000000d
    Count Owned Locks:    0
    HardErrorMode:        0

第三步、先用「dps」看一下这个程序的栈中的内存的内容。 下面截取其中比较重要的一段。

-------------------------------------------------------------------------------------------

------------------------------------
00000000`001247d8  00000000`74e6d5fa msvcr80!strncpy_s+0x10a [f:\dd\vctools\crt_bld

\self_64_amd64\crt\src\tcsncpy_s.inl @ 62]
00000000`001247e0  00000000`009c01e0
00000000`001247e8  00000000`030f5810
00000000`001247f0  00000000`0057e310 ProcessB2!work   
★「ProcessB2!work」的内容本应该是像这样的数据「DNxxxxxxxx_150_109」
但是现在「ProcessB2!work」中的内容却是「VIP_rtcrx00184-004a/b-y3b-d」这个。

00000000`001247f8  00000000`005782c0 ProcessB2!trcData 
▲「ProcessB2!trcData」的内容是「Function:testB call」。
 函数List::testB の trace("testB", __FILE__, __LINE__, TRCLV_3);

00000000`00124800  00000000`00000000
00000000`00124808  00000000`00000000
00000000`00124810  00000000`004a3150 ProcessB2!`string' 
▲「 ProcessB2!`string'」的内容是「e:\ProcessB\FunctionB.cpp  __FILE__」。

00000000`00124818  00000000`00455b65 ProcessB2!List::testB+0x55 [e:\ProcessB\Listset.cpp @ 

719]
00000000`00124820  00000000`009c01e0
00000000`00124828  00000000`030f5810
00000000`00124830  00000000`0057e310 ProcessB2!work
00000000`00124838  00000000`001249e0
00000000`00124840  32322e35`322e3000
00000000`00124848  30614031`33312e34
00000000`00124850  7097fb8e`bc923730
00000000`00124858  5049565f`5753334c
00000000`00124860  00000000`0000125f
00000000`00124868  000082bd`b1200d5e
00000000`00124870  00000000`009c01e0
00000000`00124878  00000000`00467bda ProcessB2!FunctionB+0x73a [e:\ProcessB\FunctionB.cpp @ 

181]   
-------------------------------------------------------------------------------------------

------------------------------------
这里终于定位到是哪个函数出问题。搞清楚这些函数的功能,然后打印出所有可能打印的内容,发现
函数传递了一个不合法的数据。在这里要说一下为啥传的数据不合法就会Dmp掉。

首先strncpy 这个函数在使用的时候只要有个宏定义(默认是有的)在编译的时候就会使用strncpy_s这个
安全函数。
详情可以参考下面微软的说明文档。
http://msdn.microsoft.com/zh-cn/LIBRARY/ms175759(v=vs.80)

其次说明一下为什么会dmp掉。strncpy在使用的时候如果转化成strncpy_s的时候是这样一种形式。
char dst[5];
strncpy(dst, "a long string", 5);    ---->  strncpy_s(dst, 5, "a long string", 5);

而这样就会到时报STATUS_INVALID_PARAMETER这个错误这是strncpy_s的特性。具体使用方法可以参考下
面的文档。
http://msdn.microsoft.com/zh-cn/library/5dae5d43(v=vs.90).aspx

节选:
char dst[5];
strncpy_s(dst, 5, "a long string", 5);
means that we are asking strncpy_s to copy five characters into a buffer five bytes long; 

this would leave no space for the null terminator, hence strncpy_s zeroes out the string 

and calls the invalid parameter handler.
If truncation behavior is needed, use _TRUNCATE or (size – 1):
strncpy_s(dst, 5, "a long string", _TRUNCATE);
strncpy_s(dst, 5, "a long string", 4);

详细的ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun方法还可以参考以下的例子:
http://blogs.msdn.com/b/jiangyue/archive/2010/03/16/windows-heap-overrun-monitoring.aspx
========

 windbg分析dump文件

http://blog.csdn.net/xiaoshahai/article/details/7284867/
前言:WinDbg是微软开发的免费源代码级的调试工具。WinDbg可以用于Kernel模式调试和用户模式调试

,还可以调试Dump文件。本文的讨论是在安装了Debugging Tools for Windows 的前提下进行的,下载

地址可以参考我之前的文章。WinDbg对于dump文件的调试可以通过菜单设置Symbol File Path、Source 

File Path ,并可设置多个路径。

1、  打开Dump格式文件


打开WinDbg,通过菜单[File] à [Open Crash dump] 选择dump文件打开,也可通过CMD打开Dos命令窗口

,切换到WinDbg所在目录,利用命令:

WinDbg –z “D:/Lines2009-7-25-22-20-33-900.dmp”

-z表示路径

clip_image001

图1.1 利用WinDbg打开dump文件

本文编写了一个简单能产生除数为0异常的程序,让其运行,产生崩溃,通过drwtsn产生dmp文件,然后

通过windbg分析dmp文件,定位程序bug。

目的:学习windbg基本功能使用。

程序源代码:

void Crash(void){         int i = 1;         int j = 0;         i /= j;}void main(void){         Crash();}

编译环境:vc++6.0

编译器设置:

 这一步设置,要求对release版本不使用优化,如果使用优化,上面源代码中Crash(void)函数将不被汇
编。

这一步设置,产生release版本的调试符号表,为后续定位错误准备。

步骤:

1、 安装drwtsn32

用户可以通过drwtsn32命令,查看dmp文件会被保存在何处。

2、 安装windbg,Windbg下载地址:

http://www.microsoft.com/whdc/devtools/debugging/default.mspx

3、 设置windbg

A、符号表路径设置

其中;srv*d:/symbolslocal*http://msdl.microsoft.com/download/symbols设置的目的是下载该程序用

到的操作系统相关的库函数的符号表到本地。

B、源代码路径设置

C、dmp文件导入

载入dump文件显示如图:

clip_image002

图1.2 WinDbg界面

2、  分析dump文件


若生成的dump文件在本机,dump文件中将包含调试需要的PDB文件及源代码路径,若不在本机,可以通过

WinDbg菜单[File] à [Symbol File path] 及 [Source File Path] 分别设置PDB文件路径和源代码路径

。如果程序涉及到DLL,需要将EXE、DLL所有涉及的PDB、源代码路径都包括。使用命令:

!analyze –v

将分析dump文件,并显示程序崩溃处于的代码行:

clip_image003

图1.3 分析dump 文件
========

 调试技巧 —— 如何利用windbg + dump + map分析程序异常

http://blog.csdn.net/xiaoshahai/article/details/7285103
之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估

计你会疯掉~~

所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VC6.0,

下面是自己整理的一份自动生成DUMP文件的源代码,只需要添加到工程即可,源代码如下:

MiniDump.h

[cpp] view plaincopy 
#include <windows.h>  
#include <tlhelp32.h>  
  
//#include "dbghelp.h"  
//#define DEBUG_DPRINTF     1   //allow d()  
//#include "wfun.h"  
  
#pragma optimize("y", off)      //generate stack frame pointers for all functions - same as 

/Oy- in the project  
#pragma warning(disable: 4200)  //nonstandard extension used : zero-sized array in 

struct/union  
#pragma warning(disable: 4100)  //unreferenced formal parameter  
  
/*BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & 

Module_Addr); 
int  WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str); 
int  WINAPI Get_Version_Str(PCHAR Str); 
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException); 
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);*/  
  
// In case you don't have dbghelp.h.  
#ifndef _DBGHELP_  
  
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {  
    DWORD   ThreadId;  
    PEXCEPTION_POINTERS ExceptionPointers;  
    BOOL    ClientPointers;  
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;  
  
typedef enum _MINIDUMP_TYPE {  
    MiniDumpNormal =            0x00000000,  
        MiniDumpWithDataSegs =      0x00000001,  
} MINIDUMP_TYPE;  
  
typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(  
                                            IN HANDLE           hProcess,  
                                            IN DWORD            ProcessId,  
                                            IN HANDLE           hFile,  
                                            IN MINIDUMP_TYPE    DumpType,  
                                            IN CONST PMINIDUMP_EXCEPTION_INFORMATION    

ExceptionParam, OPTIONAL  
                                            IN PVOID                                    

UserStreamParam, OPTIONAL  
                                            IN PVOID                                    

CallbackParam OPTIONAL  
                                            );  
  
#else  
  
typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(  
                                            IN HANDLE           hProcess,  
                                            IN DWORD            ProcessId,  
                                            IN HANDLE           hFile,  
                                            IN MINIDUMP_TYPE    DumpType,  
                                            IN CONST PMINIDUMP_EXCEPTION_INFORMATION    

ExceptionParam, OPTIONAL  
                                            IN PMINIDUMP_USER_STREAM_INFORMATION        

UserStreamParam, OPTIONAL  
                                            IN PMINIDUMP_CALLBACK_INFORMATION           

CallbackParam OPTIONAL  
                                            );  
#endif //#ifndef _DBGHELP_  
  
// Tool Help functions.  
typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);  
typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);  
typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);  
  
  extern void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL 

Show_Flag);  
  
extern HMODULE  hDbgHelp;  
extern MINIDUMP_WRITE_DUMP  MiniDumpWriteDump_;  
  
extern CREATE_TOOL_HELP32_SNAPSHOT  CreateToolhelp32Snapshot_;  
extern MODULE32_FIRST   Module32First_;  
extern MODULE32_NEST    Module32Next_;  

MiniDump.cpp
/* 
    Author: Vladimir Sedach. 
 
    Purpose: demo of Call Stack creation by our own means, 
    and with MiniDumpWriteDump() function of DbgHelp.dll. 
*/  
  
#include "StdAfx.h"  
#include "MiniDump.h"  
#include <Shlwapi.h>  
  
#pragma comment(lib,"shlwapi.lib")  
  
HMODULE hDbgHelp;  
MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;  
  
CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;  
MODULE32_FIRST  Module32First_;  
MODULE32_NEST   Module32Next_;  
  
#define DUMP_SIZE_MAX   8000    //max size of our dump  
#define CALL_TRACE_MAX  ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40))  //max number of traced 

calls  
#define NL              "\r\n"  //new line  
  
extern CString GetExePath();  
  
//****************************************************************************************  
BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr)  
//****************************************************************************************  
// Find module by Ret_Addr (address in the module).  
// Return Module_Name (full path) and Module_Addr (start address).  
// Return TRUE if found.  
{  
    MODULEENTRY32   M = {sizeof(M)};  
    HANDLE  hSnapshot;  
  
    Module_Name[0] = 0;  
      
    if (CreateToolhelp32Snapshot_)  
    {  
        hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);  
          
        if ((hSnapshot != INVALID_HANDLE_VALUE) &&  
            Module32First_(hSnapshot, &M))  
        {  
            do  
            {  
                if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)  
                {  
                    lstrcpyn(Module_Name, M.szExePath, MAX_PATH);  
                    Module_Addr = M.modBaseAddr;  
                    break;  
                }  
            } while (Module32Next_(hSnapshot, &M));  
        }  
  
        CloseHandle(hSnapshot);  
    }  
  
    return !!Module_Name[0];  
} //Get_Module_By_Ret_Addr  
  
//******************************************************************  
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str)  
//******************************************************************  
// Fill Str with call stack info.  
// pException can be either GetExceptionInformation() or NULL.  
// If pException = NULL - get current call stack.  
{  
    CHAR    Module_Name[MAX_PATH];  
    PBYTE   Module_Addr = 0;  
    PBYTE   Module_Addr_1;  
    int     Str_Len;  
      
    typedef struct STACK  
    {  
        STACK * Ebp;  
        PBYTE   Ret_Addr;  
        DWORD   Param[0];  
    } STACK, * PSTACK;  
  
    STACK   Stack = {0, 0};  
    PSTACK  Ebp;  
  
    if (pException)     //fake frame for exception address  
    {  
        Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;  
        Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;  
        Ebp = &Stack;  
    }  
    else  
    {  
        Ebp = (PSTACK)&pException - 1;  //frame addr of Get_Call_Stack()  
  
        // Skip frame of Get_Call_Stack().  
        if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))  
            Ebp = Ebp->Ebp;      //caller ebp  
    }  
  
    Str[0] = 0;  
    Str_Len = 0;  
  
    // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.  
    // Break trace on wrong stack frame.  
    for (int Ret_Addr_I = 0;  
        (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !


IsBadCodePtr(FARPROC(Ebp->Ret_Addr));  
        Ret_Addr_I++, Ebp = Ebp->Ebp)  
    {  
        // If module with Ebp->Ret_Addr found.  
        if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr_1))  
        {  
            if (Module_Addr_1 != Module_Addr)   //new module  
            {  
                // Save module's address and full path.  
                Module_Addr = Module_Addr_1;  
                Str_Len += wsprintf(Str + Str_Len, NL "%08X  %s", Module_Addr, 

Module_Name);  
            }  
  
            // Save call offset.  
            Str_Len += wsprintf(Str + Str_Len,  
                NL "  +%08X", Ebp->Ret_Addr - Module_Addr);  
  
            // Save 5 params of the call. We don't know the real number of params.  
            if (pException && !Ret_Addr_I)  //fake frame for exception address  
                Str_Len += wsprintf(Str + Str_Len, "  Exception Offset");  
            else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))  
            {  
                Str_Len += wsprintf(Str + Str_Len, "  (%X, %X, %X, %X, %X)",  
                    Ebp->Param[0], Ebp->Param[1], Ebp->Param[2], Ebp->Param[3], Ebp->Param
[4]);  
            }  
        }  
        else  
            Str_Len += wsprintf(Str + Str_Len, NL "%08X", Ebp->Ret_Addr);  
    }  
  
    return Str_Len;  
} //Get_Call_Stack  
  
//***********************************  
int WINAPI Get_Version_Str(PCHAR Str)  
//***********************************  
// Fill Str with Windows version.  
{  
    OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)};  //EX for NT 5.0 and later  
  
    if (!GetVersionEx((POSVERSIONINFO)&V))  
    {  
        ZeroMemory(&V, sizeof(V));  
        V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);  
        GetVersionEx((POSVERSIONINFO)&V);  
    }  
  
    if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)  
        V.dwBuildNumber = LOWORD(V.dwBuildNumber);  //for 9x HIWORD(dwBuildNumber) = 0x04xx 
  
    return wsprintf(Str,  
        NL "Windows:  %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product 

Type - VER_NT_WORKSTATION,...  
        V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, 


V.wServicePackMinor/*, V.wProductType*/);  
} //Get_Version_Str  
  
//*************************************************************  
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)  
//*************************************************************  
// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call 

stack in Str.  
{  
    PCHAR       Str;  
    int         Str_Len;  
    int         i;  
    CHAR        Module_Name[MAX_PATH];  
    PBYTE       Module_Addr;  
    HANDLE      hFile;  
    FILETIME    Last_Write_Time;  
    FILETIME    Local_File_Time;  
    SYSTEMTIME  T;  
      
    Str = new CHAR[DUMP_SIZE_MAX];  
  
    if (!Str)  
        return NULL;  
  
    Str_Len = 0;  
    Str_Len += Get_Version_Str(Str + Str_Len);  
  
    Str_Len += wsprintf(Str + Str_Len, NL "Process:  ");  
    GetModuleFileName(NULL, Str + Str_Len, MAX_PATH);  
    Str_Len = lstrlen(Str);  
  
    // If exception occurred.  
    if (pException)  
    {  
        EXCEPTION_RECORD &  E = *pException->ExceptionRecord;  
        CONTEXT &           C = *pException->ContextRecord;  
  
        // If module with E.ExceptionAddress found - save its path and date.  
        if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))  
        {  
            Str_Len += wsprintf(Str + Str_Len,  
                NL "Module:  %s", Module_Name);  
  
            if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, 
OPEN_EXISTING,  
                FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)  
            {  
                if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))  
                {  
                    FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);  
                    FileTimeToSystemTime(&Local_File_Time, &T);  
  
                    Str_Len += wsprintf(Str + Str_Len,  
                        NL "Date Modified:  %02d/%02d/%d",  
                        T.wMonth, T.wDay, T.wYear);  
                }  
                CloseHandle(hFile);  
            }  
        }  
        else  
        {  
            Str_Len += wsprintf(Str + Str_Len,  
                NL "Exception Addr:  %08X", E.ExceptionAddress);  
        }  
          
        Str_Len += wsprintf(Str + Str_Len,  
            NL "Exception Code:  %08X", E.ExceptionCode);  
          
        if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)  
        {  
            // Access violation type - Write/Read.  
            Str_Len += wsprintf(Str + Str_Len,  
                NL "%s Address:  %08X",  
                (E.ExceptionInformation[0]) ? "Write" : "Read", E.ExceptionInformation[1]);  
        }  
  
        // Save instruction that caused exception.  
        Str_Len += wsprintf(Str + Str_Len, NL "Instruction: ");  
        for (i = 0; i < 16; i++)  
            Str_Len += wsprintf(Str + Str_Len, " %02X", PBYTE(E.ExceptionAddress)[i]);  
  
        // Save registers at exception.  
        Str_Len += wsprintf(Str + Str_Len, NL "Registers:");  
        Str_Len += wsprintf(Str + Str_Len, NL "EAX: %08X  EBX: %08X  ECX: %08X  EDX: %08X", 
C.Eax, C.Ebx, C.Ecx, C.Edx);  
        Str_Len += wsprintf(Str + Str_Len, NL "ESI: %08X  EDI: %08X  ESP: %08X  EBP: %08X", 
C.Esi, C.Edi, C.Esp, C.Ebp);  
        Str_Len += wsprintf(Str + Str_Len, NL "EIP: %08X  EFlags: %08X", C.Eip, C.EFlags);  
    } //if (pException)  
      
    // Save call stack info.  
    Str_Len += wsprintf(Str + Str_Len, NL "Call Stack:");  
    Get_Call_Stack(pException, Str + Str_Len);  
  
    if (Str[0] == NL[0])  
        lstrcpy(Str, Str + sizeof(NL) - 1);  
  
    return Str;  
} //Get_Exception_Info  
  
//*************************************************************************************  
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag)  
//*************************************************************************************  
// Create dump.   
// pException can be either GetExceptionInformation() or NULL.  
// If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current 

process.  
// If Show_Flag = TRUE - show message with Get_Exception_Info() dump.  
{  
    HANDLE  hDump_File;  
    PCHAR   Str;  
    DWORD   Bytes;  
    DWORD   nLen = 0;  
  
    CString strDir,strTXTFile,strDMPFile;  
    CString strDate,strTotal;  
    CTime   tm = CTime::GetCurrentTime();  
      
    strDir.Format(_T("%s\\Log"),GetExePath());  
    strTXTFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.txt"),GetExePath

(),tm.GetYear(),tm.GetMonth(),  
        tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());  
    strDMPFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.dmp"),GetExePath

(),tm.GetYear(),tm.GetMonth(),  
        tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());  
  
    if(!PathFileExists(strDir))  
        CreateDirectory(strDir,NULL);  
  
    Str = Get_Exception_Info(pException);  
  
    //if (Show_Flag && Str)  
    //  MessageBox(NULL, Str, "MiniDump", MB_ICONHAND | MB_OK);  
  
    if (File_Flag)  
    {  
        if (Str)  
        {  
            hDump_File = CreateFile(strTXTFile,  
                GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
              
            nLen = lstrlen(Str);  
            Str[nLen] = '\0';  
  
            WriteFile(hDump_File, Str, lstrlen(Str) + 1, &Bytes, NULL);  
  
            CloseHandle(hDump_File);  
        }  
  
        // If MiniDumpWriteDump() of DbgHelp.dll available.  
        if (MiniDumpWriteDump_)  
        {  
            MINIDUMP_EXCEPTION_INFORMATION  M;  
  
            M.ThreadId = GetCurrentThreadId();  
            M.ExceptionPointers = pException;  
            M.ClientPointers = 0;  
  
            hDump_File = CreateFile(strDMPFile,  
                GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
  
            MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File,  
                MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL);  
  
            CloseHandle(hDump_File);  
        }  
    } //if (File_Flag)  
  
    delete Str;  
} //Create_Dump  

具体参考方法如下:
1、在CXXDlg::OnInitDialog()中添加这样一段:

SetUnhandledExceptionFilter(CrashReportEx);  
HMODULE hKernel32;  
  
// Try to get MiniDumpWriteDump() address.  
hDbgHelp = LoadLibrary("DBGHELP.DLL");  
MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");  
//  d("hDbgHelp=%X, MiniDumpWriteDump_=%X", hDbgHelp, MiniDumpWriteDump_);  
  
// Try to get Tool Help library functions.  
hKernel32 = GetModuleHandle("KERNEL32");  
CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, 

"CreateToolhelp32Snapshot");  
Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32First");  
Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32Next");  
测试代码如下:

class CTestDlg : public CDialog  
{  
// Construction  
public:  
    CTestDlg(CWnd* pParent = NULL); // standard constructor  
  
    void Fun1(char *pszBuffer);  
    void Fun2(char *pszBuffer);  
    void Fun3(char *pszBuffer);  
};  


void CTestDlg::Fun1(char *pszBuffer)  
{  
    Fun2(pszBuffer);  
}  
  
void CTestDlg::Fun2(char *pszBuffer)  
{  
    Fun3(pszBuffer);  
}  
  
void CTestDlg::Fun3(char *pszBuffer)  
{  
    pszBuffer[1] = 0x00;  
}  

我们在双击确定按钮时的响应代码如下:

void CTestDlg::OnOK()   
{  
    // TODO: Add extra validation here  
    Fun1(NULL);  
}  

2、设置VC编译选项,勾选生成MAP和Debug Info:


3、将编译生成的Release目录中的pdb、map文件保存起来,以后调试会用到:


4、运行程序,单击确定按钮出现异常后自动重启,并创建一个Log文件夹,里面生成dump文件:

5、我们打开WinDbg,设置一下pdb路径(File \ Symbol File Path):

6、用WiinDbg打开dump文件(File \ Open Crash Dump)

7、输入命令!analyze -v,等待几秒后会打印出错误信息,函数调用栈如下图:

OK ,这样我们就能在发布版本的程序中,准确的定位到哪个函数出了问题,所以发布程序时,一定要记

得生成pdb、map文件,不然客户运行出错的话,你不死也残!

测试工程下载地址:

http://download.csdn.NET/source/3575167
========
0 0