天天看點

Windbg教程-調試非托管程式的基本指令上

Windbg是跟visual studio差不多的一個調試器,可以用來調試非托管程式(native application),也可以調試托管程式(managed application)。它比VS強的地方是你可以使用它來調試Windows作業系統,是以它也被叫做kernel mode debugger,同樣為kernel mode debugger的調試器還有随着windbg一起安裝的kd.exe(隻不過這是一個指令行工具而已)。然而它在調試托管程式方面會比VS難用很多,當然如果你想看.NET程式的CLR内部結構,例如記憶體相關的資訊的話,使用windbg還是可以的。

本篇文章裡,以下面這個簡單的程式為例,我介紹一些在windbg中,調試托管程式常用的指令:

Nativedebug.cpp

#include "stdafx.h"

#include <tchar.h>

void Usage()

{

#ifdef _UNICODE

                   wprintf(L"[Usage]: nativedebug.exe <digital numbers>\n");

#else

                   printf("[Usage]: nativedebug.exe <digital numbers>\n");

#endif

}

int _tmain(int argc, _TCHAR* argv[])

         int result = 0;

         if ( argc != 2 )

         {

                   Usage();

                   return -1;

         }

         result = _ttol(argv[1]);

         wprintf(L"%s * %s = %d\n", argv[1], argv[1], result * result);

         wprintf(L"Press any key to exit ...\n");

         _getwch();

         printf("%s * %s = %d\n", result * result);

         printf("Press any key to exit ...\n");

         _getch();

         return 0;

編譯好了以後,運作windbg.exe,點選菜單中的“File”-“Open Executable”,在彈出的對話框中選擇nativedebug.exe。你應該可以看到類似下面的輸出,我使用綠色的字元注釋了各行輸出的含義:

#

# 注意黃色高亮的字型,它說明這個調試器是32位的調試器,隻能調試32位的程式,

# 如果你需要調試x64或者IA 64的程式,需要運作對應的調試器。

Microsoft (R) Windows Debugger Version 6.10.0003.233 X86

Copyright (c) Microsoft Corporation. All rights reserved.

# 這一行注明了被調試程式的指令行

CommandLine: E:\臨時文檔\Windbg教程\nativedebug\Debug\nativedebug.exe

# 下面這幾行說明目前沒有設定好符号檔案搜尋路徑,符号檔案在調試中比源檔案

# 還重要,關于符号檔案的說明和其重要性請參考我的另外一篇文章

Symbol search path is: *** Invalid ***

****************************************************************************

* Symbol loading may be unreliable without a symbol search path.           *

* Use .symfix to have the debugger choose a symbol path.                   *

* After setting your symbol path, use .reload to refresh symbol locations. *

# 可執行檔案搜尋路徑告訴了windbg在哪裡搜尋可執行檔案(也就是.exe和.dll檔案)

# 大部分情況下,windbg知道怎樣找到可執行檔案--從程式的啟動目錄,從PATH環境

# 變量裡面,從system32檔案夾裡面等等。是以大部分情況下,你不需要設定這個變量

# 然而在少數情況下,你需要使用.exepath設定它。請參看windbg幫助裡面的.exepath

# 的說明來了解。一種少數情況基本上我們用不到—是核心模式的調試,另外一種情況

# 是在調試使用者模式(user mode)mini dump檔案的時候需要指明。

Executable search path is:

# ModLoad: 指明了目前程式加載的其所依賴的 dll檔案,以及它們加載的位址。順便

# 說一句,windbg有的時候會使用這些資訊來将堆棧轉換到對應的函數上去。

ModLoad: 00950000 0096b000   nativedebug.exe

ModLoad: 77d80000 77f00000   ntdll.dll

ModLoad: 76070000 76170000   C:\Windows\syswow64\kernel32.dll

ModLoad: 77670000 776b4000   C:\Windows\syswow64\KERNELBASE.dll

ModLoad: 6ed80000 6eea4000  C:\Windows\WinSxS\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_2a4cbfc25558bcd3\MSVCR90D.dll

# 下面這一行說明了目前是什麼異常導緻程式中斷執行,是的 ,雖然我們剛剛啟動被

# 調試的程式,的确是一個異常—斷點異常導緻程式中斷執行。至于原因請參看我的

# 另外一篇文章:

