第零話:まずは動かしてみる 〜ブレイクポイントとステップ実行〜

(このノートは最後まで無料で読めます。投げ銭方式です)

さて、なにはともあれOllyDbgを動かしてみましょう。

こちらのzipファイルをダウンロードし、展開して出てくるexeファイルをOllyDbgで開いてみてください。

ソースコードは以下のとおりです。

 #include  

int sub(int i, int j)
{
     int x = 0;
     x = i + j;

     return x;
}

main()
{
     int a = 0, b = 0, ans = 0;

     a = 10;
     b = a + 100;
     ans = sub(a, b);

     printf("%d\n",ans);
}

aに10、bにa+100(=110)を代入し、この2つの数値を引数にしてsub()関数を呼び出し、引数を足しあわせた結果を返し、その結果を表示するという単純なプログラムです。

exeファイルをダウンロードできない場合は、目次のページを参考に自力でコンパイルしてください。

OllyDbgの4つのペイン

起動したOllyDbgは、4つのペインにわかれていると思います。


左上に実行するコードが書かれています。

右上はレジスタ一覧です。

左下はデータでグローバル変数やスタティック変数が格納されています。

右下はスタックでローカル変数などが格納されています。

この章ではコードとレジスタのペインについてのみ触れます。残り2つはおいおい説明するので名前と役割だけ覚えておいてください。

コード

左上のコードペインを見ると、一番左の列に8桁の英数字が表示されているのが分かります。

これは16進数で、(仮想)メモリ上のアドレスを指しています。最初の行はアドレス「00401000」のところに「55」という数値が書き込まれていることを示しています。

左から2番めの列の「55」「8BEC」などの数値は「マシン語」と呼ばれるコンピュータが実行する命令のビットパターンを表し、それをアセンブラコード(ニーモニック)に訳したものが左から3番目の列に表示されています。例えば、アドレス「00401000」にある「55」というマシン語をニーモニックで表すと「PUSH EBP」ということになります。

ニーモニックの左側の「PUSH」「MOV」「ADD」などを「オペコード(主命令)」、それ以外の「EBP」「ESP,EBP」など、オペコードが対象とする条件やパラメータなどを「オペランド」といいます。

Windows 7や8では実行するたびにアドレスが変わるはずです。これはASLR(Address space layout randomization:アドレス空間のランダム化)という機能がデフォルトでオンになっていて、実行するごとにそのプログラムが使うメモリの場所がランダムに変わるからです。ちなみにWindows XPにはASLRという機能は存在せず、いつ何度実行しても同じアドレスが出てくると思います。このシリーズの環境はWindows 7ですが、ASLRを無効にしているため同じアドレスが出てきます。単に説明を簡単にするためだけなので、無効にする必要はありません。というか無効にすると脆弱になるのでしないように。この辺の話はコラム的に書ければと思ってます。

レジスタ

レジスタとは、大雑把にいえば「CPUが使える変数」といったところです。それぞれのレジスタには役割があります。各レジスタの役割はおいおい説明するとして、ここでは真ん中らへんにある「EIP」についてのみ触れておきます。

レジスタ「EIP」

プログラムを実行すると、メモリ上にコードやデータが展開され、コードを一個一個実行していきます。コードペインに表示されているニーモニックを1行1行実行していくイメージです。

デバッガで実行ファイルを開くと、メモリ上にコードやデータを展開した上で停止します。
今、プログラムは、アドレス「004012B4」で止まっています。コードペインのアドレスの部分が反転している場所です。これからCPUは「CALL 00403601」を実行するよーという状態ですね。

デバッガは、これを1行1行実行されていく様子を見たり、自分の望むところまで一気に実行させたりといったことができるツールです。だからアセンブリ言語を勉強するのに便利なわけですね。

で、「CPUがどこの命令を実行するか」というのを示しているのが「EIP」です。プログラムはアドレス「004012B4」で止まっていると言いましたが、EIPを見てみると、同じく「004012B4」になっていますね。

CPUは、EIPが示す場所に書いてある命令を実行する。

と、覚えておきましょう。そこが本来の場所なのか、命令がおかしくないかなどはCPUは判断しません。愚直に、EIPが指し示す場所に書いてある命令を実行します。

EIPは「Extended Instruction Pointer」の略です。「命令ポインタ」とも呼びます。

実行してみる

