你可以從微軟網站上下載到的調試器:
· KD-內核調試器。你可以用它來調試藍屏一類的系統問題。如果是開發設備驅動程序是少不了它的。
· CDB-命令行調試器。這是一個命令行程序
· NTSD-NT調試器。這是一個用戶模式調試器,可以用來調試用戶模式應用程序。它實際上是一個CDB的windows UI增強。
· WinDbg-用一個漂亮的UI包裝了KD和NTSD。WinDbg即可以調試內核模式,也可以調試用戶模式程序。
· VS, VS.net-使用同KD和NTSD相同的調試引擎,并且相比于同樣用于調試目的的WinDbg,提供了功能更豐富的界面。
WinDbg實際上包裝了NTSD和KD并且提供了一個更好用的用戶界面。它也提供了命令行開關,比如最小化啟動(-m),附加到一PID指定的進程(-p)以及自動打開崩潰文件(-z)。它支持三種類型的命令。
· Regular commands(比如: k) 用來調試進程
· Dot commands(比如:.sympath)用來控制調試器
· Extension commands(比如: !handle)-這些命令屬于可以用來添加到WinDbg的自定義命令;它們用擴展DLL的輸出函數來實現。
PDB文件
PDB文件, 是鏈接器生成程序數據庫文件(Program database files)。私有的PDB文件包括私有以及公有符號,源代碼行號,類型,局部以及全局變量。公有的PDB文件不包含類型,局部變量以及源代碼行號信息。
配置WinDbg
運行WinDbg->菜單->File->Symbol File Path->按照下面的方法設置_NT_SYMBOL_PATH變量:
在彈出的框中輸入“C:\MyCodesSymbols; SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols”(按照這樣設置,WinDbg將先從本地文件夾C:\MyCodesSymbols中查找Symbol,如果找不到,則自動從MS的Symbol Server上下載Symbols)。另一種做法是從這個Symbol下載地址中http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx,下載相應操作系統所需要的完整的Symbol安裝包,并進行安裝,例如我將其安裝在D:\WINDOWS\Symbols,在該框中輸入“D:\WINDOWS\Symbols”。(這里要注意下載的Symbols的版本一定要正確,在我的Win2003+Sp1上,我曾經以為安裝Win2003+Sp2的Symbols可能會牛×點,但結果證明我錯了,用WinDbg打開可執行文件時,提示“PDB symbol for mscorwks.dll not loaded;Defaulted to export symbols for ntdll.dll”的錯誤,我有重新裝上Win2003+Sp1的Symbols, 現在一切運行正常^_^)
set _NT_SYMBOL_PATH=srv*C:\MySymbols*http://msdl.microsoft.com/download/symbols
WINDBG命令
調試前的必備工作
在開始調試前首先要做的工作是設置好符號(Symbols)路徑。沒有符號,你看到的調用堆棧基本上毫無意義。Microsoft的操作系統符號文件(PDB)是對外公開的。另外請注意在編譯你自己的程序選擇生成PDB文件的選項。如果設置好符號路徑后,調用堆棧看起來還是不對。可以使用lm, !sym noisy, !reload 等命令來驗證符號路徑是否正確。
Windbg也支持源碼級的調試。在開始源碼調試前,你需要用.srcpath設置源代碼路徑。如果你是在生成所執行代碼的機器上進行調試,符號文件中的源碼路徑會指向正確的位置,所以不需要設置源代碼路徑。如果所執行代碼是在另一臺機器上生成的,你可以將所用的源碼拷貝(保持原有的目錄結構)的一個可以訪問的文件夾(可以是網絡路徑)并將源代碼路徑設為該文件夾的路徑。注意如果是遠程調試,你需要使用.lsrcpath來設置源碼路徑。
2)啟動Debugger
Windbg可以用于如下三種調試:
(1)遠程調試:你可以從機器A上調試在機器B上執行的程序。具體步驟如下:
在機器B上啟動一個調試窗口(Debug Session)。你可以直接在Windbg下運行一個程序或者將Windbg附加(Attach)到一個進程。
在機器B的Windbg命令窗口上啟動一個遠程調試接口(remote):
.server npipe:pipe=PIPE_NAME
PIPE_NAME是該接口的名字。
在機器A上運行:
windbg –remote npipe:server=SERVER_NAME,pipe=PIPE_NAME
SERVER_NAME是機器B的名字。
Dump文件調試:如果在你的客戶的機器上出現問題,你可能不能使用遠程調試來解決問題。你可以要求你的用戶將Windbg附加到出現問題的進程上,然后在命令窗口中輸入:
.dump /ma File Name
創建一個Dump文件。在得到Dump文件后,使用如下的命令來打開它:
windbg –z DUMP_FILE_NAME
(2)本地進程調試:你可以在Windbg下直接運行一個程序:
Windbg “path to executable” arguments
也可以將Windbg附加到一個正在運行的程序:
Windbg –p “process id”
Windbg –pn “process name”
注意有一種非侵入(Noninvasive)模式可以用來檢查一個進程的狀態并不進程的執行。當然在這種模式下無法控制被調試程序的執行。這種模式也可以用于查看一個已經在Debugger控制下運行的進程。具體命令如下:
Windbg –pv –p “process id”
Windbg –pv –pn “process name”
(3)調試多個進程和線程
如果你想控制一個進程以及它的子進程的執行,在Windbg的命令行上加上-o選項。Windbg中還有一個新的命令.childdbg 可以用來控制子進程的調試。如果你同時調試幾個進程,可以使用 | 命令來顯示并切換到不同的進程。
在同一個進程中可能有多個線程。~命令可以用來顯示和切換線程。
.hh keyword 如何得到幫助
靜態命令:
顯示調用堆棧:在連接到一個調試窗口后,首先要知道的就是程序當前的執行情況k* 命令顯示當前線程的堆棧。~*kb會顯示所有線程的調用堆棧。如果堆棧太長,Windbg只會顯示堆棧的一部分。.kframes可以用來設置缺省顯示框架數。
顯示局部變量:接下來要做通常是用dv顯示局部變量的信息。CTRL+ALT+V可以切換到更詳細的顯示模式。關于dv要注意的是在優化過的代碼中dv的輸出極有可能是不準確的。這時后你能做的就是閱讀匯編代碼來發現你感興趣的值是否存儲在寄存器中或堆棧上。有時后當前的框架(Frame)上可能找不到你想知道的數據。如果該數據是作為參數傳到當前的方法中的,可以讀一讀上一個或幾個框架的匯編代碼,有可能該數據還在堆棧的某個地址上。靜態變量是儲存在固定地址中的,所以找出靜態變量的值較為容易。.Frame(或者在調用堆棧窗口中雙擊)可以用來切換當前的框架。注意dv命令顯示的是當前框架的內容。你也可在watch窗口中觀察局部變量的值。
顯示類和鏈表: dt可以顯示數據結構。比如dt PEB 會顯示操作系統進程結構。在后面跟上一個進程結構的地址會顯示該結構的詳細信息:dt PEB 7ffdf000。
Dl命令可以顯示一些特定的鏈表結構。
顯示當前線程的錯誤值:!gle會顯示當前線程的上一個錯誤值和狀態值。!error命令可以解碼HRESULT。
搜索或修改內存:使用s 命令來搜索字節,字或雙字,QWORD或字符串。使用e命令來修改內存。
計算表達式:?命令可以用來進行計算。關于表達式的格式請參照幫助文檔。使用n命令來切換輸入數字的進制。
顯示當前線程,進程和模塊信息:!teb顯示當前線程的環境信息。最常見的用途是查看當前線程堆棧的起始地址,然后在堆棧中搜索值。!peb顯示當前進程的環境信息,比如執行文件的路徑等等。lm顯示進程中加載的模塊信息。
顯示寄存器的值:r命令可以顯示和修改寄存器的值。如果要在表達式中使用寄存器的值,在寄存器名前加@符號(比如@eax)。
顯示最相近的符號:ln Address。如果你有一個C++對象的指針,可以用來ln來查看該對象類型。
查找符號:x命令可以用來查找全局變量的地址或過程的地址。x命令支持匹配符號。x kernel32!*顯示Kernel32.dll中的所有可見變量,數據結構和過程。
查看lock:!locks顯示各線程的鎖資源使用情況。對調試死鎖很有用。
查看handle:!handle顯示句柄信息。如果一段代碼導致句柄泄漏,你只需要在代碼執行前后使用!handle命令并比較兩次輸出的區別。有一個命令!htrace對調試與句柄有關的Bug非常有用。在開始調試前輸入:
!htrace –enable
然后在調試過程中使用!htrace handle_value 來顯示所有與該句柄有關的調用堆棧。
顯示匯編代碼:u。
程序執行控制命令:
設置代碼斷點:bp/bu/bm 可以用來設置代碼斷點。你可以指定斷點被跳過的次數。假設一段代碼KERNEL32!SetLastError在運行很多次后會出錯,你可以設置如下斷點:
bp KERNEL32!SetLastError 0x100.
在出錯后使用bl 來顯示斷點信息(注意粗體顯示的值):
0 e 77e7a3b0 004f (0100) 0:*** KERNEL32!SetLastError
重新啟動調試(.restart命令)并設置如下的斷點:
bp Kernel32!SetLastError 0x100-0x4f
Debugger會停在出錯前最后一次調用該過程的地方。
你可以指定斷點被激活時Debugger應當執行的命令串。在該命令串中使用J命令可以用來設置條件斷點:
bp `mysource.cpp:143` "j (poi(MyVar)”0n20) ''; 'g' "
上面的斷點只在MyVar的值大于32時被激活(g命令
條件斷點的用途極為廣泛。你可以指定一個斷點只在特殊的情況下被激活,比如傳入的參數滿足一定的條件,調用者是某個特殊的過程,某個全局變量被設為特殊的值等等。
設置內存斷點:ba可以用來設置內存斷點。調試過程中一個常見的問題是跟蹤某些數據的變化。如下的斷點:
ba w4 0x40000000 "kb; g"
可以打印出所有修改0x40000000的調用堆棧。
控制程序執行:p, pa,t, ta等命令可以用來控制程序的執行。
控制異常和事件處理:Debugger的缺省設置是跳過首次異常(first chance expcetion),在二次異常(second chance exception)時中斷程序的執行。sx命令顯示Debugger的設置。sxe和sxd可以改變Debugger的設置。
sxe clr
可以控制Debugger在托管異常發生時中斷程序的執行。常用的Debugger事件有:
av 訪問異常
eh C++異常
clr 托管異常
ld 模塊加載
-c 選項可以用來指定在事件發生時執行的調試命令。
堆棧顯示指令kb , kp, kP , kv
反匯編指令 u,uf
跟蹤指令 T,TA,TB,TC
執行相關指令 P,PA,PC
跟蹤查看指令 WT
----------------------------------------------------------------------------
堆棧顯示指令
k [b|p|P|v]
在內核調試的時候,k命令用來顯示內核棧的內容
先說說內核棧用來干嘛的 看了些資料個人理解是這樣的
比如我們的代碼運行時,肯定會有函數函數然后還會調用函數 但是系統如何記錄是哪個父函數調用了這個子函數,在子函數調用之前整個狀態又是怎樣的,其實系統是利用了堆棧記錄的 棧這個東西好阿 先進后出 最近調用的函數記錄在最頂層 函數執行完后就從棧內彈出之前記錄的參數,如果調用函數 一樣的把函數壓進棧內就好了 這樣一來 一旦子函數執行完,從棧內彈出的第一個函數肯定是該子函數的老爹 我們可以看上層堆棧的狀態等等 功能大家慢慢去體會吧我也沒用過 呵呵 不好說什么 下面說些細節的東西
b
顯示傳給函數的前三個參數
p
顯示傳給函數的全部參數
P( 大寫)
跟上面那個一樣 只不過是顯示形式不同而已
V
外加顯示一些額外的信息
----------------------------------------------------------------------------
u [f]
反匯編指令,嘿嘿 超級有用的指令喲雖然說內核很多東西很復雜 認識偶爾小小反下也是可以的
u
反匯編當前寄存器指向的代碼
uf 函數名(比如nt!ZwCreateFile)
反匯編指定的函數
----------------------------------------------------------------------------
t [r]
單步跟蹤
r 打開指顯示寄存器的詳細信息,狀態的開關(下面指令一樣有效,在用1次就會關閉哦~)
ta 地址
讓程序執行到指定地址
tb
讓程序運行到分支語句時停止
tc
讓程序運行到下一個函數調用停止
----------------------------------------------------------------------------
p [r]
單步執行一跳指令
r 打開指顯示寄存器的詳細信息,狀態的開關(下面指令一樣有效,在用1次就會關閉哦~)
pa
讓程序執行到指定地址
pc
讓程序執行到函數調用就停止
----------------------------------------------------------------------------
wt
在想查看指定函數的信息而又不想單步通過該函數時很有用。可以到函數的起始地址并執行 wt 命令。(摘自翻譯文檔)
這個感覺用處不是很大.不細細研究了
----------------------------------------------------------------------------
Ps: 很多人不清楚到底p指令和t指令有什么區別 其實很簡單 p指令執行到函數時把這個當做一個指令來執行也就是說不會進入函數執行,但是t指令會進入到函數里面執行 就這么簡單~~呵呵
遠程調試
使用WinDbg進行遠程調試是很容易的,而且有很多種可行的方法。在下文中,’調試服務器’指的是運行在你所要調試的遠程機器上的調試器。’調試客戶端’指的是控制當前會話的調試器。
· 使用調試器:你需要CDB, NTSD或者WinDbg已經安裝在遠程機器上。WinDbg客戶端可以連接到CDB, NTSD或者WinDbg中的任何一個作為服務器,反之亦然。在客戶端和服務器直接可以選擇TCP或者命名管道作為通訊協議。
o 在服務器端的啟動過程:
§ WinDbg –server npipe:pipe=pipename(注:可以允許多個客戶端連接)
§ 從WinDbg內部: .server npipe:pipe=pipename(注,連接單個客戶端)
你可以用多種協議開啟不同的服務會話。并且可用密碼來保護一個會話。
o 從客戶端連接:
§ WinDbg -remote npipe:server=Server, pipe=PipeName[,password=Password]
§ 從WinDbg內部: File->Connect to Remote Session: for connection string, enter npipe:server=Server, pipe=PipeName [,password=Password]
· 使用Remote.exe: Remote.exe使用命名管道作為通訊的方式。如果你使用的是一個命令行接口的程序,比如KD,CDB或者NTSD。你可以使用remote.exe來遠程調試。注意:使用@q(不是q)來退出客戶端,不用關掉服務端。
o 要啟動一個服務端:
§ Remote.exe /s “cdp –p <pid>” test1
o 從客戶端連接:
§ Remote.exe /c <machinename> test1
上面的test1是我們所選擇的命名管道的名字。
服務端會顯示那個客戶端從那個服務器連接以及執行過的命令。你可以使用‘qq’命令來退出服務端;或者使用File->Exit來退出客戶端。另外,如果要進行遠程調試,你必須屬于遠程機器的”Debugger User”組并且服務器必須允許遠程連接。
即時調試
在WinDbg的文檔的”Enabling Postmorten Debugging”部分對此有很詳細的討論。簡而言之,你可以把WinDbg設置成默認的即時調試器,命令就是:Windbg –I。這個命令實際上是把注冊表中 HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug的鍵值設置成WinDbg。如果要把WinDbg設置成為默認的托管調試器,你需要顯示設置如下的注冊表鍵值:
HKLM\Software\Microsoft\.NETFramework\DbgJITDebugLaunchSetting 設置成 2
HKLM\Software\Microsoft\.NETFramework\DbgManagedDebugger 設置成Windbg.(注意其中的啟動參數設置)
通過JIT的設置,當一個應用程序在不是調試的狀態下拋出了未處理的異常之時,WinDbg就會被啟動。
64位調試
所有這些調試器均支持在AMD64和IA64上的64位調試環境。
托管應用程序的調試
WinDbg 6.3以后的版本支持在Widbey(VS2005和.net 2.0的內部開發代號) .net CLR托管調試。在文檔中針對托管調試有很好的討論。需要注意的是,對于托管程序來說,沒有剛才所說的PDB(譯注:托管代碼實際上也是有PDB的,但是這個PDB實際上記錄了C#代碼和IL代碼的對應關系以及相關的一些信息)的概念,因為所有的程序都是編譯成為ILASM。調試器通過CLR來查詢所需的附加信息。
有幾點需要注意:
你只能在托段函數的代碼被執行過至少一次之后才能設置斷點。只有這樣它才能被編譯成匯編代碼。記住以下的幾點:
· 關于函數的地址的復雜化以及對應的斷點設置:
o CLR有可能丟棄已經編譯好的代碼,所以函數的入口地址有可能改變。
o 同樣的代碼有可能被多次編譯,如果多個應用程序域沒有共享這段代碼的話。如果你設置了一個斷點,它就會被設置在當前線程(譯注:CLR的邏輯線程)所在的應用程序域內。
o 泛型的特殊實例可能導致同一個函數有不同的地址。.
· 數據存儲布局的復雜化以及對應的數據檢查:
CLR可能會在運行的時候任意改變數據的存儲布局,所以一個結構體成員的偏移量可能會被改變掉. (譯注:實際上是在一個類型被加載的時候決定的數據布局,之后是不會改變的。)
一個類型的信息是在第一次使用的時候被加載,所以你可能不能夠查看一個數據成員如果它還沒有被使用過.
· 調試器命令的復雜化
o 當跟蹤托管代碼的時候,你會需要穿越大段的CLR自己的代碼比如JIT編譯器的代碼,原因可能是你第一次進入一個函數,或者是你在托管和非托管代碼之間進行切換。
調試Windows服務
使用WinDbg,你可以像調試其它應用程序那樣調試Windows服務程序。即可以通過附加進程的方法啟動Windows服務,也可以把WinDbg當作一個即時調試器,并且在代碼中調用DbgBreakPoint 或者 DebugBreak,或者在x86機器上加入一條int 3匯編指令。
調試異常
一個調試器會得到兩次的異常通知-第一次在應用程序有機會處理異常之前(‘first chance exception’);如果應用程序沒有處理這個異常,這時候調試器就會有機會來處理異常(‘second-chance exception’)。如果調試器沒有處理二次機會的異常,應用程序就會退出。
.lastevent或者,!analyze –v命令會給你顯示異常的記錄以及異常拋出所在函數的堆棧跟蹤信息。
你也可以使用 .exr, .cxr以及 .ecxr命令來顯示異常和上下文記錄。同時需要注意的是,你也可以改變first-chance的處理選項。對應的命令就是: sxe, sxd, sxn和sxi。
虛擬機調試驅動
1)
將 WinDbg 發送一個快捷方式,并修改在快捷方式上右鍵=>"屬性"
將"目標"中的 WinDbg 文件名后添加 "-k com:port=\\.\pipe\com_1,baud=115200,pipe" , 如下:
"C:\Program Files\Debugging Tools for Windows\windbg.exe" -k com:port=\\.\pipe\com_1,baud=115200,pipe
2)
打開虛擬機中的 c:\boot.ini 文件(之前去掉"只讀"屬性),復制一行
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise" /fastdetect
即添加了一個啟動選項,并修改為:
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise[Debug]" /fastdetect /debug /debugport=com1 /baudrate=115200
即添加了調試選項,調試端口以及串口的速率.
保存.
3)
關閉虛擬機里的目標windows系統(必須,否則在"Settings..."里的"Add..."將為灰色,不可選狀態),
選擇目標windows系統的"Settings..."選項,在"Hardware"選項中,點擊下面的"Add..."按鈕.
選擇"Serial Port"點擊"Next",再選擇"Output to named pipe","Next",
這一向導中,前兩項不修改,最后一項修改為"The other end is an application.",
如果這里存在"高級"選項,則在其中選擇"Yield CPU on poll"[注:有些虛擬機在這里并沒有"高級"選項,則在"Finish"后,選擇"Serial Port",再勾選右下角的"Yield CPU on poll"],
"Finish".
"OK",完成"Virtual Maching Setting".
4)
打開虛擬機中的目標Windows系統,選擇"Windows Server 2003, Enterprise[Debug]"后,立即打開之前創建的WinDbg快捷方式(若先打開WinDbg,則會報找不到'com:port=....'等信息).
5)
連接上虛擬機中的目標windows系統后,立即發送一個WinDbg中的"Debug"=>"Break"來中斷它.
6)
設置"Symbol"路徑,"Source"路徑和"ImagePath"路徑(若有多個,則用";"號隔開).
如:
"File"=>"Symbol" 里設置:"G:\虛擬機共享文件\Win2003Symbols;G:\虛擬機共享文件\GiveIO",勾選"Reload","OK". (第一個是Windows的符號[這里要注意,符號應該是虛擬機內Windows系統對應的],第二個是要調試的驅動的符號)
"File"=>"Source" 里設置:"G:\虛擬機共享文件\GiveIO", "OK". (要調試的驅動的源碼我放在了此目錄中)
"File"=>"Image File Path" 里設置: "G:\虛擬機共享文件\GiveIO", "Reload", "OK". (我把要調試的驅動程序放在了此目錄中)
"File"=>"Open Source File", 打開驅動源文件.
7)
在要調試的驅動程序上下斷點(這里我調試的驅動模塊名為"GiveIO",后面是在"DriverEntry"入口點下斷點[bu設置的是延遲斷點]):
bu GiveIO!DriverEntry
按"F5"(或在命令行中用"g"命令),即可讓虛擬機中的目標Windows順利啟動.
8)
在虛擬機中運行要調試的驅動程序,即可運行到斷點處.
9)
一些說明:
在已運行的Windows目標系統中調試指定驅動:
首先應使WinDbg產生一個斷點,使用WinDbg中的"Debug"=>"Break",此時目標Windows系統就會中斷.
此時使用:
bu GiveIO!DriverEntry
命令,在指定驅動的模塊名的入口點下斷點(GiveIO為此處的驅動模塊名).
運行驅動后,即會在驅動的DriverEntry處斷下來,此時就可以進行跟蹤調試了.
bl命令可以查看已下的斷點
bc清除斷點,如果后面跟"*",則清除所有斷點