Windbg調試----Windbg入門


Windbg簡單來說就是一個Windows下對用戶態/內核態的程序進行調試,以及對Core Dump文件的分析。對於Crash,資源泄露,死鎖等問題的分析,Windbg是一個強有力的利器。

相關資料

本人也是在維護和開發產品的過程中使用過Windbg,但並未對Windbg進行過系統和深入的學習,也通過這一系列的博客來完善自己對Windbg以及周邊知識的理解與使用。我也列出自己正在或者即將閱讀的書/資料與大家一起分享:

安裝Windbg

由於目前微軟官網上並沒有單獨提供Windbg的下載安裝包,可以通過以下兩個途徑獲取:

  • 下載安裝WDK
  • 有網友提供了單獨的MSI安裝包,可以通過Goole搜索獲取

Windbg同時也分32位和64位版本,有網友建議是使用32位Windbg調試32位程序,64位Windbg調試64位程序。 本人平時使用64位的Windbg,如果需要分析32位的程序/Dump, 使用如下命令進行CPU模式的切換:

.load wow64exts
!sw

Windbg調試程序

在使用Windbg調試程序之前,先給大家展示下我的測試程序:

int main()
{
char* pStr = (char*)0xa;
printf("%s\n", pStr);
return 0;
}

以Viusal Studio為例,一般發布給客戶的程序,我們采用Release模式編譯程序,而Release模式與Debug模式,有個很大的區別: Release模式編譯出來的程序默認是不帶PDB相關信息的,而Debug模式則有。 采用Release模式編譯上述代碼,生成一個應用程序testforme. 然后采用Windbg打開可執行程序testforme.exe, Windbg命令窗口打印信息如下:

