
WHVP と Bochs で構築されたファジング用ハイパーバイザー
こんにちは!applepieへようこそ!これはファジング、イントロスペクション、バグ発見のために設計されたツールです。これは、最近のバージョンのWindows(特にWindows 10 17763で開発・テスト済み)に存在するWindows Hypervisor Platform APIを使用するハイパーバイザです。Bochsは、深いイントロスペクションとデバイスエミュレーションを提供するために使用されます。
Windows Hypervisor Platform API(WHVP)は、Hyper-Vのハイパーバイザ機能にアクセスするためのAPIセットです。このAPIにより、特別なドライバや権限を必要とせずに、ユーザー空間で仮想マシンを簡単に実装できます。
これは急速に開発中のプロジェクトです。新機能のドキュメント化前に、おそらくツイートするでしょう。
私はプロジェクトに物理的なものを持つのが好きです:
これはセキュリティ研究におけるファジングとイントロスペクションのために設計されたツールです。ハイパーバイザを使用することで、カーネルやユーザーランドを問わず、あらゆるターゲットに一般的なファジング手法を適用できます。この環境により、ターゲットのソースを必要とせずに、システム全体のファジングが可能です。ハイパーバイザレベルでコードカバレッジを収集でき、必要に応じてBochsエミュレーションを使用してエミュレーション環境で任意のイントロスペクションを提供できます。このカバレッジ情報は、ファズケースの有効性を判断するために使用できます。カバレッジの増加を引き起こしたファズケースは、興味深いケースとして保存できます。この入力は後で使用され、新しい改変の基盤となります。
スナップショットファジングがこのツールの主な用途です。特定の状態にあるシステムのスナップショットを取得し、保存します。このスナップショットはファジングのためにロードでき、ファズケースを注入して再開します。VMは非常に低コストでリセットできるため、頻繁にリセットできます。Wordの起動に5秒かかる場合でも、ファイルを読み取る瞬間のスナップショットを取れば、ファズケースを入力に関連する部分だけに絞ることができます。これにより、ソースへのアクセスを必要とせずに、非常にタイトなファジングループが可能になります。VMは完全に独立したシステムであるため、並列実行してすべてのコアにスケーリングできます。
現在、このツールはコードカバレッジの収集、Windows向けの動的シンボルダウンロード、Windowsターゲットのシンボル/モジュール解析のみをサポートしています。ファジングサポートの追加は間もなく行われます。
以前にこれらの機能のほとんどを実装したことがあるので(カバレッジ、ファジング、高速リセットなど)、気を散らされなければ、このプロジェクトはかなり早くファジングに対応できるようになるはずです: D
1月末までにカバレッジ(完了!)、フィードバック、モジュール一覧(完了!)、プロセスリスト、高速リセット、シンボルサポート(完了!)を目標としています。これにより、非常に優れたファザーになります。
主なサポートターゲットは最新のWindows 10です。Windowsターゲットでは、シンボルストアからのシンボルダウンロードが可能です。これにより、Windowsターゲットでシンボリックカバレッジをすぐに利用できます。ただし、コードはLinuxのエンライトメントを簡単に追加できるように書かれています。
エンライトメントがなくても、起動するあらゆるOSをファジングでき、基本的なカバレッジを収集できます。
OSサポートの問題を報告する前に、ハイパーバイザ/Bochsの変更が原因であることを確認してください。標準のプレビルドBochs(ハイパーバイザなし)でターゲットを起動してみてください。Bochsは一般的ではなく、Linuxの起動などの一般的な処理でも壊れるバグが頻繁に発生します。特に、Spectre/Meltdown緩和策によるCPUID/MSRの急速な内部変更がOSに導入されているためです。
問題のリストはGithubのIssuesページを参照してください。いくつか既にシードしています。これらのうちいくつかは、ファジング開発を開始する前に迅速に対処する必要があります。
これをビルドするにはいくつかのものが必要です:
Visual Studio 2017をインストールし、最新の状態に更新してください。ここでは最先端のAPI、ヘッダー、ライブラリを使用しています。
使用していたcl.exeのバージョン:Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27025.1 for x64
SDKバージョン:10.0.17763.0
https://rustup.rs/ からRustをインストールしてください。rustc 1.32.0-nightly (b3af09205 2018-12-04)を使用していました。
このプロジェクトは64ビットのみをサポートしているため、x86_64-pc-windows-msvcツールチェーンをインストールしてください。
cargoがパスに含まれていることを確認してください。これはデフォルトのはずです。
https://www.python.org/ からPythonを入手し、pythonが呼び出せるようにPATHに追加してください。
「Windowsの機能の有効化または無効化」で、「Hyper-V」と「Windows Hypervisor Platform」のチェックボックスをオンにします。もちろん、お使いのコンピュータがHyper-Vをサポートしている必要があります。
このインストール手順ガイドは、以下で検証されました:
Clean install of Windows 10, Build 17763
rustc 1.33.0-nightly (8e2063d02 2019-01-07)
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27025.1 for x64
Visual Studio Community 2017 version 15.9.4
applepie commit `f84c084feb487e2e7f31f9052a4ab0addd2c4cf9`
Python 3.7.2 x64
git version 2.20.1.windows.1



