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

Log.i53

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

Unpacking Virtualization Obfuscators を読みました

Unpack Virtualization 難読化 翻訳 英論

18th USENIX Security Symposiumと併せて開催されたWOOT'09のRolf Rolles氏のペーパーを翻訳しました:Unpacking Virtualization Obfuscators(PDF)

要約

 ほぼ全てのマルウェアのサンプルは実行ファイルの保護機構で覆われており,静的解析を進める前にそれを取り除く必要がある.既存研究では,特定の保護機構に対して自動的にアンパックを行うような研究が進められているが,まだ多くの最新技術には追いつくことが出来ていない.前述の仮定に反して保護されたプログラムは,その実行の過程で完全に保護されていない状態に戻すためのプロパティを常に持っているわけではない.本研究では,現在のソフトウェア保護の最も問題のある機能の1つであるVM難読化を回避するための新しい技術を提供する.この技術は既存の不可解なマルウェアの解析を可能にする.

1. 導入

 実行ファイルの保護機構は,最初はファイル感染型ウイルスの形で概念的に発信され,後にデジタル著作権保護スキームに進化し,ここ何十年もの間に様々な装いで存在していた.最近では,マルウェアの流行の中で中心的な役割を担っている.犯罪者は,自身のマルウェアをインシデント対応上必須のステップである分析から保護しなければならないという強力な動機を持っている.そのため,保護機構を取り除くことの困難化に対してより多くの努力が注がれており,これらを自動的に取り除くような防御的な研究に拍車をかけることになった.今日のマルウェアボリュームの巨大化によって,保護機構除去の自動化の必要性はより増大している.この軍備競争の結果,除去するのが非常に困難な新たな保護機構が生まれており,そのうちいくつかは人間またはコンピュータのいずれかによって解かれることなく公的に回避されていないものである.このように,不運なことにも攻撃的な側面が現在は勝っていると言える.
 実行ファイル保護機構の古典的なモデルは,単一の実行ファイルラッパーのことである.実行ファイル作成時に,プロテクタは実行ファイルの各セクションの内容を圧縮および/または暗号化する.その後,解凍する責任のある新しいコードセクションの追加して,実行時にセクションを複号するだけでなく,リバースエンジニアリングの試みを阻止している.実行ファイルのエントリポイントは,この新しいコード("unpacking stub"と呼ばれる)にリダイレクトされ,その実行が完了すると元のエントリポイントに戻される.プログラムはその後,元の保護される前の実行ファイルと同様に機能する.
 アンパックの自動化技術はこのモデルの保護機構に対しては良好な程度で成功を収めていた.これらのツールは,その規定の目標や内部動作に応じて変化し,共通のテーマを共有している.保護されたプログラムから隠されていると想定されている各コードは,実行中のある時点でメモリ上では完全に保護されておらず,その状況に至るまでに実行をガイドするための様々な施策を使用している.一部のツールは,元のエントリポイントと隠されたコードの識別を超え,元の保護されていない実行ファイルを生成するなど,更なる目標を持っている.
 残念ながら,現代の保護機構における無数の技術と比較して,このモデルは単に単純化のための策略である.一部の保護機構は複数の実行ファイルを利用しており,新しいプロセス内に実行ファイルをアンパックするものと,元の実行ファイルの修正バージョンをデバッグする2つのプロセスモデルで動作するものが存在する.一部の保護機構はシステムの共有ライブラリと保護された実行ファイルの関係を大幅に曖昧にさせる.本稿の目的において最も顕著に現れてくるのは,いくつかの保護機構は決して保護されたコードの一部を復元することがないという事実である.その代わりに,保護機構はそれらのコードを別の言語に変換し,多くの場合は実行時に難読化されたインタプリタでこれを実行している.この分野の先行研究の多くは,これらの保護機能に対処することを今後の課題として残している.

