最も簡単な逆アセンブル(ポインタ)

最も簡単な逆アセンブル(ループ) - あしのあしあと」の続き。今回は、ポインタを使ってみる。どうなるのかは、容易に想像がつく。

int __cdecl plus(int *a, int *b) {
    return *a + *b;
}

int main(void) {
    int a = 0x07;
    int b = 0x0A;
    return plus(&a, &b);
}

アセンブルして出力されたコードが次。基本的には、初回といっしょ。

  00A83B20  push        ebp                     ; (01) ベースポインタを退避(1 つ前のスタックフレームの先頭)
  00A83B21  mov         ebp,esp                 ; (02) ベースポインタを、スタックポインタの初期値に設定
  00A83B23  sub         esp,0C0h                ; (03) スタックポインタを 48×4 先へ
  00A83B29  push        ebx                     ; (04) EBX を退避
  00A83B2A  push        esi                     ; (05) ESI(ソースレジスタ)を退避
  00A83B2B  push        edi                     ; (06) EDI(ディスティネーションレジスタ)を退避
  00A83B2C  lea         edi,[ebp+FFFFFF40h]     ; (07) EDI ← ベースポインタの 48×4 先のアドレス
  00A83B32  mov         ecx,30h                 ; (08) ECX ← 48
  00A83B37  mov         eax,0CCCCCCCCh          ; (09) EAX ← 0xCCCCCCCC
  00A83B3C  rep stos    dword ptr es:[edi]      ; (10) ES:[EDI] を EAX で埋める × 48(ECX 回)
  00A83B3E  mov         eax,dword ptr [ebp+8]   ; (11) EAX ← a が格納されているアドレス
  00A83B41  mov         eax,dword ptr [eax]     ; (12) ●
  00A83B43  mov         ecx,dword ptr [ebp+0Ch] ; (13) ECX ← b が格納されているアドレス
  00A83B46  add         eax,dword ptr [ecx]     ; (14) ●
  00A83B48  pop         edi                     ; (15) EDI を復元
  00A83B49  pop         esi                     ; (16) ESI を復元
  00A83B4A  pop         ebx                     ; (17) EBX を復元
  00A83B4B  mov         esp,ebp                 ; (18) スタックポインタを復元
  00A83B4D  pop         ebp                     ; (19) ベースポインタを復元 → スタックフレームの破棄
  00A83B4E  ret                                 ; (20)

(10) 行目までと、(15) 行目からは、いつもと同じ、前処理と後処理。(11) 行目から (14) 行目が今回の処理。初回と異なるのは、データの持ち方。
コード上では、● の箇所、(12) 行目と (14) 行目が、ポインタを使ったときと使っていないときの差になる。ポインタを使っていないときは、[ebp+8] と [ebp+0Ch] に、a と b の値が直接入っていた。今回は、a と b の値を参照するアドレスが入っている。ワンステップ多い。
図であらわすと、次のようになる。

なんか「アセンブラ」というより「ポインタ」そのものの説明になっている気がする。いや、そうでもないか。ポインタは、理屈をおさえることより、書き方になれる方が大変だもんな。