「最も簡単な逆アセンブル(ループ) - あしのあしあと」の続き。今回は、ポインタを使ってみる。どうなるのかは、容易に想像がつく。
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 の値を参照するアドレスが入っている。ワンステップ多い。
図であらわすと、次のようになる。
なんか「アセンブラ」というより「ポインタ」そのものの説明になっている気がする。いや、そうでもないか。ポインタは、理屈をおさえることより、書き方になれる方が大変だもんな。