2. 仮想マシンによるソフトウェア保護

 "仮想マシン"(VM)は,おそらく現代における最も強力な技術でありソフトウェア保護にも利用されている.これらの保護機構は,バイトコードのプログラムが実行される際の仮想環境やインタプリタを実装している.インタプリタによって受け入れられる言語は,保護機構によってランダムに選択される.インタプリタ自体は,言語の選択に応じて生成され,一般的に重度に不明瞭化されている.
 アセンブリ言語は多くの場合単純であるが,既存のツールの価値を重度に希薄化するためにVMが用いられている.その結果,デバッガによる標準の動的解析は可能となっているが,大量のノイズによって低レートの信号を解析することが非常に面倒となっている.まず,VMがパース/ディスパッチした同じコードを何度も何度もトレースしなければならない.ホワイトボックス静的解析は,あらかじめインタプリタリバースエンジニアリングする必要が有るために非常に多くの時間を浪費してしまう.VMのプログラムにパッチをあてるには,VMパーサの解析を通じて取得される命令セットに精通している必要がある.リバースエンジニアリングを行う際にはこのコンポーネントが繰り返し表示されるために,高レベルの詳細は,低レベルの詳細の洪水によって覆い隠されてしまう.
 仮想マシンソフトウェア保護には2種類が存在しており,その下で実行されるコードの種類に応じて分類されている.文献[7]のような標準的な仮想マシン保護においては,その内部の仕組みを分かりにくくするため,保護されたプログラムのアンパッキングスタブの一部をインタプリタで実行している.このタイプの実行ファイルの保護は,マニュアル分析を阻止するのに非常に有効であるが,自動化されたアンパッカーに対して任意の課題を提起しておらず,さらに,このタイプの保護機構については既によく研究されている.
 もう1つの仮想マシンソフトウェア保護が,本稿の残りの部分において関心を持つべきものの1つである.いわゆる"VM難読化"(文献[1]や[10]において命令仮想化として知られる)は,プログラムの元のx86マシンコードの一部をカスタム言語に変換し,その後カスタム言語は実行時にインタプリトされる.プログラムのコードが元の形に復元されることはない.結果として,元のx86コードの再構築なしには既存のホワイトまたはグレーボックス分析は不可能となっている.

A. 仮想マシンアーキテクチャ

 前述のようにVM難読化ツールインタプリタを含んでおり,これは通常のインタプリタと多くの共通点を共有している.仮想化インタプリタは,アセンブリ言語で記述される傾向にあり,これはディスパッチの単一のポイントを含むかまたはオペコードを直接ねじ込むような,大規模なswitch文で実装されている.インタプリタ機械語のプログラムが利用可能なデータすなわち,プロセッサのレジスタやフラグに基づいて動作する.
 VM難読化ツールによってインタプリトされた言語はRISCのようになる傾向がある.あるx86 CISCの命令は,複数の仮想化された命令に変換される.例えば,MOV EAX, [EBX+ECX*4+123456h]のような複雑なメモリアドレッシングモードを含む命令のアドレス計算は,複数の仮想命令に変換される.1つはECXを取得する命令,1つはECXに4をかける命令,1つはEBXを取得する命令,1つはこれらを合計する命令,1つはこれらの結果に123456hを加算する命令,1つは形成されたアドレスを逆参照する命令となる.
 バイトコードプログラムの開始点をVM難読化ツールに入力すると,そのインタプリタは(VMのコンテキスト構造体と呼ばれる)構造体にホストのレジスタとフラグを保存する.バイトコード実行の正味効率はメモリへの適用操作の順序とそのコンテキスト構造にある.バイトコードプログラムを出た後で,インタプリタはホストのレジスタとフラグを復元してネイティブのx86 部分に制御を戻さなければならない.

B. テンプレート言語

 VM難読化ツールは,ネイティブのx86機械語を別のカスタム言語に変換する機能を中核としており,変換される言語は保護時に任意のファミリーの中からランダムに選択され,実行時にはその特定の言語用にカスタマイズされた難読化されたインタプリタで解釈されている.これにより元のx86コードは恒久的に破壊される.
 内部的には,各VM難読化ツールは最終的な言語に由来する1つ以上のテンプレート言語を提供している.例えば,Themidaではユーザが保護オプションから4つの異なるテンプレート言語:RISC-64,RISC-128,CISCCISC-2から選択することが可能である.選択の結果,異なる"基礎"言語が別々に使用される.
 実行ファイルを同じテンプレート言語で保護することを考慮して,任意の2つの特定のインスタンス間の違いが,同じ命令に異なるエンコーディングを行うことによって一般的に構成されている(例えば,0x12バイトはVMのあるインスタンスでは加算を表すが,他のインスタンスでは乗算を表す).また,特定の命令内への余分な難読化(例えば,命令ストリームの中に埋め込まれた定数オペランドを取る方法については,定数自体が難読化されていてもよい),およびVMハーネスやその各ハンドラに対するx86レベルの難読化も含まれている.