長々と説明していきましたが、いよいよデバッガ上でプログラムを実行してみて、何が起こるのかを見てみましょう。
デバッガでプログラムを開くと、(結果が表示されるはずの)コマンドプロンプトのようなウィンドウがデバッガの後ろに表示されるので、それを見える位置にずらしてから実行します。
実行するには、[Debug]メニューから[Run]を選択するか、[F9]キーを押します。

デバッガの画面が動いて、ウィンドウに「120」と表示されましたね。EIPは「77F070F4」を指しています。「RETN」を実行するところです。

これは、プログラムのすべての処理が終わったところです。終わっちゃっちゃー意味がない。途中で止めて1行1行動かしてみたい。そこで、「ブレイクポイント」を使います。

ブレイクポイント

ブレイクポイントとは、「ここまでで処理を進めて止めてね」というマークのようなものです。
百聞は一見にしかず。やってみましょう。まずはプログラムを再度デバッガで開き直します。[Debug]メニューから[Restart]を選択するか、Ctrl + [F2]キーを押しましょう。

コードペインの一番上まで移動します。「PUSH EBP」から始まるニーモニックが10行ほどあって、数行「INT3」があり、さらに「PUSH EBP」から始まる命令群が見えますね。これ、最初の塊がsub()関数、次の塊がmain()関数です。プログラムが書かれた順に並んでいます。

プログラムとして人間が書いたのはmain()関数とsub()関数だけですが、CPU的にはmain()関数を実行する前にウィンドウを表示したりいろんな下準備をしなければなりません。それらの処理をいちいち見ていくととてつもない時間がかかるので、ここは人間が書いたmain()関数まで処理を進めていただきましょう。

そこで、main()関数が始まるアドレス(ここでは「00401020」)にブレイクポイントを設置します。該当部分を右クリックして[BreakPoint]→[Toggle]を選択するか、[F2]キーを押します。ダブルクリックでもOK。

ブレイクポイントが設置されると、アドレスの背景が赤くなります。

ここで実行。[F9]押下。EIPが「00401020」になります。これで、main()関数が始まるところで止めることができました。

ステップ実行

コードを1個1個実行していくことを、「ステップ実行」といいます。ステップインとステップオーバーという2種類がありますが、その説明は後述するとしてとりあえず動かして何が起こるのか見てみましょう。

ステップ実行は[Debug]メニューから[Step into]か[Step over]を選ぶか、[F7]キーか[F8]キーを押せばできます。いちいちメニューを選ぶのはめんどくさいんでショートカットで行きましょう。[F7]キーを押してみてください。

コードペインのアドレスの反転部分が1行下にずれ、レジスタペインのEIPが「00401021」に変わりました。変更された部分が赤字で表示されます。

EIPが「CALL 00401000」になるところまで[F7]を押していきましょう。#「00401000」の部分は環境によって変わります。sub()関数の始まりのアドレスです。(矢印の色が変わっていますが意味はありません)

CALLというのはその名の通り「呼び出し」で、ここでは、「00401000」、つまりコードペイン先頭にあるsub()関数を呼び出しています。ソースでいうと

ans = sub(a, b);

の部分ですね。
さあ、ここで[F7]を押すと何が起こるのでしょうか?

EIPがsub()関数の先頭アドレス(ここでは「00401000」)に変わりましたね!ここからはsub()関数の処理です。RETNというところまで[F7]キーを押して進めてみましょう。


RETNはreturn、つまりmain()関数に戻るということです。
main()関数のどこに戻るのか?当然sub()関数を呼び出した「CALL 00401000」の直後(ここではアドレス「00401058」)ですよね。うまく戻れるのか?[F7]キーを押してみましょう。

EIPが「00401058」に変わりました。これで、プログラムは無事適切な場所からmain()関数の処理を続けられますね。しばらく[F7]を押し続けて様子を見てみてください。CALL命令が実行されるごとにいろんなところに飛ばされることが分かると思います。
・・・数行のプログラムなのに延々終わらないですよね。ごく簡単なプログラムでも、これだけ処理が必要なわけです。

まとめ

とりあえずの使い方を説明しないと先に進まないので長くなっちゃいましたが、次回以降はもう少し細かく分けていこうと思います。

目次:OllyDbgを使ってx86アセンブラとデバッガの基礎の基礎を学ぼう(はてなブログ)

第一話:ステップイン/ステップオーバー/ステップアウト

(本稿はここで終わりです。ためになったという方、他のも読みたい!という方、投げ銭していただけると嬉しいです。)

ここから先は

0字

¥ 300

この記事が気に入ったらサポートをしてみませんか?