autoconfパッケージ)をインストールmakeパッケージ)をインストールgit clone https://github.com/gamozolabs/applepie でapplepieをチェックアウトpython build.py を実行
この初期ビルドプロセスは約2分かかる場合があります。最近のマシンでは20〜30秒の可能性があります。
プロジェクトのルートディレクトリから python build.py を実行するだけです。環境の健全性をチェックし、すべてが「自動で動作する」はずです。
python build.py clean を実行すると、BochsとRustのバイナリがクリーンされます。
python build.py deepclean を実行すると、すべてのBochsとRustのバイナリが完全に削除され、Bochsの設定もすべて削除されます。何らかの方法でBochsを再構成する場合に使用します。
Bochsの設定について調べて、環境を設定する方法を学んでください。いくつかの要件があります。例えば、sync=none、ips=1000000、現在はシングルプロセッサのみのサポートなどです。これらはコード自体で強制されており、自分自身で足を撃たないようにしています。
同梱の bochservisor_test\bochsrc.bxrc および bochservisor_test_real\bochsrc.bxrc 設定を例として使用してください。bochservisor_test_real がおそらく最新の設定なので、参考にしてください。
Windowsターゲットにはモジュールリストのエンライトメントがあり、実行中のコンテキスト内のすべてのモジュールの一覧を確認できます。これにより、命令アドレスをモジュール+オフセットに変換できます。このモジュール+オフセットは、ASLRの状態が変化するファズケース間でカバレッジ情報を保持するのに役立ちます。また、IDAなどのツールでモジュールに色を付け、どのコードがヒットしたかを視覚的に確認することもできます。
Windowsターゲットの場合、_NT_SYMBOL_PATHとsymchkを使用して、シンボルストアからシンボルが動的にダウンロードされます。パスにsymchkがない場合、静かに失敗します。シンボルがあれば、見やすい人間可読なカバレッジバージョンを保存して表示できます。さらに、プライベートシンボルを使用すると、カバレッジをソース:行に変換し、ソースコードに色を付けることができます。
実際にはテストはあまりありませんが、bochservisor_test という小さなOSがあり、ハイパーバイザで全てが起動することを確認します。
次に bochservisor_test_real という設定があり、Windows/Linuxなどの用途に使用しています。これがおそらく最も頻繁に更新される設定です。
このコードベースは、Bochsに少量のコードを追加して、CPUコンテキストへのモジュラーアクセス、ゲスト物理からそのバッキングメモリへのアクセス、デバイスとCPU状態の両方のステッピングを可能にします。
見るべき主なコードは、bochservisor Rustプロジェクトのlib.rsにあります。
BochsのメインCPUループでは、代わりにLoadLibrary()を使用してbochservisor DLLをロードします。このDLLは1つのルーチンをエクスポートし、それが呼び出されるRustのCPUループになります。
Bochsはこのbochs_cpu_loopルーチンに構造体を渡し、その構造体にはBochsから情報を取得したり、デバイスとCPU状態をステップしたりするための関数ポインタが含まれます。
MMIOまたはI/Oが発生すると、ハイパーバイザはメモリフォールトまたはI/O命令フォールトで終了します。WHVPはエミュレーションAPIを提供していますが、実際には不十分です。
代わりに、既に存在するBochsを使用して、数命令をステップ実行します。ハイパーバイザのCPU状態をBochsと同期させることで、いつでも動的にハイパーバイザとエミュレーションを切り替えることができます(少なくとも可能であるべきです)。
つまり、ハイパーバイザの完全な状態は常にBochsと同期しており、Bochsのスナップショットは通常通り動作し、ハイパーバイザなしでも起動できるはずです(スナップショット情報に保存する必要があるCPUID状態を除く)。
MMIOまたはI/Oが発生した場合、1命令だけをエミュレートするのではなく、一定数の命令をエミュレート下で実行します。ハイパーバイザへの出入りのAPIコストと、類似のMMIO操作が近くで発生する可能性が高いため、数命令をステップ実行します。これにより、APIのオーバーヘッドが削減され、VMEXITの頻度が低下します。これは調整可能な数値ですが、コードベースにあるものはおそらく理由があって設定されています。
割り込みは非常に興味深い方法で処理します。割り込みをハイパーバイザに配信するようスケジュールする代わりに、_すべて_の割り込みをBochsエミュレーション自体で処理します。もちろん、ハイパーバイザ内部で発生する例外などはBochsでは処理されません。
これにより、WHVPがサポートしていない機能(SMM用のSMIなど)も利用できます。BochsのBIOSはデフォルトでSMMを使用するため、SMIサポートがないとカスタムBIOSをビルドする必要があります。最初のイテレーションでこれをやりました…おすすめしません。
このプロジェクトはファジング用に設計されていますが、非常に新しい(わずか数日)ため、これらの機能はまだありません。
最初に追加されるものは次のとおりです:
Bochsのデバイス処理を1つのスレッドでリアルタイムループで実行し、別のスレッドでハイパーバイザを実行できる可能性があります。非同期イベントはIPCを介して通信され、ゲスト内で実行中にデバイスを更新できます。
現在、すべてが1つのスレッドで行われているため、デバイスをステップできるようにするためにハイパーバイザは定期的に終了する必要があります。独自のスケジューラを書いているようなものです。
これは少し高速になる可能性がありますが、複雑さが増し、競合問題の可能性も追加されます。これが実際に行われるかどうかはわかりません。
コードカバレッジを収集する方法はまだ決めていませんが、少なくともいくつかのオプションがあります。正確なものから高速なものまで。これらのカバレッジメカニズムはすべてシステムレベルであり、ターゲットのソースやシンボルを必要としません。
OS構造を解析して、プロセス一覧、モジュールリストなどの基本情報を取得します。その後、これを使用してPDBをクエリし、シンボル情報を取得します。
クラッシュを意味のある方法で報告します。理想的にはミニダンプが便利で、WinDbgでロードして処理できます。DMPは物理メモリとプロセッサコンテキストだけであり、既にそれらはあるため、これはかなり簡単かもしれません。
歴史的に成功してきた、バグの根本原因を特定するための楽しいテクニックがあります。それらをここに持ってくる予定です。
ダーティページを追跡し、変更されたものだけを復元することで、VMを非常に高速にリセットできるはずです。これにより、システムターゲットのすべてのコアで最大速度でファジングできます。これはfalkervisorで行ったことと似ているため、既に考えられ設計されています。ここに移植するだけです。
MMIOまたはI/Oが発生したときに実行をキャンセルする、非常に高速なファジングです。これにより、すべてのCPU時間をハイパーバイザで費やし、エミュレーション時間をゼロにできます。ファズケース中のディスクI/Oなどをサポートしないという欠点がありますが、便利です。
このプロジェクトの核となる概念の1つは、Bochsへの変更を最小限に抑えることです。これにより、このリポジトリのBochs部分を最新の状態に保つことができます。
また、可能な限り多くのコードをRustとDLLに移行し、システムをよりモジュール化し安全にすることを目指しています。これにより、ハイパーバイザ自体での愚かな破損バグが発生し、無効なファズ結果を引き起こす可能性が低減されることを期待しています。
現在、ハイパーバイザはDLLであり、FFI APIが変更されない限り、Bochsを変更せずに交換できます。
Bochs自体への変更は明確に文書化する必要があり、すぐにそのためのドキュメントを作成し、Bochsの更新で移植および再評価する必要がある変更を追跡します。