x86_64の逆アセンブルを読む(gcc版)
先週の土日にDEFCONに参加してました。
そのときに逆アセンブルを読む必要があったので、まとめてみました。
Cのソース
#include <stdio.h> int main() { printf("hello, world\n"); return 0; }
objdump -d -M intel helloの結果の一部
00000000004004fc <main>: 4004fc: 55 push rbp 4004fd: 48 89 e5 mov rbp,rsp 400500: bf c4 05 40 00 mov edi,0x4005c4 ;文字列のポインタをわたしてる 400505: e8 d6 fe ff ff call 4003e0 <puts@plt> 40050a: b8 00 00 00 00 mov eax,0x0 ;return 0 40050f: 5d pop rbp
変数をつかってみた
#include <stdio.h> int main() { int i=10; printf("hello, world\ni = %d", i); return 0; }
000000000040050c <main>: 40050c: 55 push rbp 40050d: 48 89 e5 mov rbp,rsp 400510: 48 83 ec 10 sub rsp,0x10 ;スタック領域を確保 400514: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa ;int a=10 40051b: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 40051e: 89 c6 mov esi,eax 400520: bf ec 05 40 00 mov edi,0x4005ec 400525: b8 00 00 00 00 mov eax,0x0 40052a: e8 b1 fe ff ff call 4003e0 <printf@plt> 40052f: b8 00 00 00 00 mov eax,0x0 ;return 0 400534: c9 leave 400535: c3 ret
関数の構造
1. ラベル
main:
2. スタックのベースポインタrbpをpush
push rbp
3. 他に使うレジスタがあればpush
push rsi ;たとえば
push rdi
4. スタックのベースポインタを現在のスタックの先頭に設定
mov rbp, rsp
5. 必要であれば、変数用領域を確保
sub rsp, 0x10
6. 変数への読み書き
mov DWORD PTR [rbp-0x4], 0xa ;変数へ書き込み
mov eax, DWORD PTR [rbp-0x4] ;変数から読み出し
7. 関数の呼び出し
mov r9, 0x6 ;第六引数
mov r8, 0x5 ;第五引数
mov rcx, 0x4 ;第四引数
mov rdx, 0x3 ;第三引数
mov rsi, 0x2 ;第二引数
mov rdi, 0x1 ;第一引数
call 000000 ;関数呼び出し
mov edx, eax ;eax=戻り値
8. 戻り値のセット
mov eax, 0x0 ;return 0
9. 呼びだし前の環境の復元
pop rdi ;基本的にはpopする
pop rsi
mov rsp, rbp ;忘れてた(13/06/19 14:10追記)
pop rbp ;popの順番に注意
もしくは
leave ;便利
10. 呼び出し元に戻る
ret