触りながら本を読むということであまり進まなそうなイメージがありますが、そこまで文字が多くないので、思った以上に進んでいます。ということで今回も書いていきます。
関数とスタック
すっかりCALLされたときに、戻り先アドレスをスタックに積む(PUSHする)イメージがついていましたが、関数を呼び出す時に渡す引数も(事前に)スタックに積むらしいです。逆アセンブル画面でちらほら見られるCALL命令の前のPUSH命令は引数を格納しているようです。スタックは上位方向に進むという話を前回しましたが、その影響で引数が渡される順番は一番最後のものから積まれます。
スタックポインタ(ESP)+4バイトすることで1つ目の引数にアクセスできますが、それはスタックポインタが実行中の関数を指しているときのみしか使えません。
例えば、main関数からhoge関数を呼んだとします。そうするとmain関数への戻り先がESPに格納されます。この時点では、上記画像でいうと、アドレス0004003(ESP)+4すれば引数1にアクセスでき、 0004003(ESP) +8すれば引数2にアクセスできます。
ここで、hoge関数内でfuga関数が呼ばれたとしましょう。 そうするとhoge関数への戻り先がESPに格納されます。 さて、この時点で引数1や2にアクセスしたい場合はどうしたらよいのでしょうか?先程まで、ESP+4、+8でアクセスできていましたが、ESPは更新されているため、同じ結果とはなりません。
こういったことが発生することを防ぐために、hoge関数が実行された際にESPに格納されたアドレスを別のレジスタにコピーしておき、保持しているようです。そうすれば、いつでも[コピーしたレジスタ]+4のアドレスへアクセスで値を取得することができます。ちなみにEBPレジスタにコピーして間接的にアクセスすることが多いとのことでした。 mov eax,[ebp+8]とかは引数にアクセスしている!?という感じだと思います。
で、実際に諸々処理が終わってreturnするときに、ESPが読まれて戻り先まで戻るわけですが、このままではスタックに引数が残ったままになってしまいます。ので、ESPの指す先を一番底にするために、add esp,8 します。うーん、これってスタックに積まれた値自体は開放されないということなんでしょうかね。ちょっとわからなかった。
参照文字列の検索
メッセージボックス等に書き込まれている文字列だけを検索できる機能があります。便利!ただ、日本語は、ollydbgに日本語プラグインを入れている場合のみです。
ここから文字列に対して一発でBPをかけらるので、上記画像のような対策はすぐに突破できてしまいます。別のデバッガ(x64dbg)で同じことをやってみたのですが、日本語を読めませんでした。というか、検索されません。
ちょっと今後は、ollydbgだけでなくx64dbgも交えながらやっていきたいと思います。なんといってもollydbgは古い。。デバッガで調べて出てくるのはこのx64dbgやGhidraといったものです。ollydbgだけ触っていると取り残されるかもしれないなぁと思いつつ触っていきたいと思います。ただ、枯れたソフトだけあって情報はたくさんあります。
メモリマップ
解析しているプログラムがメモリのどこに配置されているのかがわかります。
crackme02.exeは00400000 ~ 040A00000の範囲に配置されているようです。textとかrdataとかありますが、その右に書いてあるとおりの中身が入っています。00400000には何も書いてありませんが、ここはPE headerとなっていて、ファイル情報が格納されています。
ここから、メモリーアクセス(ダンプを見る)することで、中身を確認できます。.rdataを見ると、文字列が確認できました。ここから直接BPをかけて調査できるようです。
ただし、ここでのBPでは、プログラム内で止まるとは限らず、API内でとまることがあります。特にWin32API メッセージボックスは、引数として文字列をAPIに渡しているだけであって、プログラム自体が文字列を読み書きしているわけではないからです。文字列にアクセスするのはAPIです。
CMP DWORD PTR DS:[000000],1 ってなんぞ
どう見ても初見で解読不可能です。まず、[]に囲まれたものは、そのアドレスの値を意味します。アドレス自体ではなくて、その中身を指します。DWORD PTRは4バイトとして扱うという意味です。 例えば78 56 34 12 (リトルエンディアンであることに注意)の4バイト分は78 56 ,つまり5678を一つの数字として扱うということになります。
DSって何?セグメントの指定らしいけど本ではこの時点で詳しい説明してくれませんでした。
ので、調べてみましたが、ちょうど一つ前のメモリマップの「.data」を指すようです。セグメントという言葉なので、そのデータセグメント内データであることを指定しているのでしょう。データセグメントのメモリ範囲は決まっているのに、わざわざDSを明示しなければいけないものなのでしょうか。確かに、上記画像の例のCMP DWORD PTR DS:[409498],1の【DS:[409498]】の部分は、.dataの範囲内に収まっています。そういうルールであるということであれば納得ですが…
ひとこと
サンプルのcrackme01は多重起動できないそうですが、何もしなくても多重起動できてしまい、本通りに進みませんでいた。なんといっても内容がWindowsXP環境で書いてありますから仕方ありません。Windows2000の話などが出てきますが、自分にはさっぱりです。*XPは普通に使っていましたが…
まだ続きます。