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 -
EXCEPTION_CODE: (NTSTATUS) 0xc0000374 -
EXCEPTION_PARAMETER1: 000000007782b4b0
MOD_LIST:
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 -
EXCEPTION_CODE: (NTSTATUS) 0xc000000d -
MOD_LIST:
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
#include
//#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
#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
========