far call にぶち当たった後の解析が困難になり、まずはセレクタを設定してコードの実行は継続できたものの、その先のコードが何かおかしい、というのが前回の記事でした。
今回はその続きです。
今回の検体のハッシュ値:
MD5:545BFDC9B1976AE0003443FF4F90EB7E
SHA1:92E8CE006BB3C4A1DDB8D8BA8DE3A90C0BBB6326
逆アセンブルコードがおかしくなる理由
これは、実は解析の段階でちょっと心当たりがありました。
実行しているプロセスは32ビットでしたが、その処理内で暗号化データを復号した結果が、64ビットコードとみられるものがあったのです。
そのあと、32ビットのプロセスからWowを使って64ビットの svchost.exe をサスペンドで起動するという器用なマネをしていたので、「あー、Process Hollowingでもするのかなー、ハイハイ。」でスルーしていたのですが。
このケースでは、この復号化されたコード(正確には、そのコピー)を far call していたのです。
そして、呼び出されているコードが、前回記事の最後の「dec eax」が妙に混じるコードでした。
そもそも、32ビットプロセスで64ビットコードが動くわけ無くね?
と、ここが最大の疑問となりました。
このため、
「この far call された後のコードは、64ビットで実行されている」
という仮説を立てて、コードを分析してみることにしました。
展開されたデータを64ビットとして逆アセンブル解析してみる
以上の理由により、メモリに展開されたデータを、64ビットのコードとしてAnalyzeしなおして、逆アセンブルコードが出せるかどうかを試してみようと考えました。
なお、IDAでこれをやる場合は、64ビット版を使ってここまで解析する必要があります。私は製品版があるので64ビットの解析もいけますが、フリーだと32ビットしかないかもしれません。
今回は x64dbg などは試してないですが、64ビット版が無い方はそのあたりを利用する必要があるでしょう。
また、実は今回の検体は他のアンチフォレンジックテクニックが色々入っているので、何かと骨が折れるかもしれません。
なお、64ビット版のIDAでも、32ビットの実行プログラムだった場合、32ビットの逆アセンブラが実行され、デバッグも32ビットで行われます。
まず、64ビットのIDAで、64ビットのコードが復号、またはそれをコピーした後まで実行し、breakします。
次に、メニューから「View」→「Open subviews」→「Segments」を選択します。
次に、64ビットのコードが復号、64ビットのコードが復号、またはそれをコピーしたメモリの領域が含まれるセグメントを選択して右クリックしてポップアップメニューを表示し、「Edit segment...」をクリックしてセグメント編集画面を開きます。
セグメント編集画面の下部に、「Segment bitness」という領域があり、今回のケースでは「32-bit」になっています。
今回はこの領域を64ビットとして扱いたいため、「64-bit」を選択し、OKをクリックします。
「アドレッシングモードが64ビットに変更されます。全ての命令・データが失われますがよろしいですか?」というような警告が表示されますが、別にこの領域にあるデータがクリアされるわけではないので、これもYesを押します。
すると、対象の領域が64ビットに変更されます。
対象のセグメント領域が64ビットに設定したら、当該領域を通常逆アセンブルコードが表示されているIDA Viewで表示します(キーボードのGキーかJumpメニューで。詳しくは過去記事の操作方法参照)。
すると、以前32ビットで逆アセンブルしていた領域がリセットされています。
領域を適当に選択(このときは、本当に適当に範囲選択すれば、勝手に必要な部分だけ逆アセンブルしてくれます)し、右クリックでポップアップウィンドウを表示して、「Analyze selected area」をクリックします。
確認ダイアログが表示されるため、Analyzeをクリックします。
すると、64ビットのコードとして逆アセンブルした結果が表示されます。
前の記事で示した、far call の後に不可解な「dec eax」が含まれるコードと比較してみます。
(32ビットで逆アセンブルしたコード)
(64ビットで逆アセンブルしたコード)
レジスタの表示が「rax」など64ビット表示になっているほか、「おかしい」と感じたdec eaxが消えています。
素で読んだ感じでは、このコードは正しそうです。
やはり、このコードは64ビットであり、64ビットとして実行すると正しく動くのではないか、ということが考えられます。
この先の解析の問題点
これで、64ビットのコードが見れるようになったのですが。
実は、64ビット版のIDAでこのままステップ実行しても、64ビットとして実行はしてくれません。
64ビット版であっても、デバッグ開始時に32ビットだった場合、対象の領域を64ビットして逆アセンブルコードを64ビットにしても、64ビットで実行してくれるわけではないようです。
このため、この方法では、コードを目で確認する分には有効ですが、デバッガで実行しながらの解析には利用できません。
32ビットと64ビットでは、レジスタへの値の設定や、スタックへの push pop のデータサイズが4バイトと8バイトと異なるため、仮に「dec eac」を nop に置き換えても、多少は動くものの結局上手く実行はできません。
特に、スタック領域のデータ参照時のオフセット計算が全然合いません。
このため、このコードをデバッガで分析するには、どうしても64ビットで実行するようにする必要があります。
ただし、今回色々探してみたのですが、途中から 32ビット→64ビット へ実行を変更する機能が見つかりませんでした(もっと探せばあるのかも知れませんが。知っている人教えてクレメンス)。
今回のコードの場合、実行ファイルがそのまま埋められているようなので、これを抽出してみます。
原始的な方法ですが、対象領域をマウスで選択し、右クリックでポップアップメニューを表示して、「Save to file...」でバイナリデータとしてファイルへ保存します。
なお、IDAで使える便利なpythonコードもあるようなので、そちらを探して利用する手もあるかもしれません。
これにより保存したファイルの拡張子を「exe」にし、64ビット版のIDAで起動することで、展開されたコードを解析・実行することができました。
ただし、これは32ビットで実行していたマルウェアが、 far call により64ビットコードを実行している環境とは異なる点があるため、以後の動作で本来の挙動と異なる部分が発生する可能性があることに注意が必要です。
少なくとも今回のケースでは、意味ありげに32ビットのコードの方で suspend した64ビットの svchost.exe に対し、Process Hollowing やプロセスのインジェクション等を仕掛けてくるとは思います。
この場合、対象のプロセスのハンドルが無くてコケそうですので、ダンプされたコードを魔改造するなり、何らかの細工をしないと正確な解析はできないのではないかと思われます。
解決しなかった「もやっと」
結局、原因は far call の後のソースコードがどう見ても32ビットのそれではなく、かつ実際動作させても64ビットだったことにあります。
しかし、肝心の「なぜ、 far call すると、32ビットコードで実行していたコードが64ビットで実行するようになるのか?」の根本的な仕組みが分かっていません。
今回は、あくまで「現象を確認した結果、そう解釈するのが正しいらしい」という確認をしたに過ぎません。
CPUのマニュアルを見た限りでは、そういった動きをすることが明記されていないようでした(といっても、私の英語力と理解力のほうが不足している可能性が大いにありますが)。
やはり、マルウェアを解析するにはこのあたりのCPUのメカニズムの勉強が必須ですが、なかなか勉強が追いつかないのが実情です。
うーん、マルウェア解析専門のプロあたりなら、このあたりは知ってるんだろうなぁ。
満足行く解析ができるようになるのは、いつの日のことやら・・・。
(ノД`)・゜・。