C. 回避手法

 VM難読化ツールを真に破るために,我々は保護されたコードをバイトコード言語から元の保護される前のコードに近いx86マシンコードに変換する必要がある.これによって,インタプリタ上にあるプログラムの依存関係を取り除くことができる.そうすることで,我々はアンチウイルスシグネチャデータベースに対するサンプルのテストやそのファミリーに応じてサンプルを分類する[20]など,標準的なプログラム解析とリバースエンジニアリング技術を適用することが可能となる.
 VM難読化ツールを回避する上で重要なのは,それらがインタプリタであることを覚えておくことである.特定のサンプルの言語を受け入れるフロントエンドとx86マシンコードを生成するバックエンドのコンパイラを持っていれば,仮想化された命令をマシンコードに再コンパイルすることが可能である.もう1つ重要なのが,VM難読化ツールはテンプレート言語から個々のサンプルのための言語を導出するため,2つの異なる保護されたバイナリが利用する言語は多くの類似点を持つことである.インタプリタが受け付ける言語の構文と意味論を決定することは一般的に困難である一方で,いくつかの特定の既知の言語ファミリーから言語が派生していることさえ知っていればこれはより簡単に変換可能となりうる.
 これら2つの要点を組み合わせると,これらの保護を攻撃するための計画は,テンプレート言語のいくつかの表現をx86マシンコードに変換するバックエンドのコンパイラインフラストラクチャを生成することであり,これは保護されたサンプルが受け付ける言語に固有なコンパイラのフロントエンドを生成するメカニズムである.

1) 仮想マシンリバースエンジニアリング:

このステップはVM難読化ツールごとに一度だけ実行する必要がある.熟練したリバースエンジニアが,VM難読化ツールが可能としている操作を決定するために仮想マシンを調査し,言語の意味を捉えて中間言語(IR)を設計し,中間言語命令のシーケンス内にVMバイトコードの操作をマッピングするトランスレータを構成しなければならない.文献[19]では仮想マシンの任意のインスタンスから特定の情報を抽出するシステムについて説明されている.しかしながら,完全に保護を破るためには更に多くの解析が必要となっている.
 我々は実行サンプルとしてVMProtect[14]を使用する.VMProtectの言語はスタックベースでありRISCである.VMへのエントリーでは,x86の部分で,VMProtectはスタックに(サンプル毎にランダムに決定された順序で)レジスタとフラグをプッシュする.VMバイトコードプログラム内部の最初のアクションは,スタックからすべてのレジスタをポップしてそれらを16のDWORDのスクラッチ領域に格納している.計算はプロセッサのレジスタの代わりにスクラッチ領域上で行われる.VMバイトコードプログラムの終了時に,スクラッチセクションの内容はホストスタックにコピーされ,その後,サンプル毎に決められた方法でランダムにプロセッサレジスタに復元される.
 VMProtectにおける命令の2つの例を以下に示す.最初は,スタック([EBP+0])からWORD値をポップして,スタック([EBP +4])上の別のWORD値に加算し,スタック上にフラグと結果をプッシュしている.

mov ax, [ebp+0]
sub ebp, 2
add [ebp+4], ax
pushf
pop dword ptr [ebp+0]

 2つ目の例はDWORD値をポップしてその後スタックのWORD値を取り出し,DWORD値をWORD値分だけ右シフトしてからスタックにフラグと結果をプッシュするものである.

