反汇编算法介绍和应用——线性扫描算法分析
? ? ? ??做過逆向的朋友應(yīng)該會(huì)很熟悉IDA和Windbg這類的軟件。IDA的強(qiáng)項(xiàng)在于靜態(tài)反匯編,Windbg的強(qiáng)項(xiàng)在于動(dòng)態(tài)調(diào)試。往往將這兩款軟件結(jié)合使用會(huì)達(dá)到事半功倍的效果。可能經(jīng)常玩這個(gè)的朋友會(huì)發(fā)現(xiàn)IDA反匯編的代碼準(zhǔn)確度要高于Windbg,深究其原因,是因?yàn)镮DA采用的反匯編算法和Windbg是不同的。下面我來說說我所知道的兩種反匯編算法。(轉(zhuǎn)載請(qǐng)指明來自breaksoftware的csdn博客)
? ? ? ? 1 線性掃描(Linear sweep)
? ? ? ??線性掃描是一種非?;A(chǔ)的反匯編算法。看到“線性”二字,我們腦海里可能會(huì)立馬浮現(xiàn)出一個(gè)指針對(duì)一段內(nèi)存中數(shù)據(jù)從開始到最后進(jìn)行一次遍歷的場(chǎng)景。而實(shí)際上這種算法的確是如此執(zhí)行的。像windbg就是使用這種反匯編算法的。反匯編步驟是:
? ? ? ??A 位置指針lpStart指向代碼段開始處
? ? ? ??B 從lpStart位置開始嘗試匹配指令,并得到指令長(zhǎng)度n
? ? ? ??C 如果B成功,則反匯編(Intel風(fēng)格或者AT&T風(fēng)格)從lpStart之后n個(gè)數(shù)據(jù);如果失敗,則退出
? ? ? ??D?位置指針lpStart賦值為lpStart+n,即上條指令的結(jié)尾
? ? ? ??E 判斷l(xiāng)pStart是否超過了代碼段結(jié)尾處,如果超出則結(jié)束。如果不超出則進(jìn)入B流程。
? ? ? ??在A、E這兩個(gè)過程中,我們需要提前確定代碼開始處和結(jié)束處。一般來說,在windows平臺(tái)上,我們可以根據(jù)PE文件的可選頭標(biāo)準(zhǔn)域中BaseOfCode結(jié)合DataDirectory中相關(guān)信息可以算出來代碼開始位置,從PE文件可選頭標(biāo)準(zhǔn)域中SizeOfCode得到代碼段總大小,從而確定結(jié)尾位置。
? ? ? ??在B這個(gè)過程,對(duì)于不同指令集存在細(xì)微的差別。現(xiàn)在簡(jiǎn)要說下主要兩種指令集:
RISC全稱是Reduced Instruction Set Computing,即精簡(jiǎn)指令集。該指令集有個(gè)非常重要的特定——指令長(zhǎng)度相同,這樣反匯編匹配不會(huì)出現(xiàn)回溯現(xiàn)象。
CISC全稱是Complex Instruction Set Computer,即復(fù)雜指令集。該指令集一個(gè)重要的特點(diǎn)是和RISC正好相反的——指令長(zhǎng)度可變,這樣反匯編匹配會(huì)出現(xiàn)回溯現(xiàn)象。
? ? ? ??可以發(fā)現(xiàn)線性掃描的一大特點(diǎn)就是簡(jiǎn)單方便,但是它存在一個(gè)問題:它無法知道整個(gè)程序的執(zhí)行流。使用過IDA的朋友會(huì)發(fā)現(xiàn),在我們使用IDA打開一個(gè)PE文件時(shí),IDA會(huì)給我們顯示一個(gè)UML類型的執(zhí)行流程圖。而Windbg就沒有這樣的功能。為什么?就是因?yàn)榫€性掃描有不知執(zhí)行流這個(gè)缺陷。
? ? ? ??既然知道了缺陷,那么在充滿極客的安全領(lǐng)域,自然有人會(huì)去研究和利用。我們可以利用這個(gè)缺陷,讓W(xué)indbg這類使用線性反匯編算法的工具分析出錯(cuò)誤的結(jié)果。
? ? ? ??我們開始一個(gè)思考個(gè)過程:看如上ABCDE流程,我們可以發(fā)現(xiàn)特別“懸”的一個(gè)操作就是確定lpStart。因?yàn)橹灰猯pStart確定錯(cuò)誤,那么分析出來的結(jié)果肯定是不對(duì)的。的確,線性掃描算法就是存在這樣一個(gè)致命的問題。那如何利用呢?
? ? ? ??a 在一條可以改變執(zhí)行流的有效指令后插入無效信息
? ? ? ??這兒所說的指令包括jmp,ja等跳轉(zhuǎn),以及ret等改變EIP的指令。
? ? ? ??我們先看個(gè)跳轉(zhuǎn)指令例子
int _tmain(int argc, _TCHAR* argv[])
{int i = argc;if ( argc > 1 ) {i++;}__asm{jz positionjnz position_emit 0xE8position:}i++;return 0;
}
? ? ? ??我們使用jz position、jnz position使程序的執(zhí)行流肯定走到position處,從而我們?cè)趈nz position這條有效指令后插入的0xE8是個(gè)無效數(shù)據(jù)。這樣我們將觸發(fā)線性掃描出錯(cuò)
? ? ? ??我們可以看到插入的0xE8(call指令)影響了分析結(jié)果,因?yàn)榫€性掃描在掃描004017fb~004017fc時(shí)發(fā)現(xiàn)是有效的jne指令,于是它傻乎乎的認(rèn)為004017fd開始的就是下一條指令開始處。正確的結(jié)果我們看IDA的反匯編結(jié)果
? ? ? ??我們?cè)倏匆粋€(gè)ret的例子
int _tmain(int argc, _TCHAR* argv[])
{__asm{push xxxret_emit 0xE8xxx:}printf("1");return 0;
}
? ? ? ??因?yàn)閜ush xxx使得棧頂為xxx,而ret將pop出xxx,并將EIP改成xxx,讓程序從xxx初開始執(zhí)行。這樣我們又構(gòu)造了一個(gè)無效數(shù)據(jù)0xE8。我們看看Windbg和IDA的反匯編結(jié)果
Windbg
? ? ? ??IDA(此處IDA有點(diǎn)智能,它判斷了下ret之后的EIP是否為一個(gè)固定地址)
? ? ? ??b 正常的流程識(shí)別錯(cuò)誤
? ? ? ??編譯器在將處理我們代碼時(shí)是有策略的,比如當(dāng)我們switch中case比較多的時(shí)候(我在我的環(huán)境測(cè)試時(shí)發(fā)現(xiàn)好像要超過2個(gè)case),switch case邏輯會(huì)使用跳轉(zhuǎn)表來表達(dá)。舉個(gè)例子
int switchfun(int nvalue){switch(nvalue) {case 0:case 1: {nvalue++;}break;case 2: {nvalue += 2;}break;case 3: {nvalue += 3;}break;default: {nvalue = 0;};}return nvalue;
}
? ? ? ??可以見到這就是使用了跳轉(zhuǎn)表。我們可以看到0040177C位置是一組數(shù)據(jù)。如果我們將這個(gè)跳轉(zhuǎn)表放在0040174A處,將原來0040174A的邏輯后移,并修正相關(guān)偏移,是不是我們就讓W(xué)indbg分析出錯(cuò)呢?是的,可是為了構(gòu)造這樣的結(jié)構(gòu),我得對(duì)我的代碼做改動(dòng),且要修改生成的二進(jìn)制文件。
int switchfun(int nvalue){{__asm nop;...__asm nop;}switch(nvalue) {case 0:case 1: {nvalue++;}break;case 2: {nvalue += 2;}break;case 3: {nvalue += 3;}break;default: {nvalue = 0;};}return nvalue;
}
? ? ? ??我們?nèi)藶椴迦攵鄠€(gè)nop,為我們之后方便修改二進(jìn)制文件提供空間,同時(shí)可以減少計(jì)算偏移的量。對(duì)二進(jìn)制文件修改如下
我將從B7C到B92的數(shù)據(jù)拷貝到以前是一串90(nop)開始處的B34。并緊跟這串?dāng)?shù)據(jù),將BC4開始的跳轉(zhuǎn)表數(shù)據(jù)拷貝過來,同時(shí)修正跳轉(zhuǎn)表的偏移(C4->4A)。程序可以正確執(zhí)行,我們看windbg的反匯編結(jié)果。
? ? ? ??錯(cuò)了吧!
? ? ? ??我們?cè)倏纯碔DA的反匯編結(jié)果
? ? ? ??可以見到IDA分析是正確的。
? ? ? ??經(jīng)過如上分析,是不是你覺得IDA比Windbg好呢?其實(shí)也不一定。線性掃描的一個(gè)大優(yōu)點(diǎn)就是它可以把所有代碼都反匯編掉,而IDA使用的遞歸下降(recursive descent)算法并不一定會(huì)將所有代碼都反匯編掉,我會(huì)在下一篇博文說明如何利用IDA這個(gè)缺陷,來隱藏我們不想被反匯編的邏輯。
總結(jié)
以上是生活随笔為你收集整理的反汇编算法介绍和应用——线性扫描算法分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一种在注入进程中使用WTL创建无焦点不在
- 下一篇: 反汇编算法介绍和应用——递归下降算法分析