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