每個(gè)人在調(diào)試中快速查找所需代碼時(shí)都有不同的方法,但是最基本最常用的有下面4種。
學(xué)習(xí)這4種方法之前我們需要思考一個(gè)問(wèn)題。根據(jù)前兩章內(nèi)容,我們已經(jīng)知道,運(yùn)行HelloWorld.exe程序會(huì)彈出一個(gè)消息框,顯示“Hello World!”信息。固然是因?yàn)槲覀兙帉懥舜a,可在這種情形下,只要運(yùn)行一下程序,不論是誰(shuí)都能輕松意識(shí)到這一點(diǎn)。
如果你熟悉win32 api的開(kāi)發(fā),看到彈出的消息框就會(huì)想到,這是調(diào)用MessageBox() API的結(jié)果。應(yīng)用程序的功能非常明確,只要運(yùn)行一下程序,就能大致推測(cè)出其內(nèi)部結(jié)構(gòu)。不過(guò)前提是你已經(jīng)具備了開(kāi)發(fā)和分析代碼的經(jīng)驗(yàn)。
我們需要查找的是main()函數(shù)中調(diào)用MessageBox()函數(shù)的代碼。在調(diào)試器中調(diào)試HelloWorld.exe(F8)時(shí),main()函數(shù)的MessageBox()函數(shù)在某個(gè)時(shí)刻就會(huì)被調(diào)用執(zhí)行,彈出消息對(duì)話框,顯示“Hello World!”這條信息。
【私信回復(fù)“資料”獲取】
上面就是代碼執(zhí)行方法的基本原理,當(dāng)程序功能非常明確時(shí),我們可以逐條執(zhí)行指令來(lái)查找需要查找的位置。但是代碼執(zhí)行法僅適用于被調(diào)試的代碼量不大、且程序功能明確的情況。倘若被調(diào)試的代碼量很大且比較復(fù)雜時(shí),此種方法就不再適用了。下面使用代碼執(zhí)行法來(lái)查找代碼中的main()函數(shù)。從“大本營(yíng)”(40104F)開(kāi)始,按F8鍵逐行執(zhí)行命令,在某個(gè)時(shí)刻彈出消息對(duì)話框,顯示“Hello World!”信息。按Ctrl+F2鍵再次載入待調(diào)試的可執(zhí)行文件并重新調(diào)試,不斷按F8鍵,某個(gè)時(shí)刻一定會(huì)彈出消息對(duì)話框。彈出消息對(duì)話框時(shí)調(diào)用的函數(shù)即為main()函數(shù)。如圖2-20所示,地址401144處有一條函數(shù)調(diào)用指令“CALL00401000”,被調(diào)用的函數(shù)地址為401000,按F7鍵(Step Into)進(jìn)入被調(diào)用的函數(shù),可以發(fā)現(xiàn)該函數(shù)就是我們要查找的main()函數(shù)。
如上圖,地址40100E處有一條調(diào)用MessageBox() API的語(yǔ)句。401002與401007處分別有一條PUSH語(yǔ)句,他把消息對(duì)話框的標(biāo)題與顯示字符串(Title = "www.reversecore.com", Text = "Hello World!")保存到棧(Stack)中,并作為參數(shù)傳遞給MessageBox W()函數(shù)。
這樣我們就準(zhǔn)確地查找到了main()函數(shù)。
win32應(yīng)用程序中,API函數(shù)的參數(shù)是通過(guò)棧傳遞的。VC++中默認(rèn)字符串是使用Unicode碼表示的,并且,處理字符串的API函數(shù)也全部變更為Unicode系列函數(shù)。
右鍵菜單-search for-all referenced text strings
OllyDbg初次載入待調(diào)試的程序時(shí),都會(huì)先經(jīng)歷一個(gè)預(yù)分析過(guò)程。此過(guò)程中會(huì)查看進(jìn)程內(nèi)存,程序中引用的字符串和調(diào)用的API都會(huì)被摘錄出來(lái),整理到另外的一個(gè)列表中,這樣的列表對(duì)調(diào)試時(shí)相當(dāng)有用的。使用all referenced text strings命令會(huì)彈出一個(gè)窗口,其中列出了程序代碼引用的字符串。
地址401007處有一條PUSH 004092A0命令,該命令中引用的004092A0處即是字符串“Hello World!”。雙擊字符串,光標(biāo)定位到main()函數(shù)中調(diào)用MessageBox W()函數(shù)的代碼處。
在OllyDbg的value窗口,然后按Ctrl+G命令,可以進(jìn)一步查看位于內(nèi)存4092A0地址處的字符串。首先使用鼠標(biāo)單擊value窗口,然后按Ctrl+G快捷鍵,打開(kāi)Enter expression to follow in Dump窗口。(如果你的數(shù)據(jù)窗口不是這樣的,右鍵單機(jī)選擇HEX)
ascii前兩行即使“Hello World!”字符串,它是以u(píng)nicode碼形式表示的,并且字符串的后面被填充上了NULL值(記住這塊null值的區(qū)域,我們以后還會(huì)再講的)。
VC++中,static字符串會(huì)被默認(rèn)保存為Unicode碼形式,static字符串是指在程序內(nèi)部被硬編碼的字符串。
上圖我們還需要注意的是4092A0這個(gè)地址,它與我們之前看到的代碼區(qū)域地址比如401XXX是不一樣的。在HelloWorld進(jìn)程中,409XXX地址空間被用來(lái)保存程序使用的數(shù)據(jù)。大家要清楚一點(diǎn):代碼和數(shù)據(jù)所在的區(qū)域是彼此分開(kāi)的。
我們后面會(huì)慢慢學(xué)習(xí)代碼和數(shù)據(jù)在文件里是怎么保存,以及怎么加載到內(nèi)存的,這些是windows PE文件格式的相關(guān)內(nèi)容,我們暫時(shí)先不管他。
3.1在調(diào)用代碼中設(shè)置斷點(diǎn)
右鍵單擊-search for-all intermodular calls
windows 編程中,如果想讓顯示器顯示內(nèi)容,則需要使用win32API向OS請(qǐng)求顯示輸出。換句話說(shuō),應(yīng)用程序向顯示器輸出內(nèi)容時(shí),需要在程序內(nèi)部調(diào)用win32 API。認(rèn)真觀察一個(gè)程序的功能后,我們能夠大致推測(cè)出它在運(yùn)行時(shí)調(diào)用的WIN32 API,則會(huì)為程序調(diào)式帶來(lái)極大便利。以HelloWorld.exe為例,它在運(yùn)行時(shí)會(huì)彈出一個(gè)消息窗口,由此我們可以推斷出該程序調(diào)用了user_32.MessageBox W() API。(敏感詞所以加了下劃線)
在OllyDbg的預(yù)分析中,不僅可以分析出程序中使用的字符串,還可以摘錄出程序運(yùn)行時(shí)調(diào)用的API函數(shù)列表。如果只想查看程序代碼中調(diào)用了哪些API函數(shù),可以直接使用all intermodular calls命令。如下圖窗口列出了程序中調(diào)用的所有API。
可以看到調(diào)用MessageBox W()的代碼,該函數(shù)位于40100E地址處,他是user_32.MessageBox W() API。雙擊它,光標(biāo)就會(huì)定位到調(diào)用它的地址處(40100E)。觀察一個(gè)程序的行為特征,若能事先推測(cè)出代碼中使用的API,則使用上述方法能夠幫助我們快速查找到需要的部分。
對(duì)于程序中調(diào)用的API,OllyDbg如何準(zhǔn)確摘錄出他們的名稱呢?首先,他不是通過(guò)查看源代碼來(lái)摘取的,如果想要了解其中的原理,我們需要理解PE文件格式的IAT(Import Address Table,導(dǎo)入地址表)結(jié)構(gòu)。我們會(huì)在后面的文章中提到這些內(nèi)容。
3.2在API代碼中設(shè)置斷點(diǎn)
鼠標(biāo)右鍵菜單-search for - name in all calls
OllyDbg并不能為所有可執(zhí)行文件都列出API函數(shù)調(diào)用列表。使用壓縮器、保護(hù)器工具對(duì)可執(zhí)行文件進(jìn)行壓縮或保護(hù)之后,文件結(jié)構(gòu)就會(huì)改變,此時(shí)OllyDbg就無(wú)法列出API調(diào)用列表了(甚至連調(diào)試都會(huì)變得十分困難)。
壓縮器(Run time Packer,運(yùn)行時(shí)壓縮器)
壓縮器是一個(gè)實(shí)用壓縮工具,能夠壓縮可執(zhí)行文件的代碼、數(shù)據(jù)、資源等,與普通壓縮不同,它壓縮后的文件本身就是一個(gè)可執(zhí)行文件。
保護(hù)器
保護(hù)器不僅具有壓縮功能,還添加了反調(diào)試、反模擬、反轉(zhuǎn)儲(chǔ)存等功能,能夠有效保護(hù)進(jìn)程。如果想仔細(xì)分析保護(hù)器,我們還需要具有更高級(jí)的逆向知識(shí)。
這種情況下,DLL代碼庫(kù)被加載到進(jìn)程內(nèi)存后,我們可以直接向DLL代碼庫(kù)添加斷點(diǎn)。API是操作系統(tǒng)對(duì)用戶應(yīng)用程序提供的一系列函數(shù),他們實(shí)現(xiàn)于C:\Windows\system32文件夾中的 *.dll文件(如kernel32.dll、user_32.dll、gdi32.dll、advapi32.dll、ws2_32.dll等)內(nèi)部。簡(jiǎn)單的說(shuō),我們編寫的應(yīng)用程序執(zhí)行某種操作時(shí)(如各種I/O操作),必須使用OS提供的API向OS提出請(qǐng)求,然后與被調(diào)用API對(duì)應(yīng)的系統(tǒng)DLL文件就會(huì)被加載到應(yīng)用程序的進(jìn)程內(nèi)存。
在OllyDbg菜單中選擇 View-Memory菜單(Alt+M),打開(kāi)內(nèi)存映射窗口。如下圖,內(nèi)存映射窗口中顯示了一部分HelloWorld.exe進(jìn)程內(nèi)存。在圖底部可以看到user_32庫(kù)被加載到了內(nèi)存。
使用OllyDbg中的Name in all modules命令可以列出被加載的DLL文件中提供的所有API。使用Name in all moudules命令打開(kāi)All names窗口,單機(jī)Name欄目按名稱排序,通過(guò)鍵盤敲出MessageBox W后,光標(biāo)會(huì)自動(dòng)定位到MessageBox W上。
USER_32模塊中有一個(gè)Export類型的MessageBoxW函數(shù)(不同環(huán)境下函數(shù)地址不同)。雙擊MessageBoxW函數(shù)后就會(huì)顯示其代碼,它實(shí)現(xiàn)于USER_32.dll庫(kù)中,如圖
觀察MessageBoxW函數(shù)的地址空間可以發(fā)現(xiàn),它與HelloWorld.exe使用的地址空間完全不同。在函數(shù)起始地址上按F2鍵,設(shè)置好斷點(diǎn)后按F9繼續(xù)執(zhí)行。
如果HelloWorld.exe應(yīng)用程序中調(diào)用了MessageBoxW() API,則調(diào)試時(shí)程序運(yùn)行到該處就會(huì)暫停。
與預(yù)測(cè)的一樣,程序執(zhí)行到MessageBoxW代碼的斷點(diǎn)處就停了下來(lái),此時(shí)寄存器窗口中的ESP值為19FF18
它是進(jìn)程棧的地址。在右下角的棧窗口中可以看到更詳細(xì)的信息
我們會(huì)在后面的教程中詳細(xì)說(shuō)明函數(shù)調(diào)用以及站動(dòng)作原理,現(xiàn)在可以暫時(shí)忽略
如上圖,ESP值的12FF18處對(duì)應(yīng)一個(gè)返回地址401014,HelloWorld.exe的main()函數(shù)調(diào)用完MessageBoxW函數(shù)后,程序執(zhí)行流將返回到該地址處。按Ctrl+F9快捷鍵使程序運(yùn)行到MessageBoxW函數(shù)的RETN命令處,然后按F7鍵也可以返回到401014地址處。地址401014的上方就是地址40100E,它正是調(diào)用MessageBoxW函數(shù)的地方。
以上就是快速查找代碼的4種方法。需要學(xué)習(xí)資料可以私信回復(fù)“資料”
時(shí)下如果語(yǔ)言界要評(píng)選一個(gè)網(wǎng)紅的話,無(wú)疑會(huì)是Rust。Rust憑借著自己出色的安全性和高效性被各大平臺(tái)所接納,Linux內(nèi)核,安卓底層開(kāi)發(fā),Windows底層開(kāi)發(fā)相繼都采納并給出了對(duì)應(yīng)的借口。微軟的Windows也是,最近發(fā)布了Rust for Windows v0.9。更新中包括全面的調(diào)用支持,Rust可以直接原生調(diào)用任何Windows API可以極大的拓展Rust在Windows下的開(kāi)發(fā)能力和范圍。本文我們通過(guò)實(shí)例來(lái)學(xué)習(xí)Rust for Windows(windows-rs) 。
本次更新提供了很多新的特性和更新,根據(jù)官方的信息包括:
添加了對(duì)Win32和COM API的支持,統(tǒng)一了Windows板條箱。這些Windows API有新的項(xiàng)目win32metadata來(lái)添加。為了方便和統(tǒng)一,項(xiàng)目名稱由“Rust/WinRT”更改為“Rust for Windows”。
添加了幾個(gè)示例 ,演示了如何調(diào)用各種Windows API(包括Win32,COM和WinRT API)。
Windows 板條箱在crates.io發(fā)布,現(xiàn)在支持MIT或Apache雙開(kāi)源版權(quán)。
內(nèi)置生成的binding,無(wú)需再手動(dòng)編寫。
Windows支持在Linux上構(gòu)建。
Win32 API的許多改進(jìn)和修復(fù),例如對(duì)數(shù)組類型,各種字符串類型和更新的元數(shù)據(jù)的支持。
添加了對(duì)COM接口的更自然和慣用的支持,例如返回值,以及對(duì)涉及C樣式聯(lián)合和嵌套類型之類的其他API的支持。
縮短了構(gòu)建時(shí)間并改善了錯(cuò)誤處理。
保留原始的API大小寫,這會(huì)影響使用Windows crate的現(xiàn)有代碼。通過(guò)類似于QueryInterface的函數(shù)轉(zhuǎn)換為通用函數(shù),從而可以更安全,更方便地調(diào)用許多與COM相關(guān)的函數(shù)。
Window板條箱使用需要首先要在Windows下配置Rust開(kāi)發(fā)環(huán)境,還在Rust環(huán)境的安裝也非常簡(jiǎn)單傻瓜化。
首先從Rust官方(rust-lang.org)下載安裝包rustup-init.exe(記得下載當(dāng)前Windows對(duì)應(yīng)的32位或者64位版本)。
然后直接執(zhí)行安裝包,安裝程序?yàn)樽詣?dòng)配置好系統(tǒng)路徑,以后就可以直接在命令行下使用了,比如cargo包管理器。
Windows下的rust編譯還依賴Microsoft C++ build tools工具,不安裝,后面在編譯時(shí),會(huì)報(bào)錯(cuò)說(shuō)“l(fā)ink.exe”無(wú)法找到。
需要從微軟VS下載出下載vs_buildtools,選中C++工具和Windows SDK組件并安裝。
另外,為了方便一般建議安裝VS Code及其Rust插件:
配置Rust 板條箱的國(guó)內(nèi)源,由于官方crates-io國(guó)內(nèi)下載太慢,甚至容易失敗,所以先配置國(guó)內(nèi)源(比如ustc)
在用戶目錄C:\Users\CC\.cargo創(chuàng)建一個(gè)config文件,內(nèi)容配置為:
首先,通過(guò)cargo創(chuàng)建一個(gè)新的Rust項(xiàng)目:
cargo new hello-chongchong
以上命令這將創(chuàng)建一個(gè)新目錄并hello-chongchong創(chuàng)建基本項(xiàng)目框架目錄和文件。
進(jìn)入該目錄,并使用--lib 命令嵌套創(chuàng)建依賴的庫(kù)項(xiàng)目:
cargo new --lib bindings
然后通過(guò)
code .
在VS Code打開(kāi)該項(xiàng)目,截圖如下:
修改項(xiàng)目Cargo.toml文件中,添加以下依賴項(xiàng),依賴項(xiàng)告訴Cargo現(xiàn)在它依賴于新創(chuàng)建的win庫(kù)。
[dependencies]
bindings = {path = " bindings"}
現(xiàn)在,在win文件夾下的Cargo.toml文件中,添加對(duì)Windows板條箱的依賴項(xiàng),版本指定為最新的0.9.1。這樣就可以通過(guò)允許Cargo打包下載,構(gòu)建和緩存Windows支持。
[dependencies]
windows = "0.9.1"
[build-dependencies]
windows = "0.9.1"
然后在bindings目錄下創(chuàng)建一個(gè)新的源文件build.rs,并輸入一下源碼:
// build.rs
fn main() {
windows::build!(
Windows::Win32::WindowsAndMessaging::MessageBoxA
);
}
在代碼中,使用 windows::build 宏指定要使用的類型,可以再次列出需要使用的所API,Windows板條箱將直接元數(shù)據(jù)生成必要的綁定。
然后修改win/src目錄中為以下代碼:
windows::include_bindings!();
這樣,就可以在主項(xiàng)目main.rs文件中,任意調(diào)用指定的Windows API。此處我們創(chuàng)建一個(gè)“Hello Chongchong!”消息對(duì)話框。
use bindings::Windows::Win32::WindowsAndMessaging::{MessageBoxA, MESSAGEBOX_STYLE};
fn main() {
unsafe {
MessageBoxA(None, "Hello Chongchong!", "Message", MESSAGEBOX_STYLE::MB_OK);
}
}
注意,任何Win32函數(shù)和COM接口方法,都需要用unsafe方式調(diào)用。
然后通過(guò)cargo build編譯該項(xiàng)目,并通過(guò)cargo run 會(huì)彈出一下對(duì)話框。
新版本的板條箱中再帶了幾個(gè)例子,可以在windows-rs項(xiàng)目倉(cāng)庫(kù)的examples目錄。
Rust for Windows 給rust在Windows開(kāi)發(fā)應(yīng)用帶來(lái)了福音,雖然一些API可能早就被廣泛地使用了,但是現(xiàn)在有了官方的支持,可以在文檔、示例和穩(wěn)定性等各方面都有極大的改善。