Twitterに書ききれないこと

イベントや技術的なことを記したい・・・

API DEOBFUSCATOR: RESOLVING OBFUSCATED API FUNCTIONS IN MODERN PACKERS(翻訳)

今年のBlack Hatで発表された、API DEOBFUSCATOR: RESOLVING OBFUSCATED API FUNCTIONS IN MODERN PACKERSを訳した。 モダンパッカーにおけるAPIの難読化を解決する方法について発表された。

説明

モダンパッカーは、マルウェアサンドボックス解析とリバースエンジニアリングを妨害するためにAPIの難読化技術を使用する。 このようなパッカーでは、API呼び出し命令は、長く複雑なコードに置き換えられる。 APIの難読化技術は、難読化されるタイミングに応じて、静的と動的の二つに分類することができる。 静的な難読化は、実行ファイルに難読化された命令を埋め込む。 動的難読化は、新たに割り当てられたメモリブロックと、コピーされたメモリブロックにAPI関数の難読化コードを割り当てる。

動的な難読化のために、メモリアクセス解析を提案する。 以前のアプローチは、難読化コードのパターンマッチングか命令トレースのコードの最適化を使用していた。 パターンマッチングとコードの最適化に基づくアプローチは、パッカーのバージョンアップに伴うパターン変化に弱い。 提案するアプローチは、難読化パターンに変更が困難なAPI関数の難読化プロセスを利用する。 パックされたファイルに埋め込まれた難読化ツールは、実行時に各API関数を難読化する。 オリジナルのAPI関数のコードを読み取ることにより、新たに割り当てられたメモリブロックへ難読化APIコードを書き込む。 メモリアクセス解析は、各API関数のメモリの読み取りに対応する、メモリの書き込みに関するものである。 メモリアクセス解析は、難読化されたAPI関数のアドレスから、オリジナルのAPI関数へのマップを生成する。 難読化API呼び出しは、難読化された呼び出しパターンによってOEPで抽出される。 各難読化コール命令は難読化解除後のAPI呼び出しに置き換えられる。 難読化解除後のAPI呼び出しは、メモリアクセス解析によるマップによって解決される。 この解読方法は、Intelのピンを使用して、パックされたバイナリのメモリの読み出し/書き込み/実行を記録することで、実装されている。

静的な難読化のための iterative run-until-API methodを提案する。 前述のアプローチは、難読化されたAPI呼び出しを識別するために、コードのエミュレータを使用していた。 ほとんどのコードエミュレータは、オペレーティングシステム全体をエミュレートするために開発されているので、解読には適さない。 自前のエミュレータ開発は、例外に基づいた分岐や、モダンパッカーが使用するマルチスレッドなどの複雑な実行時の動作を実装する必要があり、時間がかかる。 そこで、動的なバイナリインスツルメンテーションツールIntel Pinを使用する。 これによりプロセスは、パッカーの保護メカニズムに検出されることなく監視することができる。 ツールは、オリジナルエントリポイントまでパックされたバイナリを実行した後、難読化されたAPI呼び出しアドレスに命令ポインタを変更する。 命令ポインタは、実際のAPI関数に到達するまで実行される。そのためオリジナルのAPI関数が識別されますが、関数自体は実行されない。 識別されたAPI関数が、正しいことを確認するために スタックポインタとスタックデータの整合性も確認する。 この処理は、各難読化APIのコール命令のために行われる。 ツールは、難読化されたAPI呼び出しを識別するために、プロセスの他のセクションのアドレスも含め、すべてのコール命令を検索する。

この2つの方法は、Themida 32/64でパックされたバイナリの難読化されたAPI呼び出しを、難読化解除することができる。 提案手法では、x64dbg, Ollydbg and IDA Proなど一般的なリバーシングツールでバイナリを分析できる。

スライド

https://www.blackhat.com/docs/us-15/materials/us-15-Choi-API-Deobfuscator-Resolving-Obfuscated-API-Functions-In-Modern-Packers.pdf

アウトライン

p2 アウトライン

•はじめに
APIの難読化解除の方法
•動的難読化のためのメモリアクセス解析
•静的難読化のためのiterative run-until-API method •実装
•デモ
•結論

p3 なぜAPIの解読の問題なのか?

APIの難読化によるマルウェアの関数の隠蔽
•商用パッカーによるAPI関数を難読化 •マルウェアの作成者独自のAPI難読化ツール
•モダンパッカー用の難読化解除ツールはない •x64のパッカー •カスタムパッカー

p4 モダンパッカーでのAPI難読化技術

•動的なAPIの難読化
API関数は、実行している間、難読化されている
•命令とアドレスは実行するたび変わる

実行中の間に新たに割り当てられたブロックに分岐
(難読化された User32.dll :MessageBox)

p5 モダンパッカーでのAPI難読化技術

•静的なAPIの難読化
API関数はコンパイル時(パッキング)に難読化されている
•命令とアドレスが同じ

他のセクションに分岐
'RET'命令によるAPI呼び出し

p6 API難読化解除の目標

•難読化解除後
•(付近)オリジナルエントリポイント
•復元されたAPI関数は、OEPで呼び出す
•難読化解除後のイメージ
•逆アセンブルや逆コンパイルなどのコードの静的解析
•デバッガを使用した動的解析

p7 APIの難読化解除の方法

•どのようにAPIの難読化されたバイナリを難読化解除するか?
•動的なAPIの難読化
→メモリアクセス解析
•静的なAPIの難読化
→Iterative Run-until-API Method
•アンチデバッグを回避するには?
•動的なバイナリー・インストルメンテーション(Intel Pin)
•デバッガにおけるアンチデバッグプラグイン
エミュレータ

