備忘録など

勉強したことをまとめておこうかなとか

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