# 另外,下面一行中,第一個括号裡面的是目前的線程ID,第二個括号指明了目前的異常

# 是第一次機會處理,還是第二次機會處理。First chance的含義請參看我的這篇文章:

(ec4.d7c): Break instruction exception - code 80000003 (first chance)

# 目前CPU中各個寄存器的值

eax=00000000 ebx=00000000 ecx=f2260000 edx=0016ddd8 esi=fffffffe edi=77da3bdc

eip=77e207ff esp=002df598 ebp=002df5c4 iopl=0         nv up ei pl zr na pe nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b            efl=00000246

# 沒有symbol,是以在調試的時候比較麻煩,另外export symbol的含義在後一篇文章

# 中講

*** ERROR: Symbol file could not be found. Defaulted to export symbols for ntdll.dll -

# 目前中斷時,程式正在執行的函數,以及其位置。

ntdll!LdrVerifyImageMatchesChecksum+0x6ce:

# 目前執行的指令(或者是下一條要執行的指令)。

77e207ff cc              int     3

Windbg跟VS不同的地方是,windbg在啟動程式後,執行main函數之前,就中斷了,而VS卻是直接執行,原因是VS是一個整合的平台,你可以先在源代碼裡面設定好斷點,然後VS從源代碼編譯完程式後,啟動程式之前自動就可以把斷點設定好。而windbg隻是一個調試器,是以它需要給程式員設定斷點的機會,實際上所有的調試器的使用模式都是類似的。

 因為沒有符号檔案,不好調試,是以需要先加載符号檔案。Windbg知道幾個符号檔案搜尋路徑(symbol path)――就是微軟的公開符号檔案伺服器,如果你想檢視你的程式在運作過程中使用了哪些windows的API和函數的話,可以使用它。執行下面的指令來使用這個檔案伺服器(不要忽略了前面的點号):

.symfix

上面的指令自動将symbol path設定為微軟的公開符号檔案伺服器位址,如果你在微軟内部混過,你會發現這個指令設定的路徑有點不同,噓……。

Sympath可以設定和檢視symbol的路徑,執行下面這個指令來檢視最新的符号檔案搜尋路徑:

.sympath

# 輸出結果

Symbol search path is: SRV**http://msdl.microsoft.com/download/symbols

設定好符号檔案搜尋路徑以後,接下來每次加載新的可執行(.exe或者.dll)檔案,windbg都會先在這些路徑裡面搜尋它的符号檔案,但是對于已經加載了的可執行檔案,例如ntdll.dll和kernel32.dll,我們需要手動重新加載,使用下面的指令來加載:

.reload

Reloading current modules

.....

這個時候,點選windbg菜單裡面的“View”-“Call Stack”,就可以看到堆棧了。因為我們還用到了MS CRT裡面的函數,即atoi,是以我們還需要VCRT的符号檔案。幸運的是,VS自帶了VCRT的私有符号檔案,這樣我們用下面的指令将這個符号檔案的檔案夾路徑添加到windbg的符号檔案搜尋路徑清單裡面來,注意後面的加号,如果不寫加号,就說明将搜尋路徑完全替換成新的:

.sympath+ C:\Windows\symbols\dll

Symbol search path is: SRV**http://msdl.microsoft.com/download/symbols;C:\Windows\symbols\dll

執行下面的指令看一下加載的結果(注意前面沒有點号):

lm

start    end        module name

00b60000 00b7b000   nativedebug   (deferred)            

6fef0000 70014000   MSVCR90D   (private pdb symbols) D:\Debuggers\sym\msvcr90d.i386.pdb\EBEA784C96244F1E8F8D35E0391C898D1\msvcr90d.i386.pdb

76070000 76170000   kernel32   (deferred)            

77670000 776b4000   KERNELBASE   (deferred)            

77d80000 77f00000   ntdll      (pdb symbols)          D:\Debuggers\sym\wntdll.pdb\E06BEA155E9748BEA818E2D0DD2FED952\wntdll.pdb

上面的輸出結果會在後文講到,至于如何設定斷點,以及在指令行裡面檢視堆棧,在後面講――寫文章還是蠻辛苦的。

本文轉自 donjuan 部落格園部落格,原文連結: http://www.cnblogs.com/killmyday/archive/2010/02/22/1671511.html  ,如需轉載請自行聯系原作者