惡意代碼分析實(shí)戰(zhàn) 第六章 識(shí)別匯編中的C代碼結(jié)構(gòu)

全局和局部變量

全局變量可以被一個(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 查看匯編代碼)


demo1.png

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


demo22.png

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

demo2.png

反匯編算數(shù)操作

以下的C代碼進(jìn)行了加發(fā),減法,自增自減,去模五種運(yùn)算

C語(yǔ)言代碼

suanshu1.jpg

匯編代碼

suanshu2.jpg

這里需要解釋一下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;
}
suanshu22.png

識(shí)別if語(yǔ)句

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

C語(yǔ)言代碼

if1.jpg

匯編代碼

if2.jpg

基于cmp的結(jié)果進(jìn)行判斷是否繼續(xù)執(zhí)行還是跳轉(zhuǎn)到40102B

用IDA Pro圖像化分析函數(shù)

if3.jpg
if4.png
no為紅色箭頭
yes為綠色箭頭
藍(lán)色表示下一個(gè)執(zhí)行塊

識(shí)別嵌套的if語(yǔ)句

C語(yǔ)言代碼

if5.jpg

匯編代碼


if6.jpg
if7.jpg

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

doubleif.png

這類(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);
    }
}

匯編

for1.jpg

IDA Pro圖形化


for2.jpg
for3.png

當(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ǔ)言代碼

while1.jpg

匯編代碼


while2.jpg

停止代碼的時(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)用的偽代碼
call1.jpg

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

1. cdecl

call2.jpg
在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代碼

mov1.jpg

adder函數(shù)匯編代碼

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

分析switch語(yǔ)句

if語(yǔ)句通常以兩種方式被編譯:

使用if樣式和跳轉(zhuǎn)表

IF樣式

C代碼

switch1.jpg

反匯編代碼

switch2.jpg

switch3.jpg

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

IDA Pro圖形化

switch4.jpg

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

switch5.png

跳轉(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;
    }
}
jump0.jpg

jump1.jpg
jump2.jpg

jump3-1.png

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

jump3-2.png

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é)大小的地址。

jump3-3.png

IDA Pro上有標(biāo)記這是跳轉(zhuǎn)表。

反匯編數(shù)組

數(shù)組a是局部定義的,數(shù)組b是全局定義的

C代碼

string1.jpg

匯編代碼

string2.jpg
string3.png

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

string4.png

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

C代碼

struct1.jpg

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

struct2.jpg

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

struct3.jpg
struct4.jpg

分析鏈表遍歷

C代碼

list1.jpg
list2.jpg

匯編代碼

list3.jpg
list4.jpg

小結(jié)

  • 從細(xì)節(jié)中抽象
  • 不要過(guò)度死磕匯編,從全局角度看待問(wèn)題
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容