Windbg產生dump檔案的方法:程式崩潰(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能夠得到的最多的資訊。是一種折中方案
那怎麼自動產生dump檔案呢,比如對方的電腦沒有windbg,這裡用到一個window系統內建工具,Dr.Watson
運行方式很簡單:
直接run-輸入drwtsn32 -i就可以了,會提示這樣的:
這個命令真難記,實話,記華生醫生吧,福爾摩斯中的
如果有程式崩潰,會自動產生dump,這時再輸入drwtsn32就會運行這個程式:
找到對應路徑的DMP檔案就行了,一般放在如下路徑:
C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson
以下執行個體來自AWD
代碼:
/*++
- Copyright (c) Advanced Windows Debugging (ISBN 0321374460) from Addison-Wesley Professional. All rights reserved.
-
- THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
- PURPOSE.
-
- --*/
- #include "stdafx.h"
- #include <windows.h>
- #include <stdio.h>
- #include <conio.h>
-
- VOID SimulateMemoryCorruption ( ) ;
-
- class CAppInfo
- {
- public:
- CAppInfo(LPWSTR wszAppName, LPWSTR wszVersion)
- {
- m_wszAppName=wszAppName;
- m_wszVersion=wszVersion;
- }
-
- VOID PrintAppInfo()
- {
- wprintf(L"\nFull application Name: %s\n", m_wszAppName);
- wprintf(L"Version: %s\n", m_wszVersion);
- }
-
- private:
- LPWSTR m_wszAppName ;
- LPWSTR m_wszVersion ;
- } ;
-
- CAppInfo* g_AppInfo ;
-
- int __cdecl wmain (int argc, WCHAR* args[])
- {
- wint_t iChar = 0 ;
- g_AppInfo = new CAppInfo(L"Memory Corruption Sample", L"1.0" );
- if(!g_AppInfo)
- {
- return 1;
- }
-
- wprintf(L"Press: \n");
- wprintf(L" 1 To display application information\n");
- wprintf(L" 2 To simulated memory corruption\n");
- wprintf(L" 3 To exit\n\n\n>");
-
- while((iChar=_getwche())!='3')
- {
- switch(iChar)
- {
- case '1':
- g_AppInfo->PrintAppInfo();
- break;
-
- case '2':
- SimulateMemoryCorruption();
- wprintf(L"\nMemory Corruption completed\n");
- break;
-
- default:
- wprintf(L"\nInvalid option\n");
- }
- wprintf(L"\n\n> ");
- }
- return 0;
- }
-
-
- VOID SimulateMemoryCorruption ( )
- {
- char* pszWrite="Corrupt";
- BYTE* p=(BYTE*) g_AppInfo;
- CopyMemory(p, pszWrite, strlen(pszWrite));
- }
/*++Copyright (c) Advanced Windows Debugging (ISBN 0321374460) from Addison-Wesley Professional. All rights reserved. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.--*/#include "stdafx.h"#include <windows.h>#include <stdio.h>#include <conio.h>VOID SimulateMemoryCorruption ( ) ;class CAppInfo{public: CAppInfo(LPWSTR wszAppName, LPWSTR wszVersion) { m_wszAppName=wszAppName; m_wszVersion=wszVersion; } VOID PrintAppInfo() { wprintf(L"\nFull application Name: %s\n", m_wszAppName); wprintf(L"Version: %s\n", m_wszVersion); }private: LPWSTR m_wszAppName ; LPWSTR m_wszVersion ;} ;CAppInfo* g_AppInfo ;int __cdecl wmain (int argc, WCHAR* args[]){ wint_t iChar = 0 ; g_AppInfo = new CAppInfo(L"Memory Corruption Sample", L"1.0" ); if(!g_AppInfo) { return 1; } wprintf(L"Press: \n"); wprintf(L" 1 To display application information\n"); wprintf(L" 2 To simulated memory corruption\n"); wprintf(L" 3 To exit\n\n\n>"); while((iChar=_getwche())!='3') { switch(iChar) { case '1': g_AppInfo->PrintAppInfo(); break; case '2': SimulateMemoryCorruption(); wprintf(L"\nMemory Corruption completed\n"); break; default: wprintf(L"\nInvalid option\n"); } wprintf(L"\n\n> "); } return 0;}VOID SimulateMemoryCorruption ( ){ char* pszWrite="Corrupt"; BYTE* p=(BYTE*) g_AppInfo; CopyMemory(p, pszWrite, strlen(pszWrite));}
編譯,產生Release,運行,選1再選2再選1,程式崩潰,開啟Dump:
0:000> lm
start end module name
00400000 00406000 test1 (deferred)
62c20000 62c29000 lpk (deferred)
73fa0000 7400b000 usp10 (deferred)
76300000 7631d000 imm32 (deferred)
76d70000 76d92000 apphelp (deferred)
77bd0000 77bd8000 version (deferred)
77d10000 77da0000 user32 (deferred)
77da0000 77e49000 advapi32 (deferred)
77e50000 77ee3000 rpcrt4 (deferred)
77ef0000 77f39000 gdi32 (deferred)
77fc0000 77fd1000 secur32 (deferred)
78520000 785c3000 msvcr90 (private pdb symbols) c:\mysymbol\msvcr90.i386.pdb\3ADD2E755BC041BC9149BFBE7C33387C1\msvcr90.i386.pdb
7c800000 7c91e000 kernel32 (deferred)
7c920000 7c9b6000 ntdll (pdb symbols) c:\mysymbol\ntdll.pdb\CEFC0863B1F84130A11E0F54180CD21A2\ntdll.pdb
載入符號檔案:
0:000> .sympath+ D:\Project1\test1\Release
Symbol search path is: C:\WINDOWS\Symbols;SRV*c:\mysymbol* http://msdl.microsoft.com/download/symbols ;D:\Project1\test1\Release
WARNING: Whitespace at end of path element
0:000> .reload /f
..............
Loading unloaded module list
.........
0:000> lm
start end module name
00400000 00406000 test1 (private pdb symbols) D:\Project1\test1\Release\test1.pdb
62c20000 62c29000 lpk (pdb symbols) C:\WINDOWS\Symbols\dll\lpk.pdb
73fa0000 7400b000 usp10 (pdb symbols) c:\mysymbol\usp10.pdb\D4BA2952809F469BB6D1D3AF6B956E6B1\usp10.pdb
76300000 7631d000 imm32 (pdb symbols) C:\WINDOWS\Symbols\dll\imm32.pdb
76d70000 76d92000 apphelp (pdb symbols) C:\WINDOWS\Symbols\dll\apphelp.pdb
77bd0000 77bd8000 version (pdb symbols) C:\WINDOWS\Symbols\dll\version.pdb
77d10000 77da0000 user32 (pdb symbols) C:\WINDOWS\Symbols\dll\user32.pdb
77da0000 77e49000 advapi32 (pdb symbols) c:\mysymbol\advapi32.pdb\F759D3F1C6614313B07C84BC33F02E4D2\advapi32.pdb
77e50000 77ee3000 rpcrt4 (pdb symbols) c:\mysymbol\rpcrt4.pdb\1A465C67828242F28A8C70E3B9D5C4772\rpcrt4.pdb
77ef0000 77f39000 gdi32 (pdb symbols) c:\mysymbol\gdi32.pdb\372C0F0E08FB456EAB7B4CB2B53E27952\gdi32.pdb
77fc0000 77fd1000 secur32 (pdb symbols) c:\mysymbol\secur32.pdb\7867B3F28B5C41CE847895E3FC013DC52\secur32.pdb
78520000 785c3000 msvcr90 (private pdb symbols) c:\mysymbol\msvcr90.i386.pdb\3ADD2E755BC041BC9149BFBE7C33387C1\msvcr90.i386.pdb
7c800000 7c91e000 kernel32 (pdb symbols) c:\mysymbol\kernel32.pdb\072FF0EB54D24DFAAE9D13885486EE092\kernel32.pdb
7c920000 7c9b6000 ntdll (pdb symbols) c:\mysymbol\ntdll.pdb\CEFC0863B1F84130A11E0F54180CD21A2\ntdll.pdb
查看堆棧:
0:000> kb
ChildEBP RetAddr Args to Child
0012ff20 78556215 785b73c8 004020f4 00000000 msvcr90!_woutput_l+0x94c [f:\dd\vctools\crt_bld\self_x86\crt\src\output.c @ 1624]
0012ff64 004010ba 004020f4 72726f43 00403380 msvcr90!wprintf+0x73 [f:\dd\vctools\crt_bld\self_x86\crt\src\wprintf.c @ 63]
0012ff7c 00401252 00000001 00392940 00392998 test1!wmain+0xba [d:\project1\test1\test1\test1.cpp @ 58]
0012ffc0 7c817077 00300031 0032002d 7ffdc000 test1!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]
0012fff0 00000000 0040139a 00000000 78746341 kernel32!BaseProcessStart+0x23
看下58行代碼,使用了 g_AppInfo->PrintAppInfo();
我們猜測,g_AppInfo的兩個成員無效,因為wprintf調用的就是它們
找到g_AppInfo,列出它的成員:
0:000> x test1!*g_*
00403374 test1!g_AppInfo = 0x00395b10
004022f0 test1!_load_config_used = struct IMAGE_LOAD_CONFIG_DIRECTORY32_2
00402098 test1!_imp___amsg_exit = <no type information>
004014fa test1!_amsg_exit = <no type information>
0:000> dt CAppInfo 0x00395b10
test1!CAppInfo
+0x000 m_wszAppName : 0x72726f43 "--- memory read error at address 0x72726f43 ---"
+0x004 m_wszVersion : 0x00747075 "???"
注意,這裡dt要用CAppInfo告訴它解析的地址類型
那我們再來看看這兩個局變數的內容:
0:000> dt CAppInfo 0x00395b10
test1!CAppInfo
+0x000 m_wszAppName : 0x72726f43 "--- memory read error at address 0x72726f43 ---"
+0x004 m_wszVersion : 0x00747075 "???"
0:000> !address 0x72726f43
62c29000 : 62c29000 - 11377000
Type 00000000
Protect 00000001 PAGE_NOACCESS
State 00010000 MEM_FREE
Usage RegionUsageFree
0:000> !address 0x00747075
005f0000 : 005f0000 - 001be000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageIsVAD
我們發現第一個變數竟然是不可訪問的,找到問題了,
0:000> dc 0x00395b10
00395b10 72726f43 00747075 00020201 000801c2 Corrupt.........
00395b20 6c75460a 7061206c 63696c70 6f697461 .Full applicatio
00395b30 614e206e 203a656d 64657465 4320790a n Name: eted.y C
00395b40 7572726f 6f697470 6153206e 656c706d orruption Sample
00395b50 0000000a 00000000 00000000 00000000 ................
00395b60 00000000 00000000 00000000 00000000 ................
00395b70 00000000 00000000 00000000 00000000 ................
00395b80 00000000 00000000 00000000 00000000 ................
原來第一個變數竟然成了字串Corrupt,查看代碼,我們發現在按2時,程式強行把字串"Corrupt"寫入了
一些的指導性的建議和策略:
1通過命令dc將指標的記憶體內容轉儲出來,dc可以將記憶體內容以雙字形式轉儲出來,如果在輸出中看到有任何的字串,那麼可以通過命令da或du把字串轉儲出來
2通過!address收集關於記憶體的資訊,!address可以告訴你記憶體的類型(如私人記憶體),保護層級(讀取和寫入),狀態(已提交或保留)和用途(棧或堆)
3.通過dds命令將記憶體轉儲為雙字或者符號,這有助於將記憶體和特定的類型關聯起來,
4.通過dpp命令對指標解引用,並且以雙字形式轉儲出記憶體的內容,如果有任何一個雙字匹配某個符號,那麼這個符號也會被顯示,如果在指標指向的記憶體中包含了一個虛函數表,那麼
這種技術是非常有用的
5.通過dpa和dpu將指標指向的記憶體分別顯示為ASII格式和Unicode格式
6.如果記憶體的內容是個很小的數值(4的值數)那麼它可能是一個控制代碼,可以通過!handle來轉儲這個控制代碼的資訊.