OllyDbgとアセンブラを学ぶ



●環境
Windows7 Pro SP1 + OllyDbg v2.01


●発端
単純な好奇心と、将来のための転ばぬ先の杖として勉強しておきたかった。
アセンブラの中でもおそらく最も資産が多いであろうx86アセンブラをOllyDbgを使いながら学ぶことにする。
(たぶんすごく雑に書き殴るだけになります)


●初めてのリバースエンジニアリング
手始めに、簡単なプログラムをリバースエンジニアリングしてみる。
C++で書いた以下のコードをVC++ 2015でコンパイルする。
#include <iostream>
#include <string>
 
 
void Output(const charstr) {
	std::cout << str << std::endl;
}
 
int Add(int xint y) {
	return x + y;
}
 
int main()
{
	Output("raid");
 
	int num = Add(2, 8);
	Output(std::to_string(num).c_str());
 
	std::string str;
	std::cin >> str;
 
	return 0;
}
 

File - Open から実行ファイルを選択するとアタッチが始まる。


F9でRun。プログラムが終了するかブレークポイントが設定されている箇所まで実行が進む。
今回の場合、アタッチ開始→(コンソールにraidと10が出力される)cinの入力待機→(入力すると)main()が終了する (左下に"Process terminated, exit code 0"と表示)
Ctrl+F2でRestart

適度なところで止めるために、ブレークポイントを設定する。
設定するには該当行で 右クリック→Breakpoint→Toggle か、機械語の部分をダブルクリック。
以下の画像はmain()の開始に設定した例。(設定したアドレスが赤くなっている)


Output()とAdd()の開始にもブレークポイントを振りたいが、探すのが面倒になったのでmain()の開始からコンソールを見ながらステップ実行(1行ずつ実行)していくことにする。
ステップ実行はそれぞれ、Step into(呼び出し先の関数に入っていく)がF7、Step over(呼び出し先の関数には入らない)がF8。
また、ステップ実行したとき、変更されたレジスタは赤字で表示される。
今回はF8をぽちぽちしていく。

すると00E31158から00E3115Dにステップオーバーしたところでコンソールに raid が表示された。
つまり、1つ前の行(00E31158)で呼び出した関数で文字列(raid)の出力が行われているということになる。


この部分に cpptest.cpp:19 Output("raid"); とソースファイル名と行数を表示してくれているので、これを参考に目当ての処理部分を探すのが楽だと思われる。
(※これはVC++でコンパイルしたから表示してくれるだけかもしれない, とするとやはりステップ実行で探すプロセスは身に付けた方がいい)

ちなみに、この画像はmain()内でOutput()を呼び出している行をクリックしたものになるのだが、Dest=00371A10と表示されているのが分かる。
(今回既にOutput()の開始地点にブレークポイントを設定しているのでそこからでも分かるが、)これがOutput()のアドレスということになる。
(※アタッチし直したりリビルドしてたら、いつの間にかニーモニックに直接アドレスが書き込まれてcpptest.cpp云々が表示されなくなった, 正直よくわからない)

呼ばれている関数の開始にもブレークポイントを設定して、Runからの流れをF9で送っていくとこうなる。
アタッチ開始→main()の開始→Output("raid")の開始→(コンソールにraidが出力される)Output(std::to_string(num).c_str())の開始→(コンソールに10が出力される)cinの入力待機→(入力すると)main()が終了する
太字がブレークポイントの追加で新しく実行が止まるようになった箇所である。


View - INT3 Breakpointsで設定したブレークポイントを参照でき、クリックすればダブルジャンプもできる。
おそらく必須機能

さて、やんわりと予想はしていたが、std::coutなど標準関数の実行時の処理が煩雑であり、ステップ実行で追っていても「うーん…」と意気消沈してしまう。
そこで、次はAdd()の開始を見つけてみようと思う。


MOV edx, 0A に最適化されてAdd()がたぶんなくなっちゃってました(この前にそれらしき関数をCALLしている形跡がない)
コンパイラ賢いなぁ…


ニーモニックをダブルクリックするかSpaceでAssemble(メモリ書き換え)が実行できる。
edxの値をstd::to_stringで文字列に変換し、それをchar型にキャストするのだが、そのedxを書き換えたらどうか?という直感的な試み。

意図したとおり255が出力された。なんとなくイメージしていたリバースエンジニアリングに近い行為だった。

過去入力したAssembleはPatchとして保存され、View - Patchesで閲覧できる。
右クリック→Apply Patch で再び書き換えることもできるし、Restore original code で戻すこともできる。



2016/07/27 初めて



トップに戻る