mov eax, [ebp+0]
mov cl, [ebp+4]
sub ebp, 2
shr eax, cl
mov [ebp+4], eax
pushf
pop dword ptr [ebp+0]

 VMProtectのトランスレータが2つのWORD値が加算されるx86命令に遭遇した時,前述の加算オペコードを使用するバイトコードを生成する.例えば,ADD AX, BXは,BXレジスタをフェッチする命令,AXレジスタをフェッチする命令,WORDオペコードの加算命令,スタックからフラグをポップしてスクラッチエリア内のどこかに格納する命令,そして最後にコンテキスト構造体のAXレジスタ内にスタックから生じたWORD値を格納する命令で構成されている.
 VMProtectの命令セットは,それが表現するx86アセンブリ言語と比較して多くの機能を欠いている.例えば,ビット単位のXOR,OR,AND,NOT命令だけでなく,キャリー付き加算,減算,ボロー付き減算,インクリメント,デクリメント,単項否定,条件付きのレジスタ/メモリのセット(SETCC命令)などである.これらの機能はすべて,より原始的な操作によって実装されている.
 例えば,NOR命令はスタックから2つの引数([EBP +0]と[EBP +4])を取り出して,それらを否定してビットごとのANDを計算することによって表現している.

mov eax, [ebp+0]
mov edx, [ebp+4]
not eax
not edx
and eax, edx
mov [ebp+4], eax
pushf
pop dword ptr [ebp+0]

このオペコードは論理演算を実現するために使用される.例えば,AND EAX,EBX命令はNOR(NOR(EAX,EAX)NOR(EBX,EBX))のように表現される.同様に,加算命令は前述した算術命令を実装するために使用されている.
 さらに,SIMD命令のようなx86命令セットの一部はVMProtectによって仮想化されていない.このような命令を実行する必要がある場合に,VMProtectは(すべてのフラグやレジスタを復元して)バイトコードプログラムを終了して,非仮想化命令を含む場所にジャンプしてからバイトコードプログラムを再入力する.

2) 仮想難読化へのエントリポイントを検出する:

 VMに至るエントリポイントを検出することが静的に解決する上で困難な問題となっている.完全な逆アセンブルを想定([文献[11]には矛盾する)すると,VMへの制御の転送は他の制御の転送と同じように見える.しかし,それはプログラムのどの部分が保護(例えば,暗号化方式)を必要とするのかを知る上で十分な感度であることが通常明らかなように,実際にはVMエントリポイントを検索するためのリバースエンジニアリングは容易である. 文献[19]では,VMへの転送は動的な手段を介して検出することができることを示している.

3) 逆アセンブラを生成するための手順の開発:

 さらに,仮想化インタプリタインスタンスのレイアウトを知るために,リバースエンジニアは同じテンプレート言語から2つの異なる言語への派生を尊重して,それらがどのように異なっているかを理解しなければならない.VMProtectは作者のWebサイト上で提供されているデモ版と,登録ユーザが利用可能なフルバージョンの2種類があり,我々はそのどちらも評価している. まず,我々は作者によって提供されているパッケージを使用してサンプルを作成することができた.次に,アンチウイルス業界のパートナーシップからマルウェアサンプルのコレクションを取得した.
 デモ版で保護されたファイルは,そのすべてがほぼ完全に同じ言語を認識することが分かった.VMオペコードディスパッチャのx86の実装はサンプル間で同じであった.唯一の違いは,レジスタVMエントリーに保存されて終了時に復元される順序とスイッチテーブル内のディスパッチャの順序であった.この場合,言語のバリエーションを認識することは容易である.ディスパッチャは単にスイッチケースがどの操作に対応するかをフィンガープリントで決定することができ,レジスタの保存/復元のシーケンスは入口/出口のシーケンスからスライスすることが可能である.
 フルバージョンで保護されたファイルはかなり異なることが分かった.まず,仮想マシンとそのオペコードハンドラのそれぞれのx86の実装は,サンプルによって異なる難読化が施されていた.また,意味のある命令に対して正当な計算には影響しない“ジャンク”命令が交互配置されていた.例を次に示す.

; insert junk here
mov eax, [ebp+0]
; insert junk here
mov eax, fs:[eax]
; insert junk here
mov [ebp+0], eax
; insert junk here

 さらに,命令ストリーム(例えば,PUSH 0h)から定数パラメータを取ったVMのオペコードには,特別な難読化が行われていた.そのハンドラは,命令ストリームから定数をロードして,値を使用する前(例えば,スタックにプッシュする前)に一連の算出変換を適用していた.これらの"定数難読化"はサンプル毎に異なっていた.次のコードスニペットの最初の6行は定数難読化の一例である.

neg al
ror al, 2
xor al, 39h
dec al
neg al
not al
movsx edx, al
mov edx, [ebp+0]
mov [edi+eax], edx

 他にも表面上の難読化が存在しており,いくつかのサンプルでは下位命令ストリームを読む(例えば,EIPをインクリメントするよりもむしろ各命令の後でEIPをデクリメントする)などしているものもあった.また,ハンドラに対応してバイト値の決定に対して更なる難読化を施しているものや,VM命令ハンドラのアドレスを難読化しているものも存在した.
 我々が逆アセンブラを生成するためには,これらの難読化にも関わらずハンドラを認識して,さらに定数パラメータを取るハンドラ内の難読化を認識し,定数の解読に相当する一連の算術演算を抽出しなければならない.
 この作業にはアドホック技術を容易に適用可能であることが判明した.VMのハーネスとディスパッチャのx86の表現上の難読化が"junking"(ハンドラに属する正当な命令間のランダムな無害な命令の挿入)のみで構成されているため,難読化を変成するのではなく,非難読化されたハンドラ内に存在した全ての命令が難読化されたハンドラ内にもそのままの形で存在するということである(文献[18]でハンドラ内のメタモルフィックな難読化のための可能な解決策が説明されており,これはすなわち,コンパイラの最適化を介してx86アセンブリ表現の解読が行われているということである).したがって,我々は単に非難読化されたハンドラとの比較を行うために,難読化されたハンドラの解体時に正規表現を使用して最大の一致を選択することができる.この方法は,ハンドラを難読化しないVMProtectの評価バージョンならびに商用バージョンの両方に使用することができる.
 オペコードはこのように特定され,我々は命令ストリームから定数を取るハンドラの追加的な分析を行うことができ,それらの定数解読ルーチンをスライスすることができる.上記の例では,単に定数の解読ルーチンを取得するためにALレジスタを変更する一連の命令をスライスする技術を適用するだけである.
 VMProtectの逆アセンブラジェネレータの現在の実装は,約5KLoCのC++のコードからなるIDAプラグインである.これはVMProtectのバイトコードを生のバイトに変換する逆アセンブラ用のOCamlソースコードを構築する.
 プログラム解析の最近の研究(特に文献[21])では,定数の難読化なしにハンドラを識別するためのより堅牢な方法を提供している.VMオペコードハンドラの純粋なシンボリック実行を行うことにより,我々は同じ空間内にその入力空間(VMコンテキスト構造体,VM EIPとその周辺のバイトおよびプログラムのメモリ)からのマップである算術関数としての各ハンドラの表現を取得することができる.別の状態への変容に着目して,我々はそのような(メタモルフィック難読化を含む)難読化を介して導入されるような無関係な詳細を無視することができる.ハンドラが事前に知られているハンドラの1つと同じ機能を計算する場合,我々はそれから判断するための定理証明を適用することができる.定数難読化のハンドラの場合,おそらくTQBFソルバーを使用できる.我々はこれらについては今後の課題として残している.

4) バイトコードの逆アセンブルと中間コードへの変換:

 我々はカスタム逆アセンブラを手中に収め,今ではVMProtectのバイトコード命令のシーケンスにバイトコードストリームを分解することが可能である.しかし,各VMProtect命令のやや複雑な意味論によって逆アセンブルコードが多少読みづらいことが判明した.VMProtectはスタックマシンであり,すべての命令はスタックからのデータのプッシュまたはポップのいずれかであり,これは基本ブロック全体のデータの流れの追跡を複雑にすることができる(例えば,それが最終的にポップされる前に,何十もの命令がスタックに値をプッシュすることがある).したがって,すべての暗黙の操作(PUSHやPOPのような命令)を明示化するために各VMProtectの命令をより簡単な言語で一連の命令に変換できると便利である.
 VMProtectバイトコードを中間表現に変換するOCamlソースコードは,約1000行となっている.これはVMProtectのバイトコードハンドラの1つであり,その中間表現に対応する変換となっている.

mov eax, [ebp+0] ; pop dword from stack
mov eax, fs:[eax] ; read dword from address
mov [ebp+0], eax ; push dword onto stack
DeclareTemps([(0,D);(1,D)])
Pop (Temp 0)
Assign (Temp 1, Deref (FS, Temp 0, D))
Push (Temp 1)

 また,中間言語の抽象構文を以下の図1に示す.
