教程 逆向反彙編第七課

2021-05-23 17:07:45 字數 4433 閱讀 3885

在以c為代表的高階語言中用if-then-else,switch-case等高階語句來構成程式的判斷流程,

不僅條理清晰且維護性還是不錯的.而組合語言的**則複雜得多,會看到cmp等指令的後面跟著 各類的跳轉指令jz jnz等.識別關鍵跳轉是軟體破解的乙個重要技能,許多軟體用乙個或多個跳轉 來實現註冊或非註冊.

下面先說說if-then-else語句

將語句if-then-else語句編譯**後,整數用cmp指令比較,而浮點值則是使用fcom fcomp比 較.語句if-then-else編譯後,其彙編**形式如下:

cmp    a,b    ;a和b只是代表

jz(jnz)    ***x    ;***x是乙個位址

cmp指令不修改操作符,根據兩個運算元的相減,影響處理的幾個標誌,如零標誌 進製標誌 符號標誌和溢位標誌,jz等指令就是條件跳轉指令,根據a,b的值大小決定跳轉方向.

實際上,許多情況下編譯器都用test或or之類的較短的邏輯指令來替換cmp指令.一般形式是"test eax,eax",如eax如果為0,則邏輯與運算結果為零,否則設為0.

來看乙個c反彙編的例項.

view plain

copy to clipboard

print

?

#include 

intmain()    

看反彙編的**:

view plain

copy to clipboard

print

?

push    ecx            ;為區域性變數分配記憶體相當於sub esp,4    

lea    eax,dword ptr ss:[esp]    ;eax指向區域性變數空間    

push    eax                

push    00407030        ;指向字串"%d"

call    00401030        ;c語言的scanf函式    

mov    eax,dword ptr [esp+8]    ;將輸入的字元傳出    

add    esp,8            ;由於是cdecl呼叫,所以在函式外呼叫    

test    eax,eax            ;若eax為0,則zf置1,否則zf置0    

jnz    00401020        ;若zf=1不跳轉,否則跳轉    

mov    eax,8    

add    eax,8            ;returna+b    

pop    ecx            ;釋放區域性變數用到的記憶體,相當於add esp,4    

retn   

好,看完if-then-else了,下面砍switch-case語句.

switch語句是多分支選擇語句.switch語句編譯後,實質就是多個if-then語句巢狀組合.編譯 器會將switch編譯成一組不同關係運算組成的語句.具體點,來看乙個例子.

view plain

copy to clipboard

print

?

#include 

intmain(void)    

return0;    

}   

把它編譯,然後反彙編看看彙編**:

view plain

copy to clipboard

print

?

push    ebp    

mov    ebp,esp    

sub    esp,8        ;為區域性變數分配空間    

lea    eax,[ebp-04]        

push    eax        ;指向字元("%d"

)    

call    004010a2    ;scanf("%d"

,&a)    

add    esp,8        ;    

mov    ecx,[ebp-04]    ;輸入的結果給ecx    

mov    [ebp-08],ecx    

cmp    [ebp-08],1    ;如果是1    

je    00401031    

cmp    [ebp-08],2    ;如果是2    

je    00401040        

cmp    [ebp-08],0a    ;如果是10    

je    0040104f    

jmp    0040105e    

push    00408034    

call    00401071    ;printf("a=1"

)    

add    esp,04    

jmp    0040106b    

push    00408038        

call    00401071    ;printf("a=2"

)    

add    esp,04    

jmp    0040106b    

push    0040803c    

call    00401071    ;printf ("a=10"

)    

jmp    0040106b    

push    00408044    

call    00401071    ;printf("a=default"

)    

add    esp,04    

xor    eax,eax    

mov    esp,ebp    

pop    ebp    

ret  

上面的是未優化的反彙編,再看看優化過後的:

view plain

copy to clipboard

print

?

push    ecx        ;為區域性變數分配記憶體,相當於sub    esp,4    

lea    eax,[esp]    

push    eax    

push    0040804c    

call    004010a1    ;scanf("%d"

,&a)    

mov    eax,[esp+08]    ;scanf輸入的結果傳給eax    

add    esp,08        

dec    eax        ;檢測eax是否為1,如果是下面的那句就跳轉    

je    00401055    ;相當於case1    

dec    eax        ;eax再減一,即eax的值是2    

je    00401044    ;相當於ease    2    

sub    eax,8        ;eax兩次減1後的值為8,所以值為18    

je    00401033   

編譯器優化後用"dec eax"指令代替cmp指令,這樣指令更短,並且執行速度更快,並且優化後,編譯器會合理排列switch後各case節點,有最優化方式找到所需要的節點.

如果case取值表示乙個算數級數,那麼編譯器會利用乙個跳轉表來實現例如"

view plain

copy to clipboard

print

?

switch(a)    

編譯器編譯後,"jmp dword ptr [4*eax+004010b0]"指令就相當於switch(a),其根據eax的值進行索引,計算出指向相應case處理**的指標.彙編**如下:

view plain

copy to clipboard

print

?

lea    eax,dword ptr [ecx-0]    

cmp    eax,6            ;判斷是否為default節點    

ja    0040109d    

jmp    dword ptr [4*eax+004010b0]    ;跳轉表    

push    00408054        ;case1:printf("a=1"

)    

call    004010d0    

add    esp,04    

xor    eax,eax    

pop    ecx    

ret   

下面的我就不寫了,反正大概就是這樣.

教程 逆向反彙編第三課

說完利用堆疊傳遞引數了,下面該說說使用暫存器傳遞引數的話題了.暫存器傳遞傳輸的方式並沒有乙個標準,所有與平台相關的方法都是由ide 也就是編譯器 開發人員制定的.儘管沒有統一的標準,但絕大多數編譯器提供商都在不對相容性宣告的情況下,遵循相應的規範,吉fastcall規範.fastcall顧名思義,特...

教程 逆向反彙編第五課

全域性變數作用於整個程式,一直都存在這個我們稍微了解任何一門程式語言都知道,他放在全域性變數的記憶體區 而區域性變數則是存在於函式的堆疊區,當函式呼叫結束後便消失.在大多數程式中,常數一般放在全域性變數中,如一些註冊版標記 測試版標記等.在大多數情況下,在彙編 中識別全域性變數比其他結構要容易的多....

教程 逆向反彙編第六課

既然要玩逆向,驅動那我們就算不懂但至少要了解下c c 語言,c 是一門支援oo的語言,對面想物件的軟體開發提供了豐富的支援庫.但要高效 正確的使用c 中的繼承 多型等語言的特性,必須對這些特性性的底層實現有一定得了解.其實就核心概念而言,c 的物件模型的核心概念並不多,但最核心的是虛函式.虛函式是在...