新聞中心
在以前的時(shí)代,對(duì)軟件來(lái)進(jìn)行下(匯編級(jí))逆向工程確實(shí)是一個(gè)很繁瑣的過(guò)程,但是如今現(xiàn)代反編譯器的發(fā)展已經(jīng)把這個(gè)過(guò)程變得容易了。只需要在編譯過(guò)后的機(jī)器代碼中使用反編譯器的功能就可以把機(jī)器代碼嘗試恢復(fù)到近似于軟件以前的源代碼級(jí)別。

專注于為中小企業(yè)提供成都做網(wǎng)站、成都網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)麻江免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
不可否認(rèn)的是,支持反匯編功能的反編譯器的這種技術(shù)它背后的科學(xué)和便利性是很值得贊賞的。就像這樣,在點(diǎn)擊功能選項(xiàng)時(shí),一個(gè)完完全全的新手可以將難懂的“機(jī)器代碼”轉(zhuǎn)換成人類可讀的代碼,然后就能上手逆向工程了,你說(shuō),驚不驚訝?
然而現(xiàn)實(shí)情況是,安全研究人員也越來(lái)越依賴于這些技術(shù),雖然工欲善其事必先利其器這句話沒(méi)錯(cuò),但是越依賴工具,這將使我們更加地暴露在這些工具的不完善之處。在這篇文章中,我將探討一些和反編譯器相關(guān)的破壞或有目的性地會(huì)誤導(dǎo)逆向工程師的反反編譯技術(shù)。
Positive SP Value
第一種技術(shù)是能破壞Hex-Rays反編譯器的經(jīng)典方法,在IDA Pro中,如果在返回之前沒(méi)有清理堆棧分配(平衡堆棧指針),則反編譯器將拒絕反編譯該函數(shù)。
這樣的情況一般是程序代碼有一些干擾代碼,讓IDA的反匯編分析出現(xiàn)錯(cuò)誤。比如用push + n條指令 + retn來(lái)實(shí)際跳轉(zhuǎn),而IDA會(huì)以為retn是函數(shù)要結(jié)束,結(jié)果它分析后發(fā)現(xiàn)調(diào)用棧不平衡,因此就提示sp analysis failed。
例如當(dāng)IDA無(wú)法合理地構(gòu)造出某些函數(shù)調(diào)用時(shí)的定義類型時(shí)偶爾也會(huì)發(fā)生這種情況,作為反反編譯技術(shù),開(kāi)發(fā)人員可以通過(guò)使用一些特殊的手法來(lái)破壞堆棧指針的平衡,以此誘導(dǎo)逆向者來(lái)出現(xiàn)這些效果。
- //
- // compiled on Ubuntu 16.04 with:
- // gcc -o predicate predicate.c -masm=intel
- //
- #include
- #define positive_sp_predicate \
- __asm__ (" push rax \n"\
- " xor eax, eax \n"\
- " jz opaque \n"\
- " add rsp, 4 \n"\
- "opaque: \n"\
- " pop rax \n");
- void protected()
- {
- positive_sp_predicate;
- puts("Can't decompile this function");
- }
- void main()
- {
- protected();
- }
上面定義add rsp, 4的positive_sp_predicate宏中的指令永遠(yuǎn)不會(huì)在運(yùn)行時(shí)被執(zhí)行,但是它會(huì)使IDA進(jìn)行反編譯時(shí)的靜態(tài)分析失敗。當(dāng)試圖反編譯protected()提供的生成函數(shù)會(huì)產(chǎn)生以下結(jié)果:
這種技術(shù)是比較有名的,可以通過(guò)修補(bǔ)缺陷來(lái)修復(fù),也可以通過(guò)手動(dòng)修正堆棧偏移值來(lái)修復(fù)。
在MBE中,有使用這種技術(shù)作為一個(gè)簡(jiǎn)單的技巧來(lái)阻止新手逆向工程師(例如學(xué)生)來(lái)進(jìn)行反匯編并能直接讓反編譯器輸出軟件的源代碼來(lái)。
返回型劫持
現(xiàn)代反編譯器希望的是能準(zhǔn)確地識(shí)別和抽象出編譯器生成的低級(jí)的能記錄的邏輯信息,例如功能的開(kāi)頭/結(jié)尾或能控制的流(元)數(shù)據(jù)部分。
反編譯器力圖從輸出中來(lái)省略這些信息,因?yàn)楸4孢@些寄存器或管理堆棧幀分配的任務(wù)并不會(huì)在反編譯器輸出軟件源代碼時(shí)得到執(zhí)行。
這些遺漏(或者是Hex-Rays反編譯器啟發(fā)式方法中的一個(gè)缺陷)的一個(gè)有趣的地方是我們可以在函數(shù)返回之前來(lái)“移動(dòng)”棧,使得反編譯器不發(fā)出警告或者也不顯示任何帶有惡意的指示。
Stack pivot 是二進(jìn)制開(kāi)發(fā)中常用的技術(shù),可以實(shí)現(xiàn)任意的ROP。在這種情況下,我們(作為開(kāi)發(fā)人員)使用它作為一種手段,來(lái)從不知情的逆向工程師手中劫持到執(zhí)行權(quán)??梢哉f(shuō),那些專注于反編譯器輸出結(jié)果的人肯定不會(huì)注意到它,哈哈。
我們把這個(gè)堆棧轉(zhuǎn)換成一個(gè)很小的ROP鏈,這個(gè)鏈已經(jīng)被編譯成二進(jìn)制文件來(lái)執(zhí)行這個(gè)錯(cuò)誤操作了。最終結(jié)果是一個(gè)對(duì)反編譯器“不可見(jiàn)”的函數(shù)調(diào)用。圖中我們調(diào)用函數(shù)的目的只是打印出“惡意代碼”來(lái)證明它已經(jīng)被執(zhí)行。
圖: 利用返回劫持反編譯技術(shù)執(zhí)行編譯后的二進(jìn)制文件
用于演示這種從反編譯器中隱藏代碼的技術(shù)的代碼可以在下面找到
- //
- // compiled on Ubuntu 16.04 with:
- // gcc -o return return.c -masm=intel
- //
- #include
- void evil() {
- puts("Evil Code");
- }
- extern void gadget();
- __asm__ (".global gadget \n"
- "gadget: \n"
- " pop rax \n"
- " mov rsp, rbp \n"
- " call rax \n"
- " pop rbp \n"
- " ret \n");
- void * gadgets[] = {gadget, evil};
- void deceptive() {
- puts("Hello World!");
- __asm__("mov rsp, %0;\n"
- "ret"
- :
- :"i" (gadgets));
- }
- void main() {
- deceptive();
- }
濫用 ‘noreturn’ 函數(shù)
我們將介紹的最后一個(gè)技巧是利用IDA的自動(dòng)感知功能將函數(shù)標(biāo)記為noreturn,因?yàn)槊恳粋€(gè)的noreturn函數(shù)將會(huì)表示為從標(biāo)準(zhǔn)庫(kù)來(lái)的exit()或者abort()這些函數(shù)。
在生成給定函數(shù)的偽代碼時(shí),反編譯器會(huì)在調(diào)用noreturn函數(shù)后丟棄任何代碼。能預(yù)計(jì)到的是即使使用的是exit()函數(shù),對(duì)于其他任何一個(gè)函數(shù)它都不會(huì)返回并繼續(xù)執(zhí)行代碼。
圖:直接在調(diào)用noreturn函數(shù)之后的代碼對(duì)于反編譯器是不可見(jiàn)的
如果惡意攻擊者可以欺騙IDA讓它相信一個(gè)函數(shù)是noreturn,但實(shí)際上這個(gè)函數(shù)它并不是noreturn的時(shí)候,那么這個(gè)惡意行為者可以悄悄地將惡意代碼隱藏起來(lái)。
下面的例子演示了我們可以通過(guò)多種方法實(shí)現(xiàn)這個(gè)效果。
- //
- // compiled on Ubuntu 16.04 with:
- // gcc -o noreturn noreturn.c
- //
- #include
- #include
- void ignore() {
- exit(0); // force a PLT/GOT entry for exit()
- }
- void deceptive() {
- puts("Hello World!");
- srand(0); // post-processing will swap srand() <--> exit()
- puts("Evil Code");
- }
- void main() {
- deceptive();
- }
通過(guò)編譯上面的代碼,并根據(jù)生成的二進(jìn)制文件運(yùn)行一個(gè)簡(jiǎn)短的基于二進(jìn)制的后期處理腳本,我們可以在過(guò)程連接表中交換推送的序號(hào)。這些索引用于軟件在運(yùn)行時(shí)解析庫(kù)的導(dǎo)入。
在這個(gè)例子中,我們交換了srand()與exit()的序號(hào)。因此,IDA認(rèn)為deceptive()修改后的二進(jìn)制文件中的exit()的noreturn函數(shù)才是調(diào)用函數(shù),而srand()不是調(diào)用函數(shù)。
我們?cè)贗DA中看到exit()被調(diào)用,而srand()在運(yùn)行,事實(shí)上srand()是不可控的。對(duì)反編譯器的影響程度幾乎與上一節(jié)所描述的返回劫持技術(shù)相同。運(yùn)行的二進(jìn)制文件表明我們的“惡意代碼”也正在執(zhí)行,而反編譯器對(duì)此卻并不知情。
雖然在這些例子中存在惡意代碼,但將這些技術(shù)使用在具有更大的功能和復(fù)雜的條件下時(shí),將使得它們非常容易上手,并造成更大危害。
結(jié)論
反編譯器是一個(gè)令人印象很深刻但卻又不完善的技術(shù)。它在不完整的信息上來(lái)進(jìn)行一些操作,盡其所能地來(lái)輸出接近于我們認(rèn)知的軟件源代碼。惡意行為者同時(shí)可以(也將會(huì))利用這些不對(duì)稱的技術(shù)手段來(lái)作為欺騙手法去對(duì)用戶進(jìn)行一些惡意攻擊(行為)。
隨著行業(yè)越來(lái)越依賴于反編譯器(工具),反反編譯技術(shù)的采用將會(huì)與反調(diào)試一樣地快速增加和發(fā)展起來(lái),謝謝閱讀。
名稱欄目:慎點(diǎn)!來(lái)自反編譯器的危險(xiǎn)
網(wǎng)站地址:http://fisionsoft.com.cn/article/cdgsdps.html


咨詢
建站咨詢
