本指南示範如何使用 WinDbg 來偵錯使用者模式應用程式。 您可以透過附加至執行中的進程、設定中斷點和診斷應用程式當機來練習基本的偵錯技能。
本文內容:
- 將 WinDbg 附加至記事本,並探索基本偵錯
- 除錯你自己的應用程式並分析故障
- 掌握基本的偵錯指令
您學到什麼
- 將 WinDbg 附加至執行中的進程
- 設定符號檔以取得可讀取的偵錯輸出
- 設定斷點並控制程式執行
- 分析應用程式當機
- 瀏覽執行緒和呼叫堆疊
先決條件
- WinDbg 安裝在您的計算機上。 如需安裝指示,請參閱 下載並安裝 WinDbg Windows 偵錯工具。
- 基本熟悉命令列介面
- (選擇性)封裝了第二個練習偵錯符號的已編譯應用程式
預計完成時間: 30 分鐘
開啟記事本並附加 WinDbg
移至您的安裝目錄,然後開啟 WinDbg.exe。
在 [檔案] 功能表上,選取 [啟動可執行檔]。 在 「啟動可執行檔 」對話方塊中,移至包含 notepad.exe的資料夾。 ( notepad.exe 檔案通常位於
C:\Windows\System32。針對 [檔案名稱],輸入 notepad.exe。 選擇 開啟。
設定符號檔,讓 WinDbg 可以顯示可讀取的函式和變數名稱。
在 WinDbg 視窗底部附近的命令列中,輸入下列命令:
輸出類似於下列範例:
Symbol search path is: srv* Expanded Symbol search path is: cache*;SRV什麼是符號? 符號 (PDB) 檔案包含程式碼模組的相關資訊,例如函式名稱和變數名稱。 如果沒有符號,您只會看到記憶體位址。
然後,輸入此命令:
.reload命令會告訴 WinDbg 進行初始搜尋,以尋找並載入符號檔。若要查看 notepad.exe 模組的符號,請輸入以下命令:
注意
如果沒有出現輸出,請輸入
.reload /f嘗試強制載入符號。 使用 !sym noisy 來顯示其他符號載入資訊。若要查看 notepad.exe 模組
main中包含 的符號,請使用 examine symbols 指令來列出符合遮罩的模組:x notepad!wWin*輸出類似於下列範例:
00007ff6`6e76b0a0 notepad!wWinMain (wWinMain) 00007ff6`6e783db0 notepad!wWinMainCRTStartup (wWinMainCRTStartup)若要在
notepad!wWinMain設置斷點,請輸入此命令:若要確認您已設定岔斷點,請輸入下列指令:
輸出類似於下列範例:
0 e Disable Clear 00007ff6`6e76b0a0 0001 (0001) 0:**** notepad!wWinMain若要啟動記事本程式,請輸入下列命令:
記事本會執行至
WinMain函式,然後進入調試器。Breakpoint 0 hit notepad!wWinMain: 00007ff6`6e76b0a0 488bc4 mov rax,rsp若要查看目前在記事本程式中載入的程式代碼模組清單,請輸入下列命令:
輸出類似於下列範例:
0:000> lm start end module name 00007ff6`6e760000 00007ff6`6e798000 notepad (pdb symbols) C:\ProgramData\Dbg\sym\notepad.pdb\BC04D9A431EDE299D4625AD6201C8A4A1\notepad.pdb 00007ff8`066a0000 00007ff8`067ab000 gdi32full (deferred) 00007ff8`067b0000 00007ff8`068b0000 ucrtbase (deferred) 00007ff8`06a10000 00007ff8`06aad000 msvcp_win (deferred) 00007ff8`06ab0000 00007ff8`06ad2000 win32u (deferred) 00007ff8`06b40000 00007ff8`06e08000 KERNELBASE (deferred) 00007ff8`07220000 00007ff8`072dd000 KERNEL32 (deferred) 00007ff8`07420000 00007ff8`07775000 combase (deferred) 00007ff8`07820000 00007ff8`079c0000 USER32 (deferred) 00007ff8`079c0000 00007ff8`079f0000 IMM32 (deferred) 00007ff8`07c00000 00007ff8`07c2a000 GDI32 (deferred) 00007ff8`08480000 00007ff8`085ab000 RPCRT4 (deferred) 00007ff8`085b0000 00007ff8`0864e000 msvcrt (deferred) 00007ff8`08c40000 00007ff8`08cee000 shcore (deferred) 00007ff8`08db0000 00007ff8`08fa5000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\ntdll.pdb\53F12BFE149A2F50205C8D5D66290B481\ntdll.pdb 00007fff`f8580000 00007fff`f881a000 COMCTL32 (deferred)若要查看堆疊追蹤,請輸入下列命令:
輸出類似於下列範例:
0:000> k 00 000000c8`2647f708 00007ff6`6e783d36 notepad!wWinMain 01 000000c8`2647f710 00007ff8`07237034 notepad!__scrt_common_main_seh+0x106 02 000000c8`2647f750 00007ff8`08e02651 KERNEL32!BaseThreadInitThunk+0x14 03 000000c8`2647f780 00000000`00000000 ntdll!RtlUserThreadStart+0x21若要再次啟動記事本,請輸入以下命令:
下一個: 您將中斷執行並探索載入的模組。
若要使用記事本中的「中斷」功能,請在 [檔案] 功能表上,選擇「中斷」。
若要在
ZwWriteFile設定及驗證斷點,請輸入下列命令:若要重新啟動記事本,請輸入 g。 在 [記事本] 視窗中,輸入一些文字。 在 [檔案] 功能表上,選取 [儲存]。 當談到
ZwCreateFile時,執行中的程式代碼會中斷。 輸入 k 命令以查看堆疊追蹤。
在 WinDbg 視窗中,命令列的左側會顯示處理器和執行緒號碼。 在此範例中,目前的處理器編號為0,而目前的線程編號為11(
0:011>)。 視窗會顯示處理器 0 上執行之線程 11 的堆疊追蹤。若要查看 [記事本] 程式中所有執行緒的清單,請輸入此命令(波浪號):
輸出類似於下列範例:
0:011> ~ 0 Id: 5500.34d8 Suspend: 1 Teb: 000000c8`262c4000 Unfrozen 1 Id: 5500.3960 Suspend: 1 Teb: 000000c8`262c6000 Unfrozen 2 Id: 5500.5d68 Suspend: 1 Teb: 000000c8`262c8000 Unfrozen 3 Id: 5500.4c90 Suspend: 1 Teb: 000000c8`262ca000 Unfrozen 4 Id: 5500.4ac4 Suspend: 1 Teb: 000000c8`262cc000 Unfrozen 5 Id: 5500.293c Suspend: 1 Teb: 000000c8`262ce000 Unfrozen 6 Id: 5500.53a0 Suspend: 1 Teb: 000000c8`262d0000 Unfrozen 7 Id: 5500.3ca4 Suspend: 1 Teb: 000000c8`262d4000 Unfrozen 8 Id: 5500.808 Suspend: 1 Teb: 000000c8`262da000 Unfrozen 10 Id: 5500.3940 Suspend: 1 Teb: 000000c8`262dc000 Unfrozen . 11 Id: 5500.28b0 Suspend: 1 Teb: 000000c8`262de000 Unfrozen 12 Id: 5500.12bc Suspend: 1 Teb: 000000c8`262e0000 Unfrozen 13 Id: 5500.4c34 Suspend: 1 Teb: 000000c8`262e2000 Unfrozen在此範例中,14 個線程的索引為 0 到 13。
若要檢視線程 0 的堆疊追蹤,請輸入下列命令:
輸出類似於下列範例:
0:011> ~0s 0:011> ~0s win32u!NtUserGetProp+0x14: 00007ff8`06ab1204 c3 ret 0:000> k # Child-SP RetAddr Call Site 00 000000c8`2647bd08 00007ff8`07829fe1 win32u!NtUserGetProp+0x14 01 000000c8`2647bd10 00007fff`f86099be USER32!GetPropW+0xd1 02 000000c8`2647bd40 00007ff8`07d12f4d COMCTL32!DefSubclassProc+0x4e 03 000000c8`2647bd90 00007fff`f8609aba SHELL32!CAutoComplete::_EditWndProc+0xb1 04 000000c8`2647bde0 00007fff`f86098b7 COMCTL32!CallNextSubclassProc+0x9a 05 000000c8`2647be60 00007ff8`0782e858 COMCTL32!MasterSubclassProc+0xa7 06 000000c8`2647bf00 00007ff8`0782de1b USER32!UserCallWinProcCheckWow+0x2f8 07 000000c8`2647c090 00007ff8`0782d68a USER32!SendMessageWorker+0x70b 08 000000c8`2647c130 00007ff8`07afa4db USER32!SendMessageW+0xda若要結束偵錯並從記事本程式卸載,請輸入下列命令:
開啟您自己的應用程式並連結 WinDbg
例如,假設您撰寫並建置了這個小型主控台應用程式:
...
void MyFunction(long p1, long p2, long p3)
{
long x = p1 + p2 + p3;
long y = 0;
y = x / p2;
}
void main ()
{
long a = 2;
long b = 0;
MyFunction(a, b, 5);
}
在此練習中,假設建置的應用程式 (MyApp.exe) 和符號檔 (MyApp.pdb) 位於 C:\MyApp\x64\Debug 中。 也假設應用程式原始碼位於 C:\MyApp\MyApp and that the target machine compiled MyApp.exe中。
開啟 WinDbg。
在 [檔案] 功能表上,選取 [啟動可執行檔]。 在 [啟動可執行檔 ] 對話方塊中,移至 C:\MyApp\x64\Debug。 針對 檔名,輸入 MyApp.exe。 選擇 開啟。
輸入下列命令:
.sympath+ C:\MyApp\x64\Debug
這些命令會告訴 WinDbg 在何處尋找應用程式的符號和原始程式碼。 在此情況下,您不需要使用 .srcpath 來設定原始程式碼位置,因為符號具有原始檔案的完整路徑。
輸入下列命令:
您的應用程式在到達其
main函式時會中斷偵錯工具。WinDbg 會顯示您的原始程式碼和 [命令] 視窗。
在 [偵錯] 功能表上,選取 [進入](或選取 F11)。 繼續邁步,直到您踏入
MyFunction。 當您進入第y = x / p2行時,您的應用程式會當機並進入偵錯模式。輸出類似於下列範例:
(1450.1424): Integer divide-by-zero - code c0000094 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. MyApp!MyFunction+0x44: 00007ff6`3be11064 f77c2428 idiv eax,dword ptr [rsp+28h] ss:00000063`2036f808=00000000輸入此指令:
WinDbg 會顯示問題的分析結果(在這個例子中,除以 0)。
FAULTING_IP: MyApp!MyFunction+44 [c:\myapp\myapp\myapp.cpp @ 7] 00007ff6`3be11064 f77c2428 idiv eax,dword ptr [rsp+28h] EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 00007ff63be11064 (MyApp!MyFunction+0x0000000000000044) ExceptionCode: c0000094 (Integer divide-by-zero) ExceptionFlags: 00000000 NumberParameters: 0 ... STACK_TEXT: 00000063`2036f7e0 00007ff6`3be110b8 : ... : MyApp!MyFunction+0x44 00000063`2036f800 00007ff6`3be1141d : ... : MyApp!main+0x38 00000063`2036f840 00007ff6`3be1154e : ... : MyApp!__tmainCRTStartup+0x19d 00000063`2036f8b0 00007ffc`b1cf16ad : ... : MyApp!mainCRTStartup+0xe 00000063`2036f8e0 00007ffc`b1fc4629 : ... : KERNEL32!BaseThreadInitThunk+0xd 00000063`2036f910 00000000`00000000 : ... : ntdll!RtlUserThreadStart+0x1d STACK_COMMAND: dt ntdll!LdrpLastDllInitializer BaseDllName ;dt ntdll!LdrpFailureData ;.cxr 0x0 ;kb FOLLOWUP_IP: MyApp!MyFunction+44 [c:\myapp\myapp\myapp.cpp @ 7] 00007ff6`3be11064 f77c2428 idiv eax,dword ptr [rsp+28h] FAULTING_SOURCE_LINE: c:\myapp\myapp\myapp.cpp FAULTING_SOURCE_FILE: c:\myapp\myapp\myapp.cpp FAULTING_SOURCE_LINE_NUMBER: 7 FAULTING_SOURCE_CODE: 3: void MyFunction(long p1, long p2, long p3) 4: { 5: long x = p1 + p2 + p3; 6: long y = 0; > 7: y = x / p2; 8: } 9: 10: void main () 11: { 12: long a = 2; ...
後續步驟
偵錯系統應用程式和您自己的程式碼之後,您就可以探索更進階的偵錯案例:
- 開始使用 WinDbg (核心模式) - 偵錯 Windows 核心和驅動程式
- 偵錯工具作業 - 瞭解偵錯工具作業概念
- 偵錯技術 - 探索進階偵錯方法
命令摘要
以下是您在本教學中使用的基本命令:
設置和符號:
- .sympath (設定符號路徑) - 設定 WinDbg 尋找符號檔案的位置
- .reload (重新載入模組) - 載入符號檔
控制執行:
- g (Go) - 繼續程式執行
- bu (設定中斷點) - 在特定函數上暫停執行
-
Step Into(F11) - 執行一條指令並逐步執行函數
檢查您的程式:
- x(檢查符號) -列出函數和變數
- lm (列出載入的模組) - 顯示所有載入的 DLL 和可執行檔
- k (顯示堆疊回溯) - 檢視呼叫堆疊
- ~(線程狀態) - 列出所有線程
- !analyze -v - 自動分析當機
參考: