Windbg dump分析 學習總結
阿新 • • 發佈:2019-02-17
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檔案的強迫產生
生一個。一般有以下兩個辦法。
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檔案的分析
*** 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
//以上幾段看名字就知道了,是以上資訊的重複沒有多大價值。
四 總結
起的。也知道了這個引發藍屏的驅動程式是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: 0000000000c59e50Heap 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: 0LAST_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