本系列教程主要講述Modern Windows Exploit Development。
學習本系列教程首先要求你熟悉x86匯編語言,并且因為根據本系列教程所講述的內容可以非常容易地進行相關內容的實踐性操作,所以你應該能較好地重現我進行過的相關實驗。在本系列教程的后幾部分,我們將會進行關于Internet Explorer 10和Internet Explorer 11的攻擊實驗。我的主要目的不僅是向你講述攻擊Internet Explorer的方法,還向你講述進行如此復雜的攻擊前首先需要進行的相關研究,進而讓你實現預期的目標。本系列教程有部分內容將會講述對Internet Explorer進行逆向工程的細節,進而會了解到對象在內存中的布局情況和根據我們已經了解到的情況來進行利用的方法。
我推薦你在64位的Windows 7 SP1 上創建兩臺虛擬機:一臺用于進行與Internet Explorer 10相關的實驗,另一臺用于進行與Internet Explorer 11相關的實驗。 我希望你喜歡本系列教程!
來源:http://expdev-kiuhnm.rhcloud.com/2015/05/17/windbg/
這篇文章描述windbg的一些重要命令及其最重要的選項.當然,我們在下篇文章中,當有需要時,也將了解到其他的命令及選項.
為避免出現問題:
請用32-bit版的WinDbg來調試32位的可執行程序;用64-bit版的WinDbg來調試64位的可執行程序。
打開某一WinDbg實例,如果你正使用Windbg調試某一進程,那么關閉WinDbg并將它重新打開)。?在File→Symbol File Path 里
輸入:SRV*C:\windbgsymbols*http://msdl.microsoft.com/download/symbols
保存工作區 (File→Save Workspace).
如上的星號是定義符。如上指定目錄為本地符號緩存目錄。paths/urls位于第二個星號后(如果有更多的paths/urls,那么使用‘;’分割)。用星號具體指定符號的位置。
要在調試時追加符號的搜索路徑,使用命令:.sympath+ c:\symbolpath
(使用的命令如沒有’+’,其作用是替換默認的搜索路徑)
重載符號表:.reload
如果需要了解模塊加載了哪些符號,使用命令:x *!
X命令支持使用通配符并可在搜索一個或多個模塊中的符號時使用.例如,我們可以搜索kernel32內帶有virtual字樣開頭的所有符號:
#!bash
0:000> x kernel32!virtual*
757d4b5f kernel32!VirtualQueryExStub (<no parameter info>)
7576d950 kernel32!VirtualAllocExStub (<no parameter info>)
757f66f1 kernel32!VirtualAllocExNuma (<no parameter info>)
757d4b4f kernel32!VirtualProtectExStub (<no parameter info>)
757542ff kernel32!VirtualProtectStub (<no parameter info>)
7576d975 kernel32!VirtualFreeEx (<no parameter info>)
7575184b kernel32!VirtualFree (<no parameter info>)
75751833 kernel32!VirtualAlloc (<no parameter info>)
757543ef kernel32!VirtualQuery (<no parameter info>)
757510c8 kernel32!VirtualProtect (<no parameter info>)
757ff14d kernel32!VirtualProtectEx (<no parameter info>)
7575183e kernel32!VirtualFreeStub (<no parameter info>)
75751826 kernel32!VirtualAllocStub (<no parameter info>)
7576d968 kernel32!VirtualFreeExStub (<no parameter info>)
757543fa kernel32!VirtualQueryStub (<no parameter info>)
7576eee1 kernel32!VirtualUnlock (<no parameter info>)
7576ebdb kernel32!VirtualLock (<no parameter info>)
7576d95d kernel32!VirtualAllocEx (<no parameter info>)
757d4b3f kernel32!VirtualAllocExNumaStub (<no parameter info>)
757ff158 kernel32!VirtualQueryEx (<no parameter info>)
在模塊部分使用通配符:
#!bash
0:000> x *!messagebox*
7539fbd1 USER32!MessageBoxIndirectA (<no parameter info>)
7539fcfa USER32!MessageBoxExW (<no parameter info>)
7539f7af USER32!MessageBoxWorker (<no parameter info>)
7539fcd6 USER32!MessageBoxExA (<no parameter info>)
7539fc9d USER32!MessageBoxIndirectW (<no parameter info>)
7539fd1e USER32!MessageBoxA (<no parameter info>)
7539fd3f USER32!MessageBoxW (<no parameter info>)
7539fb28 USER32!MessageBoxTimeoutA (<no parameter info>)
7539facd USER32!MessageBoxTimeoutW (<no parameter info>)
如想臨時改變策略,立刻將所有模塊的符號加載到WinDbg調試器,可以使用:ld*
這可能會花去一段時間.可通過 Debug→Break 來停止調試。
僅需輸入.hh
或按F1打開幫助窗口。用以下命令得到指定命令的幫助信息:
.hh <command>
<command>
為你想得到幫助信息的某個指定命令,或按F1,選擇Index(索引)來搜索命令,從而得到其幫助信息.
本地調試
可以調試某一新進程或某一正在運行的進程:
通過File→Open Executable
運行新進程以進行調試
通過File→Attach to a Process
附加到某一正運行的進程
遠程調試
至少使用如下兩個選項來遠程調試程序
1 如果你已在機器A上本地調試某一程序,那么使用如下命令(選擇你想要的端口):
.server tcp:port=1234
此時開啟服務器(WinDbg內).轉到 File→Connect to Remote Sessions
并輸入:
tcp:Port=1234,Server=<IP of Machine A>
來指定端口和IP.
2 在機器A,用如下命令運行dbgsrv:
dbgsrv.exe -t tcp:port=1234
即可以在機器A啟動服務器.
在機器B運行Windbg,接著File→Connect to Remote Stub
,輸入
tcp:Port=1234,Server=<IP of Machine A>
這里需要設置適當的參數。
你將看到File→Open Executable
已無法選擇,但你可以通過File→Attach to a process
附加到進程 .這時可在機器A上看到進程列表。
如果要在機器A停止服務器,可用Task Manager(任務管理器)接著kill dbgsrv.exe。
當你加載某一可執行程序或附加到某一進程時,WinDbg將列出已加載的模塊.如果你要再次列出模塊,那么可輸入:lmf 列出指定模塊(ntdll.dll),可用: lmf m ntdll
得到模塊(ntdll.dll)的鏡像頭部信息: !dh ntdll
帶有‘!’符號的命令為擴展命令,這里的作用是顯示指定模塊的詳細信息,等等。從某一外部DLL中導出某一外部命令,并且WinDbg內部會調用該命令。用戶可創建他們自己的擴展程序來擴展WinDbg的功能。
當然了,你也可以使用模塊的起始地址:
#!bash
0:000> lmf m ntdll
start end module name
77790000 77910000 ntdll ntdll.dll
0:000> !dh 77790000
WinDbg支持使用表達式,這意味著,當需要某一值時,你可直接輸入該值或輸入與該值等價的表達式。例如,如果EIP是77c6cb70
,那么bp77c6cb71
和bp EIP+1
等價。
你也可以使用符號:u ntdll!CsrSetPriorityClass+0x41
和寄存器:dd ebp+4
數字默認用base 16
表示,添加前綴來明確使用的base所表示的進制格式:
#!bash
0x123: base 16 (hexadecimal)? 0n123: base 10 (decimal)? 0t123: base 8 (octal)? 0y111: base 2 (binary)
用命令.format來展示某一值的多種格式
#!bash
0:000> .formats 123
Evaluate expression:
Hex: 00000000`00000123
Decimal: 291
Octal: 0000000000000000000443
Binary: 00000000 00000000 00000000 00000000 00000000 00000000 00000001 00100011
Chars: .......#
Time: Thu Jan 01 01:04:51 1970
Float: low 4.07778e-043 high 0
Double: 1.43773e-321
用’?’來對某個表達式求值,例如:? eax+4
在WinDbg中可支持多種偽寄存器(含有某些值). 用前綴‘$‘來指明其是偽寄存器.在使用寄存器或偽寄存器時,[email protected],[email protected]?器而不是某一符號。
這有一些偽寄存器的范例:
$teb
或 @$teb
(TEB的地址)
$peb
或 @$peb
(PEB的地址)
$thread
或 @$thread
(當前線程)
用sxe命令可中斷某一特定的異常.例如,中斷某一已被加載的模塊,可輸入:
sxe ld <module name 1>,...,<module name N>
例如,
sxe ld user32
查看異常類型的列表:sx
用sxi
命令忽略某一異常: sxi ld
使用該命令可讓第一次輸入的命令失效。
執行到single-chance和second-chance的異常處將會使Windbg中斷 。它們并非是不同的異常類型。執行到異常處時,WinDbg將停止執行 ,并提示該位置為single-chance異常。 Single-chance意味著異常事件還沒被發送到被調試的程序。當我們恢復執行時,WinDbg將異常事件發送到被調試的程序。如果被調試程序不處理異常,WinDbg將再次停止執行并提示此處為second-chance異常。
在我們測試EMET5.2時,我們需要忽略single-chance的單步異常(single step exceptions)。用如下命令實現: sxd sse
軟件斷點:
在某指令上設置斷點時,WinDbg將指令的第一字節保存于內存并用0xCC覆蓋它(操作碼為”int 3”)。
當“int 3”指令被執行時,斷點即被觸發,那么執行將會被停止,且WinDbg通過重置它的首字節來重置該指令。
輸入如下命令在位于0x4110a0地址的指令上設置斷點:
bp 4110a0
第三次運行時激活0x4110a0地址的斷點:
bp 4110a0 3
恢復執行(并在第一次觸發的斷點上停止)輸入如下:g
這是“go“的縮寫.?運行直到到達某地址 (含有代碼 ), 輸入:g <code location>
WinDbg內將會在指定的位置上設置軟件斷點(如‘bp’),但此處的斷點被觸發后將會被刪除.主要原因是使用‘g’設的是一次性軟件斷點.
硬件斷點
使用特定的CPU寄存器設置硬件斷點,它比軟件斷點更通用.事實上,它可中斷執行或內存訪問.硬件斷點不會修改任意代碼,甚至帶有self modifying code。不幸的是,最多只能下4個硬件斷點。
最簡單的形式如下,命令格式為:
ba <mode> <size> <address> <passes (default=1)>
<mode> 可以是
‘e‘ (用于執行
‘r‘ (用于讀取存儲器
‘w‘ (用于寫存儲器
<size>
是監控訪問(當<mode>
是‘e’時,它總為1)指明位置的大小,其以字節的形式表示。?<address>
為設置斷點的位置,<passes>
激活斷點時(查看’bp’用法的范例)需要的傳遞數,其起到計數器的作用.
筆記:在運行某一進程前,該進程不可能使用硬件斷點。因為通過修改CPU寄存器(dr0,dr1,等等…)可以設置硬件斷點,在開啟進程及它的線程被創建時,寄存器將會被重置。
處理斷點
列出斷點類型:bl
‘bl’表示斷點列表(breakpoint list).?例如:
0:000> bl
0 e 77c6cb70 0002 (0002) 0:**** ntdll!CsrSetPriorityClass+0x40
區域的位置,從左到右表示如下:
0:斷點ID
e: 斷點狀態,可以設置(enabled)或關閉(disabled).
77c6cb70: 內存地址
0002(0002): 在激活前余下的傳遞數(起到計數器作用),利用所有傳遞數來等待激活(當斷點被創建時,將會指定該值) 0:***|*: 相關聯的進程和線程.用星號代表該斷點不是thread-specific。
ntdll!CsrSetPriorityClass+0x40: 設置斷點的位置(模塊, 函數和偏移)
關閉(disable)某一斷點
bd <breakpoint id>
刪除斷點
bc <breakpoint ID>
刪除所有斷點
bc *
斷點命令
每次某個斷點被觸發后將自動執行某個命令,可以使用如下命令:
bp 40a410 ".echo \"Here are the registers:\n\"; r"
另一個范例:自定義命令如下:
bp jscript9+c2c47 ".printf \"new Array Data: addr = 0x%p\\n\",eax;g"
逐步執行有至少三種類型:
步進/跟蹤(命令:t) 該命令中斷每條指令的執行.如果執行到call指令或int指令,那么該命令將各自在調用函數的第一條指令或int handler上中斷。 步過 (命令: p) 該命令能讓每條指令(沒有calls或ints,等等)執行后中斷,如果你剛好執行到call或int指令,那么會在call或int指令執行后中斷 步出 (命令: gu) 該命令(go up) 能讓WinDbg恢復程序的執行,并且能在下一條ret指令執行后中斷。在exit函數中經常使用到該命令。
還有其它兩個用于exit函數的命令:
tt (trace to next return):等價于重復使用’t’命令并且在執行過程中遭遇的第一條ret指令上停止執行。 pt (step to next return):等價于重復使用‘p’命令并且在執行過程中遭遇的第一條ret指令上停止執行。
記錄:使用tt命令會執行到函數內,如果你想到達當前函數的ret指令,那么改為使用pt命令。 pt和gu命令的不同點在于,使用pt命令將會在ret指令上中斷,使用gu命令將會在ret指令后的下一條指令上中斷。
這里是包含‘p‘ 和‘t‘命令的不同形式:
pa/ta <address>: step/trace 到地址。
pc/tc: step/trace 到 下一條 call/int 指令。
pt/tt: step/trace 到下一條 ret (discussed above at point 3)指令。
pct/tct: step/trace 到下一 條call/int 或 ret指令。
ph/th: step/trace 到下一分支的指令。
可使用‘d’或它的變量中的其中一種類型來展示(display)內存中的內容,
db: display bytes
dw: display words (2 bytes)
dd: display dwords (4 bytes)
dq: display qwords (8 bytes)
dyb: display bits
da: display null-terminated ASCII strings
du: display null-terminated Unicode strings
輸入 .hh d 來查看其它變量。 ‘d’命令用相同的格式展示數據,正如大多數的d*命令那樣(或如果不是單一數據則使用db)。
這些命令的(簡化)格式為:d* [range]
這里,使用星號來描繪我們已列出的如上所有的變化,并且方框內應指明所選的范圍。如果沒有選好范圍,那么在使用d*命令展示一部分數據后,將展示內存部分的數據。
可以用許多種方式指定范圍:
<start address> <end address>
? 范例,db 77cac000 77cac0ff
<start address> L<number of elements>
? 范例, dd 77cac000 L10
查看 10 dwords(始于 77cac000地址).?Note: 因為范圍比256 MB還要大,我們必須使用L?而不是L來指定行數。<start address>
要編輯(edit)內存,使用:
e[d|w|b] <address> [<new value 1> ... <new value N>]
[d|w|b]是相關選項,它指定編輯的元素類型(d = dword, w = word, b = byte)。 如果新值被省略了,那么你在WinDbg中可以交互式地輸入它們。
這是范例:ed eip cc cc
用值0xCC來覆蓋地址(在eip內)上的頭兩個dwords。
使用‘s’命令來搜索內存。它的格式為:
s [-d|-w|-b|-a|-u] <start address> L?<number of elements> <search values>
d,w,b,a,u
分別代表dword, word, byte, ascii
和 unicode.
<search values>
是序列值(用于搜索)
例如:
s -d eip L?1000 cc cc
在內存區間內搜索兩個連續的dwords 0xcc 0xcc
。[eip, eip + 1000*4 – 1]
。
使用如下命令解引用某個指針:
dd poi(ebp+4)
用該命令,poi(ebp+4)對地址ebp+4求值,其結果的類型為dword或qword(在64位模式下)。
查看寄存器信息,輸入如下:r
查看特定寄存器信息,例如eax和adx,輸入:r eax, edx
打印前三行EIP指向的指令,用命令如下:u EIP L3
‘u‘ 是unassemble的縮寫并且‘L‘可讓指定你想查看信息的行數.列出調用棧(call stack)可以使用k
如下是一些查看結構體信息的命令:
創建窗口后保存工作區(File→Save Workspace)