基本知識和常用命令
(1) Windbg下載地址http://msdn.microsoft.com/en-us/windows/hardware/gg463009.aspx
安裝完后執行windbg –I將Windbg設置成默認調試器
(2) Windbg的命令分為標準命令,原命令和擴展命令,輸入問號(?)可以顯示所有的標準命令的幫助信息; 元命令以一個點(.)開始,輸入.help可以顯示所有的原命令的幫助信息;擴展命令以嘆號(!)開始。
所有命令的具體用法可以通過F1查看Windbg的幫助文件。
(3) 通過設置符號文件路徑,讓Windbg自動從微軟網站更新系統Dll的符號文件
SRV*d:\symbols* http://msdl.microsoft.com/download/symbols
(4) 用分號(;)作為分隔符,可以在一行輸入多條命令
(5) 按上下箭頭可以瀏覽和選擇以前輸入過的命令
(6) Ctrl+Break終止一個很長時間沒有完成的命令, Ctrl+Break也可以讓正在運行的程序暫停
(7) Windbg默認的數值進制一般是16, 可以通過n命令查看和設置當前進制,所以我們一般在數值里帶上進制, 0n(十進制), 0x(十六進制), 0t(8進制), 0y(2進制), 比如0n20表示20, 0x14表示20等
(8) 可以通過問號命令(?)顯示表達式值,雙問號(??)顯示C++表達式值, 通過.cls命令清屏
(9) 如果x表示的一個地址, 則可以通過以下方法獲取x所指向的值
hi(x) 高16 bits
low(x) 低16 bits
by(x) 返回第一個byte
wo(x) 返回第一個word
dwo(x) 返回第一個dword
qwo(x) 返回第一個4 word(Quad-word)
poi(x) 返回第一個指針值
(10) 函數調用如果還沒開始,即一般函數入口代碼
push ebp
mov ebp, esp
還未執行,
則[esp+4]表示第一參數值, [esp+8]表示第二參數,以此類推, [esp]表示的是返回地址
如果上面的入口代碼已經執行,則一般通過ebp來獲取函數參數和局部變量
[ebp+8]表示第一參數, [ebp+0xC]表示第二個參數, 以此類推,[ebp+4]表示返回地址 [ebp]表示上一堆棧楨的基地址。
[ebp-4]表示函數第一個局部變量
(11) 條件表達式
j<條件表達式>[Command1];[Command2]
例如bp consoletest!add "j(dwo(esp+4)==0n10) 'kv;.echo \"break\"';'g'" 表示條件斷點,如果consoletest!add的第一個參數是10, 則打印堆棧,輸出”break”, 并暫停,否則繼續執行
也可用元命令代替
bp consoletest!add ".if(dwo(esp+4)==0n10) {kv;.echo \"break\"} .else {g}"
(12) 上下文
上下文(Context)包括會話(Session)上下文, 進程上下文,寄存器上下文,局部變量上下文。
會話上下文和登陸用戶帳號有關。進程上下文和當前調試的默認進程有關, 寄存器上下文和當前默認線程有關。
局部變量上下文和當前的堆棧楨有關, 比如可以通過.frame [index] 切換當前堆棧楨,然后通過dv 顯示當前堆棧楨函數的局部變量(堆棧楨的index從0開始,可以通過kn命令顯示堆棧楨索引)
(13) 保存dump文件
.dump /ma c:\test.dmp 保存full-dump
.dump /m c:\test.dmp 保存mini-dump
(14) 分析Dump
一般先 !analyze –v Windbg會根據上面命令自動分析,
然后 ~* kv 打印所有線程的堆棧
(15) 重新加載符號文件
.reload –f [name], 強制重新加載某個模塊的符號文件
比如 .reload –f test.dll
(16) 察看模塊信息
lm顯示所有模塊信息
lmf 顯示所有模塊及其路徑
lmD 顯示所有模塊詳細信息
!lmi [module name] 顯示某一模塊的詳細信息
(17) 分析調試符號
X [選項] 模塊名!符號名
比如x ntdll!dbg*顯示所有ntdll模塊中以dbg開頭的符號
比如x test!cmyclass::init 顯示test模塊中cmyclass類中的init函數符號
(18) 搜索符號
ln [address] 搜索離address最近的符號名(list nearest symbols)
(19) 事件處理
可以通過菜單Debug->Event Filter…設置
sx 顯示各個事件的代碼和目前的處理選項
sx {e|d|i|n} [command] , e|d|i|n分別對應Enabled, Disabled,Output和Ignore
比如 sxe ld user32.dll 表示在加載user32.dll時設置的中斷
sxr 恢復成默認設置
(20) 單步調試
g 繼續運行(go), 熱鍵F5
t 單步越過(step over), 熱鍵F10
p 單步進入(step into), 熱鍵 F11
(21) 設置斷點(break point)
bp [address] [“command”] 設置軟件斷點。
比如 bp kernel32!CreateProcessW表示在調用這個CreateProcess時設置斷點。
比如bp kernel32!CreateFileW "du poi(esp+4); g" 表示在調用CreateFile時打印出文件路徑(第一個參數),然后繼續執行
針對某線程設置斷點,只要在命令前加~線程號:
比如 ~0 bp 0x441242, 表示0號線程執行到地址0x441242時中斷
ba [access size] [command]設置硬斷點。
其中,access指定訪問方式(e執行指令, r讀取數據,w寫入數據)
size 表示監視數據的大小(1, 2, 4)
比如ba r4 0x414422, 表示在地址0x414422寫入4字節數據是觸發斷點
(22) 管理斷點
bl 列出所有當前斷點的狀態
bc 清除斷點, bc * 清除所有斷點, bc 0 清除0號斷點
bd 禁用某個斷點(disable)
be 打開某個斷點(enable)
(23) 察看堆棧
kn [frame count]察看當前堆棧及其索引, frame count指定要顯示多少楨
kb顯示堆棧楨地址,返回地址,參數,函數名等
kv在kb的基礎上增加了函數調用約定等信息, 所以推薦用kv命令察看堆棧.
.frame [frame index] 將當前堆棧切換到某個堆棧楨, 比如.frame 1 切換到第1楨
dv 命令察看當前堆棧楨的局部變量
(24) 察看和修改寄存器
r顯示所有寄存器的值
r eax=0x100 將eax寄存器的改成0x100
(25) 顯示內存區域(dump memory)
d{a|b|d|D|f|q|u|w} [range]
其中a表示ASCII碼,b表示byte, d表示DWORD, D表示double, f表示float, q表示8字節, u表示Unicode String, w表示word
Range 表示地址范圍,可以用2種表示:一是起始地址加終止地址, 二是起始地址加L長度(不是字節長度,是單位長度)。
比如 dw 77e0d827 L10 表示顯示77e0d827開始的10個word
比如 dd 77e0d820 77e0d844, 表示顯示 77e0d820 和77e0d844之間的所有dword
比如 du 77e0d820, 表示77e0d820開始的以0結尾的字符串
dps [range] 顯示某一地址范圍內的符號(display word and symobols)
(26) 顯示數據類型(dump symbolic type information)
dt [模塊名!]類型名
dt testApp!g_appInstance 表示顯示testApp里全局變量g_appInstance的內存布局
dt 0x0458e850 test!CMyApp 表示將地址0x0458e850以test!CMyApp類地址解析,并打印內存布局, 所以只有私有符號才有這個功能
如果當前堆棧楨是在某個類函數內,可以通過dt this 打印當前類的內促布局。
(27) 搜索內存(search memory)
s –[type] range pattern
其中type, b表示byte, w表示word, d 表示dword, a表示ASCII string,u表示unicde string
Range 表示地址范圍,可以用2種表示:一是起始地址加終止地址, 二是起始地址加L長度(不是字節長度,是單位長度)。如果搜索空間長度超過256M,用L?length。
Pattern指定要搜索的內容.
比如 s -u 522e0000 527d1000 "web"表示在522e0000 和527d1000之間搜索Unicode 字符串”web”
比如s -w 522e0000 L0x100 0x1212 0x2212 0x1234 表示在起始地址522e0000之后的0x100個單位內搜索0x1212 0x2212 0x1234系列的起始地址
(28) 修改內存 (edit memory)
e{a|u|za|zu} address “String”
其總za和zu表示以0結尾的Ascii和Unicode字符串, a和u則表示沒有0結尾
比如 ezu 0x445634 “abc” 表示在0x445634地址寫如unicode 字符串abc
比如ea 0x445634 “abc” 表示在0x445634地址寫入Ascii字符串abc, 不包含結束符0
e{a|b|d|D|f|q|u|w} address [values]
其中a表示ASCII碼,b表示byte, d表示DWORD, D表示double, f表示float, q表示8字節, u表示Unicode String, w表示word
比如eb 0x123432 0x41 0x41 0x41 表示在地址0x123432 寫入3個0x41
(29) 觀察內存屬性
!address [address]
比如!address 0x414453, 顯示地址0x414453所在區域的內存屬性
!heap -h 顯示所有的內存堆(heap)
(30) 反匯編某一地址
u address, 比如u 0x410040表示反匯編地址0x410040的代碼
uf 反匯編某個函數, 比如uf test!main
ub 反匯編某地址之前的代碼,比如ub 0x 0x410040 L20
(31) 進程線程控制
~*命令顯示當前所有線程的詳細信息
~[Index] n增加索引為Index的線程的掛起計數
~[Index] m減少索引為Index的線程的掛起計數
比如通過~2 n 增加2號線程的掛起計數后, 執行g命令(繼續運行), 這時2號線程會依然暫停運行。
~[Index] f 凍結某一線程的執行
~[Index] u 解凍某一線程的執行
~[Index] g只運行線程號為index的線程
~[Index] s 切換當前線程
比如 ~2 kv; ~2 r 可以打印2號線程的當前堆棧和寄存器
~* kv可以打印所有線程堆棧。
!runaway 顯示所有線程的CPU消耗
|. 顯示當前調試進程
|* 顯示當前調試中的所有進程
|[nIndex] s 切換當前調試進程
!peb 顯示進程信息塊(process environment block)
!teb 顯示線程信息塊(thread environment block)
(32) 線程死鎖
!locks 顯示死鎖
!handle 列出當前進程所handle
!handle [index] f, 顯示某個handle的詳細信息
(33) 自動調試子進程
.childdbg 0
Disable child process debugging
.childdbg 1
Attach child process automatically
(34) 腳本支持
$$>< filename 加載腳本文件,并將腳本里的換行符自動換成;(分號)
$t0~$t19為偽寄存器,可用來存儲臨時值, 使用偽寄存器時前面盡量加@符號以加快搜索
.printf 可以輸出格式化信息
as Name EquivalentLine 別名,類似define宏 , 比如 r $t0=poi(esp+4); as $filename $t0
ad Name 刪除別名, ad*刪除所有別名
al 列出所有別名
.block {…}重新開始替換里面的所有別名
${alias}強制要求替換里面的別名, ${/v:alias}不要替換里面的別名
運用別名的腳本樣例:
bp Kernel32!CreateFileW "
r $t0=poi(esp+4)
as /mu ${/v:$fileName} @$t0
.block
{
.if( $sicmp(\"${$fileName}\", \"C:\\11.txt\")==0)
{
.echo OK
.printf \"done:%mu\", @$t0
}
.else
{
.echo error
}
}
ad ${/v:$fileName}
gc
"
FAQ
(1) 如何在某個窗口收到某個消息時設置斷點? 比如在我想在某窗口收到系統最小化消息時設置斷點,該怎么操作?
其實就是監視窗口處理函數MsgProc(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0)消息:首先通過Spy察看窗口句柄, 比如為0x350d72; 通過Spy也可以看到窗口消息處理函數的地址,比如00E814DE; 察看WM_SYSCOMMAND的值(0x0112); 察看SC_MINIMIZE的值0xF020, 因此我們可以寫入如下條件斷點:
bp 0xE814DE
"j((dwo(esp+4)==0x350d72)&(dwo(esp+8)==0x0112)&(dwo(esp+0xc)==0xF020)) ‘kv’;’g’"
上面的命令表示在調用窗口函數時如果符合我們的條件,則打印堆棧(kv)并暫停,
否則繼續執行(g).
(2) 堆棧楨的含義
