「IDAの操作メモ - デバッガの使用例 -」ではソフトウェアブレークポイントを使用しましたが、今回はIDAハードウェアブレークポイントを使ってみたいと思います。はい、「IDAの」ハードウェアブレークポイントです(意味深)。
一般的に、コードに対して設定されるブレークポイントは「ソフトウェアブレークポイント」で、これは「IDAの操作メモ - デバッガの使用例 -」で述べた通り、コードをINT 3(設定により割り込みは変更可能)に置き換え、そこで割り込みが発生したらデバッガでハンドルする、といった仕組みになっています。
一方、世に言う「ハードウェアブレークポイント」は、CPUの特定のレジスタを用いて、指定したアドレスにアクセスしたときに割り込みを発生させるといった仕組みになっています。このため、CPUにそういった機能があるかどうかで使えるかどうか変わる機能です。「世に言う」ハードウェアブレークポイントは。
では、試しに使ってみましょう。まず、下のコードを見てください。
見たまま、ただのmemcpy関数です。
memcpy(void *dist, void *src, size_t size);
のアレです。
コレ自体は不審なコードでもなんでもないのですが。今回はオチから言ってしまうと、コピーされる対象データは暗号化された実行コードです。
この実行コードを復号化している箇所を調べる、或いは、このコピーしたデータをどのように使っているかを調べる場合、「この領域にアクセスしたときにブレークしたい」という要求が発生します。こんなとき、「ハードウェアブレークを利用する」ということになります。なにしろ、メモリのデータとしてアクセスするので、ソフトウェアブレークポイントでここを割り込みコードに変えたところで、ここがコードとして実行されるわけではありませんからね。
この場合、「Step over(F8)」でmemcpyを実行した後、コピーされた0x004E4A88にアクセスしてきた処理の前後を調べるために、0x004E4A88にハードウェアブレークポイントを設定します。この場合で一番簡単なのは、IDA ViewでGキーを押してアドレス指定ジャンプのダイアログを表示し、0x004E4A88を入力して画面に表示します。そして、このアドレスの行にカーソルを合わせてF2キーを押します。すると、「Breakpoint settings」の画面が表示されます。データ領域にブレークポイントを設定しようとした場合、「Hardware」が自動的にチェックされています。もし、コードのブレークポイントもハードウェアにしたい場合は、ここのチェックボックスをチェックします。
ハードウェアブレークポイントを設定した後にブレークポイントリストを表示すると、設定したブレークが「Hardware」の列で「RW(1byte)」となっており、この1バイトに読み書きが発生したときにブレークすることが分かります。
これで、0x004E4A88にアクセスしたときにトラップする準備ができました。続きを実行してみます。
すると、ハードウェアブレークポイントにヒットし、以下のようなメッセージがでます。
「ハードウェアブレークポイントがトリガーされた」というアラートですね。
そして、このケースでは以下で止まっていました。
ハードウェアブレークポイントのトリガ原因は0x00496F48です。ただ、ここをぱっとみると、edxも0x004E6A08となっており、レジスタの値だけではぱっと見関係ないように見えます。しかし、この行は[edx-1F80h]を参照しています。この値を計算してみましょう。ViewメニューのCalculatorを開くと、「Ecaluate expression」というウィンドウが開きます。ここに「edx-0x1F80」と入力してやると、計算結果が4E4A88。ビンゴです。なんとなく、注意をそらしたいアドレスの値を直接レジスタに入れたくない、という意図を感じますねぇ。「0x004E4A88というアドレスがアヤシイ!」と、レジスタの値に目を血走らせて追っていた人は、まあまず見逃すでしょうね。こういうアヤシイ領域のアクセスを追うときは、素直にハードウェアブレークポイントを使ったほうが健康的です。写輪眼とか白眼とか開眼していて見切れる人は別ですが。
さて、このコードを見ると、eaxに一旦ロードした後、すぐに0x004ED500にストアしています。次にアヤシイのは、0x004ED500へのアクセスとなりますね。と、いうわけで、0x004ED500にもハードウェアブレーク追加。
そしてさらに処理を進めると、今度は0x004ED500のハードウェアブレークにヒットします。
そして、ここでこの処理の本質が見つかりました。
コードを読むと、0x004E4A88のデータを格納した0x004ED500から、eaxにデータをロードしています。つまり、ここのeaxは0x004E4A88のデータと同じ値になります。これに、0x011FBA40を加算しています。そして、そのデータを一度0x004ED500にストアして戻した後、再度edxにロードしなおしています。つまり、このときのedxの値は「0x004E4A88のデータ値 + 0x011FBA40」となります。この値を[ecx - 0x1F80]にストアしなおしていますが、このアドレスを計算すると0x004E4A88です。
つまり、この処理は、「0x004E4A88のデータに0x011FBA40をプラスしただけ」です。
他に色々処理をしていますが、実際には0x004E4A88の値には関与していません。
この関数の呼び出し元の処理を精査すると、先ほどmemcpyした領域全体に同じ処理を行います。また、ループして一定回数この処理を繰り返します。つまり、「決められた回数0x011FBA40を加算することで復号できる、極めてに単純なシーザー暗号だった」というわけです。
この検体の場合、この処理の間に結構な量の処理がありますが、これは復号に関与しませんでした。それでもそういったコードがあるのは、解析者の目を惑わせることや、別のちょっとした副次的要因が考えられます(この副次的要因は、推測でもあるのと、あまり書かないほうが良さそうなので、ここでは差し控えます)。
このケースでは、「必要なパラメータの動きに注視するために、ハードウェアブレークポイントを用いて解析に必要な箇所だけを捉える」という例でした。
この復号結果に対し、分析をしようとするとまたちょっとあるのですが・・・。長くなった(ここまで書くのに3時間以上(泣))のと、別の話題なので次回にでも回します。
今回は、IDAの紹介記事の伏線回収が多かった(リンクが多かった)ワケですが、最初の「『IDAの』ハードウェアブレークポイント(意味深)」が未回収でしたね。
IDAのハードウェアブレークポイントの設定は柔軟で、セグメント単位でできたりします。
実際にやってみた例がこちらです。
いやいやいやいや、おかしいだろ!?
ハードウェアブレークポイントがいくつ設定できるん!?そんなに割り込みのレジスタあったっけ??
マニュアル読んでないので推測ですが(読めよwww)、IDAのハードウェアブレークポイントは実際にCPUのハードウェアブレークポイントを使っていないのではないかと思われます。つまり、「ハードウェアブレークポイント」として登録されたアドレスに対するアクセスを、IDAのデバッグ機能が各ステップで監視しているのではないかなーと・・・。もしそうなら、これ作った人頭おかしい(語彙力がないだけで、褒めている)んだけど。実際のところどうなんだろう?
ともあれ、ハードウェアブレークポイントは有用な機能なので、ぜひ使い倒してください。
2019/6/15追記:
検体のハッシュ値
MD5:5A80F4F7307ED946DB450DD42EEA2F77
SHA1:5398F2F77C5DCEC3A98041E8D61B67D06B452FDF