読者です 読者をやめる 読者になる 読者になる

C++を逆アセンブルする

C++

以前、C++でnewやクラスをコンパイルしたら、ネイティブコードではどうなるのか

気になったことがあったので、調べてみた。

もしライブラリやOSに依存しないような仕組みだったら、OS開発にもすぐに使えるかも…って。

尤も、newはメモリ確保だからどう頑張ってもシステムコールを使ってるだろうけどなあ。



とりあえず、newの方はまだうまく読破できてないので、クラスを逆アセンブルして

読破した結果を書いてみる。





こちらがソース。


#include <cstdio>
using namespace std;

class Formula {
private:
int a, b;
public:
Formula(int n, int d);
~Formula();
int res();
};

Formula::Formula(int n, int d) {
a = n;
b = d;
}

Formula::~Formula() {}

int Formula::res() {
return a + b;
}

int main() {
Formula f1(1, 3);

printf("%d\n", f1.res());
}


ソース自体はなんの意味もないけど。

2整数で初期化してあげると、resを呼べば加算した結果を返してくれるだけ。

これだけだけど、途中で3,4回くらいコンパイルに怒られて涙目(´・ω・`)



これをコンパイルしてみる。


$ g++ test.cpp -g -o test
$ objdump -d test > dump
$ view dump

アセンブルした結果がこちら(必要な部分だけの部分抜粋です)。


080484f4 <_ZN7FormulaC1Eii>:
80484f4: 55 push %ebp
80484f5: 89 e5 mov %esp,%ebp
80484f7: 8b 45 08 mov 0x8(%ebp),%eax
80484fa: 8b 55 0c mov 0xc(%ebp),%edx
80484fd: 89 10 mov %edx,(%eax)
80484ff: 8b 45 08 mov 0x8(%ebp),%eax
8048502: 8b 55 10 mov 0x10(%ebp),%edx
8048505: 89 50 04 mov %edx,0x4(%eax)
8048508: 5d pop %ebp
8048509: c3 ret

0804850a <_ZN7FormulaD1Ev>:
804850a: 55 push %ebp
804850b: 89 e5 mov %esp,%ebp
804850d: 5d pop %ebp
804850e: c3 ret
804850f: 90 nop

08048510 <_ZN7Formula3resEv>:
8048510: 55 push %ebp
8048511: 89 e5 mov %esp,%ebp
8048513: 8b 45 08 mov 0x8(%ebp),%eax
8048516: 8b 10 mov (%eax),%edx
8048518: 8b 45 08 mov 0x8(%ebp),%eax
804851b: 8b 40 04 mov 0x4(%eax),%eax
804851e: 01 d0 add %edx,%eax
8048520: 5d pop %ebp
8048521: c3 ret

08048522 <main>:
8048522: 55 push %ebp
8048523: 89 e5 mov %esp,%ebp
8048525: 53 push %ebx
8048526: 83 e4 f0 and $0xfffffff0,%esp
8048529: 83 ec 20 sub $0x20,%esp
804852c: c7 44 24 08 03 00 00 movl $0x3,0x8(%esp)
8048533: 00
8048534: c7 44 24 04 01 00 00 movl $0x1,0x4(%esp)
804853b: 00
804853c: 8d 44 24 18 lea 0x18(%esp),%eax
8048540: 89 04 24 mov %eax,(%esp)
8048543: e8 ac ff ff ff call 80484f4 <_ZN7FormulaC1Eii>
8048548: 8d 44 24 18 lea 0x18(%esp),%eax
804854c: 89 04 24 mov %eax,(%esp)
804854f: e8 bc ff ff ff call 8048510 <_ZN7Formula3resEv>
8048554: 89 44 24 04 mov %eax,0x4(%esp)
8048558: c7 04 24 70 86 04 08 movl $0x8048670,(%esp)
804855f: e8 ac fe ff ff call 8048410 <printf@plt>
8048564: 8d 44 24 18 lea 0x18(%esp),%eax
8048568: 89 04 24 mov %eax,(%esp)
804856b: e8 9a ff ff ff call 804850a <_ZN7FormulaD1Ev>
8048570: b8 00 00 00 00 mov $0x0,%eax
8048575: 8b 5d fc mov -0x4(%ebp),%ebx
8048578: c9 leave
8048579: c3 ret
804857a: 89 c3 mov %eax,%ebx
804857c: 8d 44 24 18 lea 0x18(%esp),%eax
8048580: 89 04 24 mov %eax,(%esp)
8048583: e8 82 ff ff ff call 804850a <_ZN7FormulaD1Ev>
8048588: 89 d8 mov %ebx,%eax
804858a: 89 04 24 mov %eax,(%esp)
804858d: e8 9e fe ff ff call 8048430 <_Unwind_Resume@plt>
8048592: 90 nop
8048593: 90 nop
8048594: 90 nop
8048595: 90 nop
8048596: 90 nop
8048597: 90 nop
8048598: 90 nop
8048599: 90 nop
804859a: 90 nop
804859b: 90 nop
804859c: 90 nop
804859d: 90 nop
804859e: 90 nop
804859f: 90 nop


とりあえず、main関数のところをC言語風に逆コンパイルしてみると…




void main() {
int d[2];// 0x18(%esp) ~ 0x1c(%esp)の部分
_ZN7FormulaC1Eii(d, 0x1, 0x3);
int tmp = _ZN7Formula3resEv(d);
printf((const char *)0x8048410, tmp);
_ZN7FormulaD1Ev(d);
return;
}

こんな感じになるのだろう。4行目でtmpを媒介にしたのは、あくまで読みやすさを追求しただけで、

本来はない方が望ましいんじゃないかな。



ここからわかるのは、おそらくdという配列、まあアセンブラには配列の概念なんてないけれど、

これがFormula f1というオブジェクトのことを指しているんだろう。



_ZN7FormulaC1Eii ⇒ Formula(コンストラクタ)

_ZN7Formula3resEv ⇒ res

_ZN7FormulaD1Ev ⇒ ~Formula(デストラクタ)



これは、逆アセンブルコードを辿っていくと、抜粋部分の始めの部分に書いてある。

これもC言語に直してみよう。




void _ZN7FormulaC1Eii(int *d, int a, int b) {
d[0] = a;
d[1] = b;
return;
}

void _ZN7FormulaD1Ev(int *d) {
return;
}

int _ZN7Formula3resEv(int *d) {
int tmp;
tmp = d[0] + d[1];
return tmp;
}


これを元のメソッドと比較してみよう。




Formula::Formula(int n, int d) {
a = n;
b = d;
}

Formula::~Formula() {}

int Formula::res() {
return a + b;
}


何か決定的な違いがないだろうか?



そう、アセンブルされると、オブジェクトはすべて引数を通してアドレス渡しされるみたいだ。

クラスをもたないC言語でたまに使う手法だ。





オブジェクト指向なんて、人間が勝手にそう考えただけで、2進数しか理解できないコンピュータに

とってみればいい迷惑。

それでも、機械語に翻訳されてもオブジェクトの欠片が残っているのがわかったので、

なかなか面白かった。







あと…



アセンブルコードのmain関数の最後の部分、804857aから804858dが

どうなっているのか全くわからないので、教えていただけると助かります!