如何取樣 memory dump (1)
August 28th, 2009
在之前的文章裡,我們寫了一支會掛掉的 HelloCrash 程式。當然,若是在開發這支程式的機器上跑,我們直接把 debugger 連上去看就可以了。不過呢,理想是美麗的,現實是殘酷的,客人的機器上通常沒裝 VC++ .。我們己經秀過怎麼樣從 Windows 事件記錄中把有用的資料挖出來,不過呢,通常這些資料是不夠的,比方說當我們需要知道 register 的內容時。遠端除錯在某些時候是可行的,不過大部份的狀況下,光是搞定防火牆就是個大問題,更別提客人是不是有足夠的耐心讓你玩他的機器數小時來找問題。因此在這樣的情況下,我們得想想別的辦法,通常會直接採樣 memory dump,然後帶回家分析。
微軟在這篇文章中詳細地解釋了如何使用 ADPlus 來採樣 memory dump。ADPlus 包含於 Debugging Tools for Windows 套件中。接下來就讓我們照微軟文章中的步驟來抓抓看 HelloCrash 的問題在哪裡。首先裝好 Debugging Tools for Windows,讓 Hello Crash 掛掉,打開一個有管理員權限的命令列提示,輸入以下的內容:
cd <Debugging Tools for Windows folder>
tlist /p HelloCrash.exe
adplus -crash -p <PID acquired from tlist /p>
在 Debugging Tools for Windows 的資料夾中會跑出一個新的目錄,裡頭有 memory dump。接下來我們打開 WinDBG,選 File->Open Crash Dump 把這個 memory dump 打開。底下是最常用的基本分析方法:
0:000> !analyze -v
*********************************************
*
* Exception Analysis
*
*********************************************FAULTING_IP:
+0
00000000 ?? ???EXCEPTION_RECORD: ffffffff — (.exr 0xffffffffffffffff)
ExceptionAddress: 00000000
ExceptionCode: 80000003 (Break instruction exception)
ExceptionFlags: 00000000
NumberParameters: 0FAULTING_THREAD: 000009c8
DEFAULT_BUCKET_ID: STATUS_BREAKPOINT
PROCESS_NAME: HelloCrash.exe
ERROR_CODE: (NTSTATUS) 0x80000003 – {EXCEPTION} Breakpoint A breakpoint has been reached.
NTGLOBALFLAG: 400
APPLICATION_VERIFIER_FLAGS: 0
LAST_CONTROL_TRANSFER: from 77519e89 to 774f075a
STACK_TEXT:
0035fcf0 77519e89 ffffffff c0000005 0035fd40 ntdll!ZwTerminateProcess+0x12
0035fd00 7754d058 c0000005 7c4c2188 00000000 ntdll!RtlExitUserProcess+0x7a
0035fd40 7754d1ff 001712c8 7efde000 00000000 ntdll!__RtlUserThreadStart+0x84
0035fd58 00000000 001712c8 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1bSTACK_COMMAND: ~0s; .ecxr ; kb
FOLLOWUP_IP:
ntdll!ZwTerminateProcess+12
774f075a c20800 ret 8SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: ntdll!ZwTerminateProcess+12
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: ntdll
IMAGE_NAME: ntdll.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 4791a783
PRIMARY_PROBLEM_CLASS: STATUS_BREAKPOINT
BUGCHECK_STR: APPLICATION_FAULT_STATUS_BREAKPOINT
FAILURE_BUCKET_ID: APPLICATION_FAULT_STATUS_BREAKPOINT_ntdll!ZwTerminateProcess+12
BUCKET_ID: APPLICATION_FAULT_STATUS_BREAKPOINT_ntdll!ZwTerminateProcess+12
Followup: MachineOwner
———
等等,這結果看起來不太對勁,為什麼自動分析的結果跑出來說是中斷點 (breakpoint)? 另外那個 stack 也不對,這是怎麼回事呢? 這都是因為 VC++ 使用的 secure CRT (安全的 C 執行程式庫) 已經自行做好錯誤處理,並呼叫了 TerminateProcess() 函數。在這種狀況下,我們必須修改使用 ADPlus 的方式:
adplus -crash -bp kernel32!TerminateProcess -sc <full path of HelloCrash.exe>
在 CRT 呼叫 TerminateProcess() 之前就中斷它,並採取 memory dump。這個方法得到的 memory dump 經過 !analyze -v 的分析就有用多了。