図1: VMProtect IRのための抽象構文
f:id:i53:20150621054150p:plain

 我々は変換を容易にするために一時的な変数を導入している.これらの変数は,実際にはVMProtectの命令セットや生産されるx86コードの一部ではない.我々はコンパイラの最適化で後にそれらを排除しなければならない.

5) IRに対するコンパイル最適化の適用:

 VMProtectバイトコードが適切に変換され,我々は今x86に近いものに中間表現を変換するため,各基本ブロックに対してローカルにコンパイラの最適化を適用することが可能である.我々は最適化されていないリストで始まり,直後に中間言語に変換している.そのコードは,スタックマシンから期待される何かであり,ほぼ全ての命令はPUSHまたはPOP命令である.我々が実行例として使用する最適化されていないIRのリストを以下に示す.

push Dword(-88)
push esp
push Dword(4)
pop t3
pop t4
t5 = t3 + t4
push t5
push flags t5
pop Scratch:[52]
pop t6
pop t7
t8 = t6 + t7
push t8
push flags t8
pop Scratch:[12]
pop esp

 我々は,そのスタックマシン機能のIRを取り除くために簡単な分析を適用している.まずスタックを維持して最初から最後まで基本ブロックをスキャンする.遭遇する各PUSH命令のために,プッシュのサイズとそれが生成する命令を記録しておく.各ポップのために,単にスタックの最下部を点検し,スタックに値をプッシュされた命令に注意する.ここからは,PUSH/POPのペアを解消することで直接代入文に置き換えられる.生成されたプログラムはより従来型のリストに似ていることが分かる.

t3 = Dword(4)
t4 = esp
t5 = t3 + t4
Scratch:[52] = flags t5
t6 = t5
t7 = Dword(-88)
t8 = t6 + t7
Scratch:[12] = flags t8
esp = t8

 次に,標準のコンパイラが一時的な変数を除去するために定数伝播とコピー伝播に対して最適化を行う.結果として,元のIRの80%を排除しており,非常に読みやすいリストが残される.

Scratch:[52] = flags 4 + esp
esp = esp - 84
Scratch:[12] = flags esp - 84 

 我々は,スクラッチ領域に対応するレジスタを決定するためにコピー伝播を行う.実行サンプルを最適化した結果,これは単一の命令ESP = ESP – 84となった.
 IRは適切に変換されて,我々は今,前述のようにビット単位の演算命令に関する変換を反転させることが可能となった.我々は今ではNOR(NOR(EAX,EAX),NOR(EBX,EBX))のような表現もAND EAX,EBXのような表現に簡素化することが可能である.
 VMProtect IRの完全に最適化されたブロックの代表例を以下に示す.容易に見てとれるように,リストはMOV命令の代わりに代入文を使用している部分を除きx86アセンブリ言語と実質的に同じであり, 命令がフラグに与える効果の明示的な表現も備えている.

push ebp
ebp = esp
push -1
push 4525664
push 4362952
eax = FS:[0]
push eax
FS:[0] = esp
eflags = flags esp - 84
esp = esp - 84
push ebx
push esi
push edi
SS:[ebp-24] = esp
call [4590300]

 比較のために,VMProtectによって保護される前の元のコードを以下に再現する.

push ebp
mov ebp, esp
push 0FFFFFFFFh
push 450E60h
push offset sub_4292C8
mov eax, large fs:0
push eax
mov large fs:0, esp
add esp, 0FFFFFFA8h
push ebx
push esi
push edi
mov [ebp-18h], esp
call ds:[0460ADCh]
6) x86コードの生成:

 この特定のアプリケーションのコード生成は,通常コンパイラによって実行される操作からの違いがいくつか存在する.我々は,保護される前のオリジナルに可能な限り近いコードを生成しようとしている.例えば,任意のレジスタ割り当てを選択するよりも,我々は前述のコンパイラの最適化を介して,各操作に使用されるべきレジスタを推論することができる.さらに,VMProtect命令のいくつかがNOR命令のようなx86命令の類似物を備えていないため,関連するシーケンスや構文をコンパイルするのとは対照的に,我々はその上に構築された構造物を認識してアセンブリ言語に直接変換しようとしている.また,我々はINやOUTなどの特権命令のような,標準的なコンパイラでは一般的に考慮されていない命令を生成しなければならない.