Microsoft (R) Windows Debugger Version 6.3.9600.16384 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: C:\Users\Administrator\Desktop\testforme.exe
Symbol search path is: *** Invalid ***
****************************************************************************
1. Symbol loading may be unreliable without a symbol search path. *
2. Use .symfix to have the debugger choose a symbol path. *
3. After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is:
ModLoad: 00000000`00400000 00000000`00410000 testforme.exe
ModLoad: 00000000`77390000 00000000`77539000 ntdll.dll
ModLoad: 00000000`77570000 00000000`776f0000 ntdll32.dll
ModLoad: 00000000`74420000 00000000`7445f000 C:\Windows\SYSTEM32\wow64.dll
ModLoad: 00000000`743c0000 00000000`7441c000 C:\Windows\SYSTEM32\wow64win.dll
ModLoad: 00000000`743b0000 00000000`743b8000 C:\Windows\SYSTEM32\wow64cpu.dll
(9f8.4d4): Break instruction exception - code 80000003 (first chance)
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ntdll.dll -
ntdll!CsrSetPriorityClass+0x40:
00000000`7743cb70 cc int 3

主要描述了3點信息:

  • 沒有設置符號信息的路徑,所以找不到符號。這里所說的符號信息就指上述PDB文件,並且在默認Release模式編譯出來的程序,會帶有一個同名的PDB文件。你也可以通過配置Visual Studio的配置項來決定是否產生PDB文件:
    VS配置

  • 可以看到已經加載的模塊,以及這些模塊所在的內存區域。比如可以看出testforme.exe的模塊位置在內存0x400000~ 0x410000

  • 調試器中斷,這時還沒有真正去執行testforme的代碼,並且可以通過Windbg命令去設置斷點,查看已加載模塊的信息等操作。

接着,我們要讓程序執行,在命令行下使用g命令, 可以看到程序中斷了在Access Violation,也就是內存訪問錯誤。

0:000:x86> g
(7b8.238): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Module load completed but symbols could not be loaded for testforme.exe
testforme+0x1e88:
00401e88 803800 cmp byte ptr [eax],0 ds:002b:0000000a=??

然后我們用kv指令查看當前異常處的函數調用棧

0:000:x86> kv
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0018fcf0 00812d70 00000000 00810000 0018fd48 testforme+0x1e88
00000000 00000000 00000000 00000000 00000000 0x812d70

這個完全看不出來哪里異常了啊,那是因為並沒有配置應用程序相應的符號文件,也就是之前所說的PDB文件。

設置調試Symbol

0:000:x86> .sympath C:\mysymbols;SRV*C:\symbols*http://msdl.microsoft.com/download/symbols

Symbol search path is: C:\mysymbols;SRV*C:\symbols*http://msdl.microsoft.com/download/symbols
Expanded Symbol search path is: c:\mysymbols;srv*c:\symbols*http://msdl.microsoft.com/download/symbols

************* Symbol Path validation summary **************
Response Time (ms) Location
OK C:\mysymbols
Deferred SRV*C:\symbols*http://msdl.microsoft.com/download/symbols

先通過.sympath設置符號文件的目錄。可以將testforme.pdb存放到設置好的符號目錄C:\mysymbolsSRV*C:\symbols*http://msdl.microsoft.com/download/symbols這標明將從http://msdl.microsoft.com/download/symbols下載微軟的PE文件對應的符號文件,並且緩存到C:\symbols目錄下。 你也可以通過File->Symbol Search Path查看符號目錄設置(如下圖),當然也可以在這里直接對符號目錄進行設置。

Symbols設置

接着調用.reload命令重新加載模塊的符號信息,然后調用kv就可以查看函數異常的函數調用棧了!

0:000:x86> .reload
Reloading current modules
........
0:000:x86> kv
ChildEBP RetAddr Args to Child
0018fee8 00401085 0040c028 0040b344 00000000 testforme!_output_l+0x7f4 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\sp\vctools\crt_bld\self_x86\crt\src\output.c @ 1648]
0018ff2c 0040100c 0040b344 0000000a 004012aa testforme!printf+0x73 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\sp\vctools\crt_bld\self_x86\crt\src\printf.c @ 63]
0018ff88 7700336a 7efde000 0018ffd4 77909f72 testforme!main+0xc (FPO: [0,0,0]) (CONV: cdecl) [d:\vsproject\testforme\testforme\test.cpp @ 11]
0018ff94 77909f72 7efde000 3cf2f376 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0018ffd4 77909f45 00401301 7efde000 ffffffff ntdll32!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
0018ffec 00000000 00401301 7efde000 00000000 ntdll32!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

通過函數調用棧,可以清楚的看出異常發生在main函數中,在test.cpp11行處調用了printf。熟悉Windows函數棧的同學應該比較清楚,ChildBEP在32位程序中表示當前調用棧的棧底指針,並且指向的內存處保存的是上一個棧貞的棧底位置。 Args to Child表示當前函數的參數,比如這里printf的參數分別為0040b3440000000a: 0000000a表示測試程序中變量pStr的值,而0040b344則表示格式化字符串,我們可以使用da命令來查看一下:

0:000:x86> da 0040b344
0040b344 "%s."

當然有時候你需要切換到特定的棧貞(frame),去查看當前frame下的上下文信息,比如局部變量。例如我們現在想去看看main函數下的變量的值,先用kn命令查看棧貞的編號,然后用.frame切換到main函數那一幀接着用dv -V查看所有的局部變量:

0:000:x86> kn
# ChildEBP RetAddr
00 0018fee8 00401085 testforme!_output_l+0x7f4 [f:\sp\vctools\crt_bld\self_x86\crt\src\output.c @ 1648]
01 0018ff2c 0040100c testforme!printf+0x73 [f:\sp\vctools\crt_bld\self_x86\crt\src\printf.c @ 63]
02 0018ff88 7700336a testforme!main+0xc [d:\vsproject\testforme\testforme\test.cpp @ 11]
03 0018ff94 77909f72 kernel32!BaseThreadInitThunk+0xe
04 0018ffd4 77909f45 ntdll32!__RtlUserThreadStart+0x70
05 0018ffec 00000000 ntdll32!_RtlUserThreadStart+0x1b
0:000:x86> .frame 2
02 0018ff88 7700336a testforme!main+0xc [d:\vsproject\testforme\testforme\test.cpp @ 11]
0:000:x86> dv /V

奇怪了為啥dv /V並沒有打印出臨時變量pStr呢? 會不會是因為被優化了? 於是就用uf命令反匯編了一下main函數 (如下), 可以看到printf的參數壓棧直接用的是常量0A

0:000:x86> uf main
testforme!main [d:\vsproject\testforme\testforme\test.cpp @ 9]:
9 00401000 6a0a push 0Ah
11 00401002 6844b34000 push offset testforme!`string' (0040b344)
11 00401007 e806000000 call testforme!printf (00401012)
11 0040100c 83c408 add esp,8
12 0040100f 33c0 xor eax,eax
13 00401011 c3 ret

