1. 程式人生 > >Windbg dump分析 學習總結

Windbg dump分析 學習總結

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, "M