仮想化技術によるアンチルートキット

今回は、仮想化技術で隠されたプロセスを検出する、という研究のお話をします。また、これについて試験的にWindows 7(x86)で動くものを実装してみましたので、その実装詳細と評価も交えていきます。実装はこちら


いくつか用語を使いますので、あらかじめ定義しておきます。ルートキットという言葉は、ここではカーネルモードで動作する悪意のあるコードを指します。隠されたプロセスとは、ルートキットの働きによってタスクマネージャーなどから隠され、検出できなくなってしまったプロセス(マルウェア)であるとします。仮想化技術は、例を出すときはIntel VTを前提にします。

#あとなにか文体が違うのは気のせいです。

仮想化技術がセキュリティに利用されるわけ

仮想化技術を使うと、カーネルよりさらに高い権限で動く仮想マシンモニター(以後VMM)を動作させることができるようになります。VMMが動いている状態では、カーネルモードで動作するコードであっても、あらかじめ設定した状況になればイベントが発生しVMMに制御が移ります。発生するイベントをキャッチし、VMMがさまざまなチェックを行うことでルートキットを見つけたり、場合によっては無力化したりすることができます。以下はこの概要図です。

さらに都合のよいことに、これらイベントの発生、VMMによるキャッチ、追加の処理は、イベントを発生させたルートキットやその他のカーネルモードのコード、また一般アプリケーションすべてにとって透過的です。そのため、ルートキットはVMMを検出して動作を妨害することができません(実際には、できないように実装できる、というのが正確です)。このような特性に注目され、VMMはセキュリティ用途としても研究されています。

VMMで隠されたプロセスを見つけ出す

VMMを用いたセキュリティの研究としてもっとも基礎的なもので、隠されたプロセスを見つけ出すというものがあります。あくまで一例ですが、今回実装した隠されたプロセスを見つけ出す仕組みについて説明します。


VMMは、特殊なレジスタであるCR3が書き変わるタイミングをイベントとして処理できます。CR3の変更は、プロセスのコンテキストスイッチのときに必ず発生するため、このイベントを処理することでプロセスのCR3の値が取得できます。プロセスごとにCR3の値は一意ですから、この値を記録しておくと、どのようなプロセスが活動しているのかを一覧できるようになります。


隠されたプロセスを見つけ出すために、このCR3によるプロセスの一覧と、APIで取得したプロセスの一覧を比較します。APIの結果はルートキットによって改ざんされることがあるため、CR3によるプロセスの一覧には存在しAPIで取得したプロセス一覧には存在しないプロセスがあったとしたら、そのプロセスはルートキットによって隠されたプロセスである、という仕組みです。


以下はこの概念図です。この図の例では左側のプロセスEが、右側のAPIの結果には含まれておらず、APIによるプロセスの一覧から隠ぺいされていることが分かります。

このように、改ざんの可能性がない信頼できる(Trusted)情報と、改ざんの可能性がある信頼できない(Untrusted)情報を比較し、その矛盾から異常を検出する方法を「Cross-view validation」といいます。これはルートキットの検出に広く用いられている手法です。

VMMとOSでは扱える情報に差がある

簡単な理屈でしたが、VMMを使ったOSの検査を実装する場合、「セマンティックギャップ」と呼ばれる問題が出てきます。これはVMMがハードウェアレベルの情報しか取得できないため、OSの世界で扱われる情報とギャップがあることを指しています。


先ほどのケースでは、VMMはCR3を取得できましたが、プロセスID(以後PID)のようなOSレベルの情報が直接には取得できないというセマンティックギャップがあります。OSレベルの情報がないと、OSの機能(API)で取得したプロセス一覧と比較することはできず、意味のある情報になりません。


PIDを取得するにはAPIを利用すればよいのですが、APIを利用して得た情報はルートキットの影響を受けている可能性があり信頼できません。またVMMの実装方法によっては、OS側のAPIを利用できない場合もあります。仮にPIDを取得したとしても、そのPIDを使ってプロセスハンドルを取得するAPIが、ルートキットによって汚染されているかもしれません。


この問題に対しては一般に、APIがやることをVMMが自力でやる、という対応になります。ファイルシステムを再解釈したり、OS依存のデータ構造を解析したり、それなりに障壁があります。

