読者です 読者をやめる 読者になる 読者になる

Log.i53

Themidaのアンパックを目指すブログ改め使い物になるえんじにゃを目指すブログ

マニュアルアンパック:UPX

導入

 UPXはいくつかの異なる実行ファイルフォーマットに対応したフリーの実行ファイルパッカーです。これは優れた圧縮率を実現し非常に高速な解凍を提供しています。UPXの設計は非常にシンプルでありアンパック妨害機能も備わっておらず、アンパッキングの基礎を理解する教材として適しています。本チュートリアルでは、OllyDbgを使用してUPXでパックされた実行ファイルをアンパックする方法を学習します。

UPXを使用して実行ファイルをパックする

 はじめに、UPXでサンプルとなる実行ファイルをパックする必要があります。まず、UPXウェブサイトから最新のUPXパッカーをダウンロードして、サンプルとなる実行ファイルをパックするために以下のコマンドを使用(またはパックしたいファイルをupx.exeにドラッグアンドドロップ)します:

upx.exe -9 c:\sample.exe


 既にUPXでパックされたバイナリファイルを所持している場合にはそちらを利用することもできます。PEiDやRDG Packer Detectorのようなツールを使用すればUPXでパックされたバイナリであるかどうかを簡単に確認するができます:
f:id:i53:20150613025406p:plain

 なお、この手の分析ツールシグニチャの関係か、アンチウイルスによって悪性ソフトウェアと判断されてしまうことが多いので例外ディレクトリ下か分析環境下で利用しましょう。

UPXのアンパックプロセス

 アンパッキング演習を始める前に、UPXの動作を理解しておきましょう。UPXで任意の実行ファイルをパックするとき、既存のすべてのセクション(text, data, rsrcなど)が圧縮されます。これら圧縮後の各セクションはUPX0, UPX1などで名前付けされます。そして、ファイルの終わりに実行時に全てのパック済みセクションを実際に解凍する新しいコードセクションを追加します。

 UPXでパックされた実行ファイルの実行時の動作を以下に示します:

  • 新しいエントリポイント(ファイル末尾に新しく追加されたコードセクション)から実行を開始する
  • 最初にPUSHAD命令を実行して現在のレジスタの状態を保存する
  • 全てのパック済みセクションがメモリ上でアンパックされる
  • 元の実行ファイルのインポートテーブルを解決する
  • POPAD命令を実行してレジスタの状態を復元する
  • アンパックされた元の実行ファイルのOEP(オリジナルエントリポイント)にジャンプする

 UPXのアンパッキングスタブはこのように動作するためESPトリックと呼ばれるテクニックを利用することで、POPAD命令を実行してレジスタを復元する操作まで一気にトレースをスキップすることができ、マニュアルアンパックの最終目的となるOEPを簡単に見つけることが可能となります。

UPXのマニュアルアンパック

 以下にアンパッキング操作における一般的な手順を示します:

  • 実際のOEP(Original Entry Point)を検索するために実行ファイルをデバッグする
  • OEPに到達したらアンパックされたプログラム全体をダンプする
  • インポートテーブルを解決する

 パッカーのタイプおよび複雑さに基づき、アンパッキング操作にかかる時間やその難易度は異なります。UPXはシンプルな設計でありアンパック妨害機能も備わっておらず、アンパッキングの基礎を理解する教材として適しています。ここでは、OllyDbgを使用してUPXでパックされた実行ファイルのデバッグとアンパックを行います。アンパックには任意のデバッガを使用することができますが、OllyDbgにはアンパックを行う上で有用なプラグインがいくつも提供されています。

OllyDbgを利用したマニュアルアンパック

 前準備としてOllyDumpプラグインをダウンロードして解凍し、OllyDbgのプラグインフォルダ(デフォルトでOLLYDBG.EXEと同一フォルダ)に配置しておきます:
f:id:i53:20150613041832p:plain

 まずはOllyDbgでUPXでパックされた実行ファイルをロードしましょう。この時OllyDbgがロード対象の実行ファイルがパックされていることを検出してコード分析をスキップするかどうか尋ねてきます。どちらを選択しても構わないのですが自分は基本的に「いいえ」を選択するか、プラグインでこのダイアログが表示されないように設定しています:
f:id:i53:20150613042215p:plain

 ロードが完了したらPUSHAD命令に遭遇するまで実行ファイルのトレースを開始します。たいていロードした時点で最初の命令がPUSHAD命令となっていますがそうでない場合にはPUSHAD命令に到達するまでステップ実行してください:
f:id:i53:20150613040630p:plain

 PUSHAD命令に到達したらスタック上にHBP(Hardware Break Point)を設置(Alt+F1でコマンドラインを開いて'hr esp-4'を入力)します。これによりこのPUSHAD命令に対応するPOPAD命令が実行された後でブレークが発生することになります。簡単化のためにコマンドプラグインを利用してスタック"esp-4"にリードブレークポイントを設置していますが、これはPUSHAD命令を実行した後でESPが指すアドレスにHBPを設置する操作と等価です。また、その他の方法としてはマニュアルでPOPAD命令(オペコード0x61)を検索してブレークポイントを設定する方法があります:
f:id:i53:20150613043539p:plain

 ブレークポイントを設定したら実行を再開(F9)しましょう。すぐにPOPAD命令の直後の命令でブレークするはずです。末尾にあるJMP命令は一般的にtailjump(テールジャンプ)と呼ばれており、パッカーによるアンパッキングスタブが完了した後で実際のOEPに遷移させるためのJMP命令となっています。UPXではJMP命令となっていますが、多くの悪意あるパッカーはretやcall命令などを使用してテールジャンプを曖昧にしようと試みます:
f:id:i53:20150613043902p:plain

 JMP命令を実行してOEPまで到達したらOllyDumpプラグインでプログラム全体をダンプします。【Plugins】➤【OllyDump】➤【Dump debugged process】を選択しましょう。OllyDumpプラグインは可能であれば全てのインポートテーブルを自動的に解決してくれます:
f:id:i53:20150613045110p:plain

 プラグイン利用によるツールアシストが若干多いですが、これにてUPXのマニュアルアンパックが完了となります:)

終わりに

 UPXのマニュアルアンパックを実際に行うことでパッカーのアンパック工程を理解できたでしょうか。アンパッキングスタブは基本的に次の3ステップを実行することになります:

  • 元の実行ファイルをメモリ上にアンパックする
  • 元の実行ファイルの全てのインポートを解決する
  • OEP(オリジナルエントリポイント)に実行を遷移させる

 如何に堅牢なパッカーであっても最後には必ずメモリ上にアンパック済みバイナリを展開してOEPに実行を遷移することになります。パックされたプログラムをデバッガ上で実行してアンパッキングスタブが完了した後でプロセスをメモリからダンプし、ダンプしたプログラムが正しく動作するようにPEヘッダとインポートテーブルをマニュアルで解決します。これがマニュアルアンパックのより効率的なアプローチとなります。