3. 関連研究

A. 一般的なアンパックの自動化

 これは,この分野におけるこれまでの研究によって標的とされてきた導入部で説明した保護の単純化されたモデルとなる.従来の自動化では,汎用のアンパッカーはパッカーによる保護が簡単なモデルに執着していることを想定している.すなわち,実行可能ファイルがメモリ内で完全に保護されていない状態がいくつかの点で存在することになり,結果として(その保護されていない)プロセスイメージをディスクへダンプすることが可能となっている.これらのシステムの目的は,パックされたプログラムがそのアンパッキングスタブの実行を終えるまで実行を監視して,保護されていないことが想定されるポイントを見つけることにある.
 これまで製造されてきた自動アンパッカーは2つのカテゴリに分けることができ,1つはプロセッサの機能および/またはオペレーティングシステムを利用するもの,そしてもう1つはエミュレーションに基づくものである.自動アンパッカーの原型的な例となるのがOllyBonE[6]である.OllyBonEはx86におけるTLBの命令/データ分割機構を利用し,さらにページフォルトハンドラを組み入れることで,ソフトウェア内の実行不可能なページングをシミュレートするカーネルドライバを使用している.一般的な動作の流れはPaX[13]を強く連想させる.この機能は半自動化されたアンパッカーを提供しており,ユーザはプログラムの元のコードが存在するセクションを手動で選択し,ドライバがそのセクション内で実行されるすべての試みをトリガするものである.
 Renovo[1],Saffron[8],Azure[9]のような近年の自動アンパッカーは,その内部動作に応じて変化するが,その背後にある統一的な考え方は上記に示した保護の単純な概念に依存している.これらのツールはプログラムカウンタが保護されていないコードセクションがディスクに書き込まれるポイント,すなわちオリジナルエントリポイントに到達するまで(それぞれエミュレーション,計測機器,あるいはハイパーバイザの下で)プログラムを自動的に実行する.これら3つのツールを取り巻く文献において,実行ファイルのレクリエーションやインポートの再構築については言及されていない.
 Renovoは文献[1]に記載されているように,動的テイント分析に基づいた自動アンパッキングシステムであり,BitBlazeのTEMU[2]をQEMU[3]に改造して使用している.エミュレートされたx86コードは,各メモリの書き込みがそのディスティネーションを"dirty"な状態にするよう計測される.命令ポインタが既に"dirty"なメモリ上に存在する場合,システムがその周辺領域が隠蔽コードであることを考慮してそれをディスク上にダンプする.その後,その"clean"/"dirty"なビットマップをリセットしてエミュレーションを再開する.PandoraのBochs[10]は,Renovoと同様の原理を採用するBochs[12]に基づいたアンパッカーであるが,これはRenovoとは異なりインポート情報の再構築を含む動作する実行ファイルの再構築を試みる.
 Azure[9]は,IntelのVTハイパーバイザ機能に基づいた,ステルス設計なトレーシングユーティリティの概念実証モデルである.その文献では,オリジナルエントリポイントが検出されない場所までアンパッキングスタブをトレースすることでその有用性を説明しており,研究課題においてはこれを用いたアンパッキングフレームワークの実装について言及している.また,文献ではArmadillo[4]パッカーのアンパッキングが可能であることも示されているが,このトレーシングフレームワークがArmadilloのようなマルチプロセス保護をどのようにして破るかは不明である.
 アンチウイルスエンジンは,ファイル感染型のウイルスの除去や,シグニチャベースの検出またはビヘイビア検出を実行ファイル下で適用する目的から,未知の保護に対するアンパッキングをこれまで一般的な目標としてきた.これらのコンポーネントは一般的にx86プロセッサエミュレータWindowsオペレーティングシステムエミュレータの組み合わせで構成されている.一見,これはRenovoやPandoraのBochsのようなシステムに類似しているように見えるが,決定的な違いは前者が単純にエミュレートされたモデルを提供しているだけなのに対して,後者は考慮するためのサンプルとしてWindowsの正規コピーを提供することある.この設計上の決定は,資源効率のため仕方がないといった現実世界の圧力を感じやすい一方で,その実はアンチウイルスエミュレータが検出と回避の影響を特に受けやすいといった理由がある.

参考文献