今回の実装では簡単に実装するために、ルートキットに干渉されるとは考えにくいAPI、または簡単に代替実装できるAPIはそのまま利用することにしました。

実装 CR3とPIDを紐付ける仕組み

今回はVMMがsysenterをフック(仮想化)し、フックした関数の中で__readcr3関数とPsGetCurrentProcess関数を使ってCR3とプロセス構造体(EPROCESS)の紐付けを行いました。取得したデータはSTLのmapで管理します。


ここではsysenterを利用し、最初の例で出したCR3の変更のタイミングを利用しませんでした。CR3の変更のタイミングでは、その処理がカーネルのコンテキストで行われるために、__readcr3もPsGetCurrentProcessも、対象のプロセスの情報が取れなかったからです。


意味のある処理をするプロセスは通常sysenterを使うことになるため、これは実質的にはどちらの方法でも問題がないといえます。

実装 CR3ベースのプロセス一覧の管理

プロセスの生成は、新しいCR3の値が出てきたことから分かります。この新しいCR3の値は保存します。

プロセスの終了は、この実装の中では以下のいずれかに当てはまる場合にプロセスが終了したとみなし、CR3の情報を破棄します。

  • CR3に対応するEPROCESSのアドレスがページアウトされている
  • EPROCESS内のPIDが0になっている
  • EPROCESS内のCR3がsysenterで取得したCR3とは異なる
  • EPROCESS内のObjectTableがNULLになっている

実装 APIベースのプロセス一覧の管理

APIによるプロセスの一覧は、ZwQuerySystemInformation(SystemProcessInformation, …)関数を使って取得しています。取得は1秒間隔で起動するスレッドの中で実行します。

実装 隠されたプロセスの報告

ZwQuerySystemInformation 関数のプロセス一覧と、CR3ベースのプロセス一覧との比較は1秒ごとに行い、隠されたプロセスがあればDbgPrintEx関数で報告します。

また、ユーザーモードからも隠されたプロセスの調査を行えるように、要求に応じてCR3ベースのプロセス一覧をユーザー空間に複製する機能も実装しました。

効果とオーバーヘッドの評価

検出効果の測定としては、一般に知られているルートキットコードでも試したかったのですが、テスト中、仮想化技術を利用できるWindows7のPCが日常的に使用しているPCしかなかったため安全性の点から諦め、単純なルートキットを自作してテストしました。このルートキット は、ファイル名の先頭2文字が ”__” で 始まるプロセスを隠します。以下はこのルートキットを動作させた様子です。


画像の左側のタスクマネージャーとProcess Explorerからは “__notepad.exe” を見つけることができません。一方、右側の列挙では、VMMからCR3ベースのプロセス一覧を受け取ることで、隠されたプロセスを検出できていることがわかります。


オーバーヘッドの測定としては、一般的なワークロードがあいまいだったため、ワーストケースを想定して計測しました。VMMをインストールしていない状態と、インストールした状態で、sysenterを発行するだけのAPIを100万回実行し、その所要時間を比較しました。

void do_test(long count)
{
    for (long i=0; i<count; i++)
        FlushProcessWriteBuffers();
}



少なくとも今回の試験的な実装では、sysenterに関して10倍以上のオーバーヘッドがあるようです。

まとめ

  • VMMを使い、カーネルモードのルートキットによって隠されたプロセスを検出することができる
  • VMMからOSの情報を扱うにはセマンティックギャップを埋める必要がある

参考

いかがでしたでしょうか。ややターゲットが狭い分野の話題ですが、多少なりとも楽しんでいただけたなら幸いです。最後に関連する研究の論文をあげておきます。

・Stealthy Malware Detection Through VMM-Based "Out-of-the-Box" Semantic View Reconstruction
http://www.cs.purdue.edu/homes/dxu/pubs/CCS07.pdf

・Antfarm: Tracking Processes in a Virtual Machine Environment
http://www.usenix.org/event/usenix06/tech/jones.html

・Implicit Detection of Hidden Processes with a Local-Booted Virtual Machine
http://www.sersc.org/journals/IJSIA/vol2_no4_2008/5.pdf

ではまた。