逆向--C函数和汇编

C函数和汇编

C代码

(编译工具gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609平台ubuntu i386 32位)

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风格的汇编代码文件

        .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编译器符合

参数从右到左入栈

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传递。

在逆向中将重点放在函数的识别和参数的传递上是节省体力的方法,函数是一个程序模块,程序就是由这样一个模块一个模块组成的。

posted @ 2019-10-24 17:01  干掉那个寂寞  阅读(284)  评论(0编辑  收藏  举报