「最も簡単な逆アセンブル(ポインタ) - あしのあしあと」の続き。今回は(小さな)構造体を扱ってみる。
typedef struct { int price; int number; int tax; } payment; int totalAmount(payment *p) { return p->price * p->number + p->tax; } int main(void) { payment pc; pc.price = 2000; pc.number = 3; pc.tax = 100; return totalAmount(&pc); }
逆アセンブルして出力されたコードは、次の通り。さすがに、いっつも同じである「前処理」と「後処理」は飽きた。重要な箇所だけ抜粋する。余計な情報は、だんだん削っていこう。
まずは、main 関数。前処理が終わって、totalAmount 関数を呼び出すところまで。(11) 〜 (13) 行目で、構造体に値を入れている。(14) 行目で、構造体の先頭のアドレス(ポインタ)を取得して、(15) 行目でスタックにのっける。
(11) mov dword ptr [ebp-10h],7D0h (12) mov dword ptr [ebp-0Ch],3 (13) mov dword ptr [ebp-8],64h (14) lea eax,[ebp-10h] (15) push eax (16) call 00151361
次に、totalAmount 関数。main 関数でスタックしてくれた構造体のポインタを用いて、(14) 行目でかけ算、(15) 行目で足し算を行っている。
(11) mov eax,dword ptr [ebp+8] ; EAX ← 00D7FE98 (12) mov ecx,dword ptr [ebp+8] ; ECX ← 00D7FE98 (13) mov eax,dword ptr [eax] ; EAX ← 000007D0 (14) imul eax,dword ptr [ecx+4] ; [ECX+4] = 7D0h (15) mov edx,dword ptr [ebp+8] ; EDX ← 00D7FE98 (16) add eax,dword ptr [edx+8]
一応、スタックの状態を図示しておくか。最近、物忘れがはげしいから。
左が、main 関数が終わるときのスタックの様子。右は、totalAmount 関数の前処理が終わったときのスタックの様子。