在以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
?
#include
intmain()
看反彙編的**:
view plain
copy to clipboard
?
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
?
#include
intmain(void)
return0;
}
把它編譯,然後反彙編看看彙編**:
view plain
copy to clipboard
?
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
?
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
?
switch(a)
編譯器編譯後,"jmp dword ptr [4*eax+004010b0]"指令就相當於switch(a),其根據eax的值進行索引,計算出指向相應case處理**的指標.彙編**如下:
view plain
copy to clipboard
?
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 的物件模型的核心概念並不多,但最核心的是虛函式.虛函式是在...