動的難読化ツールAPI難読化解除

p9 動的難読化ツールAPI難読化解除

•メモリアクセス解析
API関数のコードに関連するメモリが読み込まれると、対応するメモリには、難読化されたコードに書き込まれる。
  •難読化されたAPI関数の命令アドレス
→オリジナルAPI関数
•難読化されたコールのターゲットとなっているアドレスでオリジナルのAPI関数を復元

p10 動的難読化処理

•ランタイム難読化では何が起こるのか?
•ランタイム難読化ツールは、各機能を読み込み、各命令を難読化し、新たに割り当てられたメモリに難読化コードを書き込む
•各機能は順番に難読化される

p11 メモリアクセス解析

•どのようにオリジナルAPI関数を識別するか?
•次のAPI関数やDLLの読み込み前に、すべてのメモリ書き込みを記録
•最後のAPI関数のメモリ書き込みの数を制限

p12 OEPの探し方

•OEPを探す
•すべてのメモリ書き込み・実行を記録する
•OEPは、OEPが実行されたときに、最後に書き込まれたアドレスである
•書き込まれたメモリブロック(1ブロック=4バイト)をチェックして保存する
•OEPはオリジナルの実行ファイルのセクションにある

p13 難読化コールの識別

•パターンマッチングによる、OEPでのモジュール間呼び出しの検索
•一致したパターンは、誤検知含んでいる可能性がある
•ターゲットアドレスの解決後、誤解釈した命令が消える

p14 難読化コールの解決

•直接呼び出し解決
•呼び出しターゲットが、API関数への難読化されたアドレスにマップされている場合、呼び出しのターゲットのアドレスをオリジナルのAPI関数のアドレスに変更する
API関数呼び出しとOEPの解決を含むテキストファイルを生成する

p15 難読化コールの解決

•間接呼び出し解決 •オリジナルのセグメント(.text, .idata, …) は、パッキングで一つのセグメントにマージされる
•連続した難読化されたAPI関数のアドレスを含むメモリブロックを特定する
•オリジナルのAPI関数を持つIATの候補と一致する、難読化されたの呼び出しのアドレスを変更する

p16 難読化コールの解決

例: API難読化解除情報

p17 難読化解除したバイナリのデバッグ

API呼び出しを解決するための、デバッガスクリプトの生成
•メモリアクセスアナライザによって生成されたテキストファイルには、OEP、難読化解除されたアドレスが含まれる
OEPまで実行しておき、難読化されたアドレスを解決するPythonスクリプトを実装した

p18 難読化解除ツールでのリバーシング

•解読のスクリプトを実行した後、Ollydbgでx86バイナリをデバッグ

p19 難読化解除ツールでのリバーシング

•ダンプファイルを逆アセンブル

静的難読化ツールAPI難読化解除

p21 静的難読化ツールAPI難読化解除

•OEPでの静的難読化パターン
•難読化呼び出しパターン
•難読化されたとき、“Call qword ptr [___]”が“Call rel32”に変更される
API関数への難読化コールの実行
•スタックの形状が保持される
APIコール命令およびAPI関数の最初のいくつかの命令は、難読化されている
•難読化された命令を実行した後、オリジナルのAPI関数内の命令に到達する

p22 難読化解決の識別

•パターンによる難読化呼び出し検索
•call rel32 は難読化の候補
•アドレスがプロセスの別セクションにあるかどうかを確認

p23 難読化呼び出し解決

•難読化コードは、API関数まで実行される
API関数まで実行
API呼び出しアドレス候補へRIPを変更
API関数まで実行

p24 難読化呼び出し解決

•整合性チェック •難読化された呼び出し後、スタックポインタとスタック内容が保存されているかを確認する必要がある

p25 Iterative run-until-API Method

•難読化されているコールに対して、Iterative run-until-API Methodメソッドを適用する
•コンテキストを保存&復元

p26 Iterative run-until-API Method

•Iterative run-until-API Methodは、様々なパッカーに適用することができる    •VMP:API関数呼び出しは、仮想難読化
•Themida64:API関数呼び出しが変異している
•Obsidium:API関数の最初のいくつかの命令は、難読化されている
•カスタムパッカー

しかし最後に実際のAPI関数へリダイレクトされる

p27 難読化解除ツールでのリバーシング

•難読化解除後、x64DBGでx64のバイナリをデバッグ

p28 難読化解除ツールでのリバーシング

ダンプファイルを IDA Proを使用して静的解析

実施

p30 実施詳細

• Pin toolによるAPIアドレスの解決
Windows 8.1/7 – 32/64 bit (on VMWare)
Visual Studio 2013
Intel Pin 2.14
•難読化されたコールにパッチを適用するPythonスクリプト
リバーシングツール
•X64dbg
•IDA

まとめ

p35 まとめ

API難読化解除のための2つの手法
•動的な難読化のためのメモリアクセス解析
•静的な難読化のためのRun-until-API method
•商用パッカーで保護されたバイナリも、API難読化を用いて解析することが可能
•デバッガを使用
•逆アセンブラ&逆コンパイラを使用

p36 制限

DBIツールに依存する
•パッカーはDBIツールを検出することができる
DBIのtransparency機能を無効(BH US'14) •ObsidiumはデバッガとしてIntel Pinを検出
•静的に関数全体が難読化されているコードを難読化解除することができない
•関数全体が難読化されたとき、オリジナルのAPI関数にある命令は実行されない

p37 今後の課題

•アンチアンチデバッギング
•アンパックのためのx86/64 エミュレータの開発
API関数解決
•静的な関数全体の難読化に対応するための、コードの最適化とバイナリの相違取得
•カスタムパッカーの逆方向依存解析