所以在調試優化后的程序要注意這些可能的變化,當然如果你想讓Release的程序不進行優化,可以在Visual Studio中關閉這個選項,如下圖:
Visual Studio關閉優化選項

這里我就不再展示關閉優化后,用windbg調試打印局部變量了,大家可以自己試一試。

源碼調試

習慣於VS調試的同學,可能會覺得Windbg命令調試難記難用(事實上,當你熟悉了之后可能會改變看法)。雖然沒有VS的強大的源碼調試功能,其實Windbg也提供源碼調試的功能。可以通過.srcpath命令或者通過菜單File->Source Search Path去設置源碼的目錄。
源碼路徑設置
現在我將測試源碼拷貝到C:\source目錄,然后在用Windbg 程序后,設置斷點到測試程序的main函數入口處,然后繼續執行程序:

0:000> bp testforme!main
0:000> g

這時候你會看到源碼文件自動彈出來了,並且中斷在main入口處:
源碼調試

Windbg 還提供直接打開Source文件的功能File->Open Source File...,在調試前打開源碼文件,可以直接在里面設置斷點,調試的快捷鍵和Visual Studio一樣!

Windbg工作空間

Windbg的工作空間主要表示調試會話的狀態、調試器的設置以及窗口布局的設置等。工作空間的使用主要分為以下幾點:

  • 未加載任何調試文件的時候,選擇File -> Save Workspace保存默認工作空間,則當每次打開Windbg的時候,將采用這個默認的工作空間
  • 當調試器已經加載了調試文件的時候,選擇File -> Save Workspace將當前工作空間保存為默認工作空間 (這個默認空間僅針對這個調試文件), 則當下次還是調試這個文件的時候,則采用之前保存的默認工作空間。
  • 可以選擇File -> Save Workspace As...保存為命名的工作空間,在以后調試應用程序的時候可以選擇File -> Open Workspace去打開指定的工作空間
  • 以上的工作空間都保存在注冊表項HKEY_CURRENT_USER\Software\Microsoft\Windbg\Workspaces里面,你也可以通過File -> Save Worksapce to File...將工作空間保存到文件

還有刪除工作空間等操作,大家可以自己去熟悉一下。

Windbg的命令分類

Windbg主要分為3大類的調試命令:

  • 標准命令 (Standard Command): 這類命令對於所有的調試目標都試用,比如常見的k命令;
  • 元命令 (Meta-Command): 這類目標主要針對特定的目標所做的擴展命令,比如常見的.sympath命令。因為這類命令前面都有一個.,所以也叫作Dot-Command
  • 擴展命令 (Extension Command):標准命令和元命令都是Windbg內建的命令,而擴展命令是實現在動態加載的DLL中。這類命令前面都有一個!, 比如常用analyze -v.

順便再這里提一個很實用的命令.hh,用來在Windbg中打開幫助文檔,比如使用.hh k則幫助文檔會打開到索引k命令處。



最后貼上一個本人覺得不錯的入門文章《Windbg調試基礎》。也歡迎大家一起學習和討論。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: