逆向--C函數和彙編

C函數和彙編

C代碼編程

(編譯工具gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609平臺ubuntu i386 32位)ubuntu

int bar(int c , int d){
    int e = c +d;
    return e;
}

int foo (int a , int b){
    return bar(a,b);
}

int main (void){
    foo(2,3);
    return 0;
}

gcc -S -masm=intel function_asm.c 生成一個intel風格的彙編代碼文件ide

.file   "function_asm.c"
        .intel_syntax noprefix
        .text
        .globl  bar
        .type   bar, @function
bar:
.LFB0:
        .cfi_startproc
        push    ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        mov     ebp, esp
        .cfi_def_cfa_register 5
        sub     esp, 16
        mov     edx, DWORD PTR [ebp+8]
        mov     eax, DWORD PTR [ebp+12]
        add     eax, edx
        mov     DWORD PTR [ebp-4], eax
        mov     eax, DWORD PTR [ebp-4]
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE0:
        .size   bar, .-bar
        .globl  foo
        .type   foo, @function
foo:
.LFB1:
        .cfi_startproc
        push    ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        mov     ebp, esp
        .cfi_def_cfa_register 5
        push    DWORD PTR [ebp+12]
        push    DWORD PTR [ebp+8]
        call    bar
        add     esp, 8
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE1:
        .size   foo, .-foo
        .globl  main
        .type   main, @function
main:
.LFB2:
        .cfi_startproc
        push    ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        mov     ebp, esp
        .cfi_def_cfa_register 5
        push    3
        push    2
        call    foo
        add     esp, 8
        mov     eax, 0
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE2:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609"
        .section        .note.GNU-stack,"",@progbits

objdump -M intel -S -d a.out 將生成的二進制文件用intel格式反彙編,咱們關心的以下:函數

080483db <bar>:
 80483db:       55                      push   ebp
 80483dc:       89 e5                   mov    ebp,esp
 80483de:       83 ec 10                sub    esp,0x10
 80483e1:       8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 80483e4:       8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 80483e7:       01 d0                   add    eax,edx
 80483e9:       89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 80483ec:       8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 80483ef:       c9                      leave
 80483f0:       c3                      ret

080483f1 <foo>:
 80483f1:       55                      push   ebp
 80483f2:       89 e5                   mov    ebp,esp
 80483f4:       ff 75 0c                push   DWORD PTR [ebp+0xc]
 80483f7:       ff 75 08                push   DWORD PTR [ebp+0x8]
 80483fa:       e8 dc ff ff ff          call   80483db <bar>
 80483ff:       83 c4 08                add    esp,0x8
 8048402:       c9                      leave
 8048403:       c3                      ret

08048404 <main>:
 8048404:       55                      push   ebp
 8048405:       89 e5                   mov    ebp,esp
 8048407:       6a 03                   push   0x3
 8048409:       6a 02                   push   0x2
 804840b:       e8 e1 ff ff ff          call   80483f1 <foo>
 8048410:       83 c4 08                add    esp,0x8
 8048413:       b8 00 00 00 00          mov    eax,0x0
 8048418:       c9                      leave
 8048419:       c3                      ret
 804841a:       66 90                   xchg   ax,ax
 804841c:       66 90                   xchg   ax,ax
 804841e:       66 90                   xchg   ax,ax

函數有幾個關鍵點調用函數、參數傳遞、返回值、調用後會返回計息執行下面的指令工具

其中參數和返回地址都是經過棧來實現的。平衡棧的動做根據不一樣的調用約定來實現的。C編譯器符合3d

參數從右到左入棧rest

int main (void){
    foo(2,3);
    return 0;
}
main函數調用foo函數(2,3)做爲參數被傳遞
8048407:       6a 03                   push   0x3
8048409:       6a 02                   push   0x2
804840b:       e8 e1 ff ff ff          call   80483f1 <foo>
8048410:       83 c4 08                add    esp,0x8
先將3入棧,再將2入棧
call foo 函數(跳轉到80483f1 <foo>執行)
call指令
call首先將它的下一條指令入棧(也就是8048410地址)將它調用的函數地址(foo函數地址複製到EIP寄存器當中)
下條指令CPU會執行EIP中地址,如今進入foo函數當中
080483f1 <foo>:
 80483f1:       55                      push   ebp
 80483f2:       89 e5                   mov    ebp,esp
 80483f4:       ff 75 0c                push   DWORD PTR [ebp+0xc]
 80483f7:       ff 75 08                push   DWORD PTR [ebp+0x8]
 80483fa:       e8 dc ff ff ff          call   80483db <bar>
 80483ff:       83 c4 08                add    esp,0x8
 8048402:       c9                      leave
 8048403:       c3                      ret
 foo調用了bar進入bar
 int bar(int c , int d){
    int e = c +d;
    return e;
}

int foo (int a , int b){
    return bar(a,b);
}
================================================================================
 080483db <bar>:
 80483db:       55                      push   ebp
 80483dc:       89 e5                   mov    ebp,esp
 80483de:       83 ec 10                sub    esp,0x10
 80483e1:       8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 80483e4:       8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 80483e7:       01 d0                   add    eax,edx
 80483e9:       89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 80483ec:       8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 80483ef:       c9                      leave
 80483f0:       c3                      ret
 其中
 80483f1:       55                      push   ebp;保存ebp現場
 80483f2:       89 e5                   mov    ebp,esp;將esp的值賦給ebp如今是參數、參數、返回地址、ebp現場。
 ebp是用來取調用者傳過來的參數的也能夠用來引用局部變量(在棧上分配)。爲啥呢?由於esp老是指向棧頂,剛進入被調用函數的時候將esp賦值給ebp這個時候好找前面壓過棧的參數。編譯器好實現。
 EBP:高級語言經過 EBP 來引用堆棧中的函數參數和局部變量。除了高級編程,它不用於通常算術運算和數據傳輸。
 取參數
 80483e1:       8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 80483e4:       8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 全部函數都是經過eax來返回值的
 80483ec:       8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 leave是下面函數開始的時候的逆操做從新將ebp賦值給esp,而後pop ebp;ebp又恢復到維護foo函數的了,前面的數據還在棧上可是我已經不維護了,沒有意義了。
 80483db:       55                      push   ebp
 80483dc:       89 e5                   mov    ebp,esp
 ret指令是call指令的逆操做,pop如今的esp的保存的數據給EIP(也就是foo函數調用bar函數前call保存的地址),而後esp值-4
 其中EBP老是指向當前棧幀的棧底、返回值經過EAX傳遞。

在逆向中將重點放在函數的識別和參數的傳遞上是節省體力的方法,函數是一個程序模塊,程序就是由這樣一個模塊一個模塊組成的。code

相關文章
相關標籤/搜索