全局和局部變量
全局變量可以被一個(gè)程序中的任意函數(shù)訪問(wèn)和使用
局部變量只能在它被定義的函數(shù)中訪問(wèn)
在反匯編代碼中:
全局變量通過(guò)內(nèi)存地址引用
局部變量通過(guò)棧地址引用
代碼清單6-1:兩個(gè)全局變量
#include<stdio.h>
int x = 1;
int y = 2;
void main() {
x = x + y;
printf("total=%d", x);
}
代碼清單6-2:兩個(gè)局部變量
#include<stdio.h>
void main(){
int x = 1;
int y = 2;
x = x+y;
printf("total=%d", x);
}
使用gcc編譯為64位程序,使用IDA打開(kāi)(也可以使用gcc -S 查看匯編代碼)

此部分代碼為未經(jīng)標(biāo)記的匯編代碼。

這部分代碼是經(jīng)過(guò)IDA標(biāo)記的代碼。

反匯編算數(shù)操作
以下的C代碼進(jìn)行了加發(fā),減法,自增自減,去模五種運(yùn)算
C語(yǔ)言代碼

匯編代碼

這里需要解釋一下cdq的作用,cdq其實(shí)多出現(xiàn)在除法運(yùn)算之前,這里使用除法來(lái)進(jìn)行取模運(yùn)算。
CDQ 是一個(gè)讓很多人感到困惑的指令。這個(gè)指令把 EAX 的第 31 bit 復(fù)制到 EDX 的每一個(gè) bit 上。 它大多出現(xiàn)在除法運(yùn)算之前。它實(shí)際的作用只是把EDX的所有位都設(shè)成EAX最高位的值。也就是說(shuō),當(dāng)EAX <80000000, EDX 為00000000;當(dāng)EAX >= 80000000, EDX 則為FFFFFFFF。
例如 :
假設(shè) EAX 是 FFFFFFFB (-5) ,它的第 31 bit (最左邊) 是 1,
執(zhí)行 CDQ 后, CDQ 把第 31 bit 復(fù)制至 EDX 所有 bit
EDX 變成 FFFFFFFF
這時(shí)候, EDX:EAX 變成 FFFFFFFF FFFFFFFB ,它是一個(gè) 64 bit 的大型數(shù)字,數(shù)值依舊是 -5。
備注:
EDX:EAX,這里表示EDX,EAX連用表示64位數(shù)
在使用div或者idiv指令時(shí),是在用edx:eax除操作數(shù)并將結(jié)果保存在eax中,余數(shù)保存在edx中,最后把余數(shù)賦值給b。
void main(){
int a = 0;
int b = 1;
a = a + 11;
a = a - b;
a--;
b++;
b = a % 3;
}

識(shí)別if語(yǔ)句
對(duì)于一個(gè)if語(yǔ)句必定有一個(gè)條件跳轉(zhuǎn)
不是所有的條件跳轉(zhuǎn)都對(duì)應(yīng)一個(gè)if語(yǔ)句
C語(yǔ)言代碼

匯編代碼

基于cmp的結(jié)果進(jìn)行判斷是否繼續(xù)執(zhí)行還是跳轉(zhuǎn)到40102B
用IDA Pro圖像化分析函數(shù)


no為紅色箭頭
yes為綠色箭頭
藍(lán)色表示下一個(gè)執(zhí)行塊
識(shí)別嵌套的if語(yǔ)句
C語(yǔ)言代碼

匯編代碼


1,2,3處發(fā)生了三次跳轉(zhuǎn)。

這類(lèi)多重if嵌套還是直接看IDA圖像比較方便。
識(shí)別循環(huán)
找到for循環(huán)
for循環(huán)的四個(gè)組件
- 初始化
- 比較條件‘
- 執(zhí)行代碼
- 遞增遞減
C語(yǔ)言代碼
# include<stdio.h>
void main(){
int i;
for(i=0;i<100;i++){
printf("i equals %d.\n", i);
}
}
匯編

IDA Pro圖形化


當(dāng)比較語(yǔ)句為false的時(shí)候,執(zhí)行循環(huán)四步,有一個(gè)很明顯的閉環(huán)。紅的箭頭部分是跳出循環(huán),可以進(jìn)行下一步操作,本示例程序中,沒(méi)有進(jìn)行下一步動(dòng)作,僅清理?xiàng)2⒎祷亍?/p>
找到while循環(huán)
C語(yǔ)言代碼

匯編代碼

停止代碼的時(shí)候應(yīng)該關(guān)注1處的條件跳轉(zhuǎn)。
理解函數(shù)的調(diào)用約定
函數(shù)調(diào)用在匯編代碼中的表現(xiàn)可能不一樣,調(diào)用約定決定了函數(shù)調(diào)用發(fā)生的方式。這些約定包含了參數(shù)被放在棧上或者寄存器中的順序,以及是由調(diào)用者或者被調(diào)函數(shù)負(fù)責(zé)在函數(shù)執(zhí)行完畢后清理?xiàng)!?/p>
一個(gè)函數(shù)調(diào)用的偽代碼

最常見(jiàn)的三個(gè)調(diào)用約定:cdecl,stdcall,fastcall,下面討論他們的關(guān)鍵區(qū)別。
1. cdecl

在cdecl約定中:
參數(shù)從右到左按序被壓入棧
當(dāng)函數(shù)完成時(shí)由調(diào)用者清理?xiàng)!?將返回值保存在EAX中。
2.stdcall
stdcall主要約定了棧的清理是由被調(diào)函數(shù)來(lái)執(zhí)行的。stdcall是Windows API的標(biāo)準(zhǔn)調(diào)用約定。任何調(diào)用這些API的代碼都不需要清理?xiàng)#謇項(xiàng)S蓪?shí)現(xiàn)API函數(shù)代碼的DLL程序所承擔(dān)
3.fastcall
在fastcall中,前面的一些參數(shù)被傳到寄存器(典型的是前兩個(gè)),備用的寄存器是EDX和ECX。如果需要,剩下的參數(shù)再以從右到左的次序被加載到棧上。
使用fastcall比其他約定更高效,因?yàn)榇a不需要涉及過(guò)多的棧操作
4.壓棧與移動(dòng)
C代碼

adder函數(shù)匯編代碼

即使是同一種編譯器,在調(diào)用約定方面也可能存在差異性,這依賴于各種選項(xiàng)和設(shè)置

分析switch語(yǔ)句
if語(yǔ)句通常以兩種方式被編譯:
使用if樣式和跳轉(zhuǎn)表
IF樣式
C代碼

反匯編代碼


采用三對(duì)比較跳轉(zhuǎn),最后一句無(wú)條件跳轉(zhuǎn)
IDA Pro圖形化

整個(gè)圖線看上去就像是多層嵌套的IF,在if判斷為False的情況下執(zhí)行下一個(gè)判斷。

跳轉(zhuǎn)表
上面三個(gè)case的switch會(huì)被編譯器編譯為if,else結(jié)構(gòu),但是如果case的數(shù)量線性增加,或者剛好我們需要的case在最后一個(gè),那么算法的時(shí)間復(fù)雜度不就變成了O(n)嗎,其實(shí)并不是這樣,當(dāng)case的數(shù)量增加,編譯器會(huì)使用跳轉(zhuǎn)表優(yōu)化代碼,降低算法的時(shí)間復(fù)雜度。
C語(yǔ)言代碼
#include<stdio.h>
void main(){
int i=2;
switch(i)
{
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
case 4:
printf("4");
break;
case 5:
printf("5");
break;
default:
break;
}
}




[rbp+var_4]存儲(chǔ)的是case 一共有六種case(從0到5)所以是和case-1做比較

lea rdx, ds:0[rax*4]這個(gè)指令中是rax*4加上跳轉(zhuǎn)表的基址來(lái)確定要跳轉(zhuǎn)到哪一個(gè)case塊,乘以四是因?yàn)樘D(zhuǎn)表中每一項(xiàng)是一個(gè)4字節(jié)大小的地址。

IDA Pro上有標(biāo)記這是跳轉(zhuǎn)表。
反匯編數(shù)組
數(shù)組a是局部定義的,數(shù)組b是全局定義的
C代碼

匯編代碼


eax是索 引,每個(gè)元素的大小是4,數(shù)據(jù)的基址加上偏移來(lái)訪問(wèn)正確的數(shù)組元素。

識(shí)別結(jié)構(gòu)體
C代碼

main函數(shù)反匯編代碼

test函數(shù)反匯編代碼


分析鏈表遍歷
C代碼


匯編代碼


小結(jié)
- 從細(xì)節(jié)中抽象
- 不要過(guò)度死磕匯編,從全局角度看待問(wèn)題