Intel VT-xを使ってみた

id:rootkitの人に会うのに手ぶらで行けるわけがない!


というわけで(?)私もvmxcpuをリファクタリングして、Intel VTを使った仮想マシンモニタのコア部分を書いてみました。独自性のないデモソース(動作環境:私のPC!)です。


嬉しいのでエントリーにしてますが、vmxcpuを理解している人にとっては見るべきものは無いです^^;


これは仮想マシンモニタといっても、大多数の人(IT技術者)がイメージするようなOS on OSを実現する凄まじいもの(これは本当に凄まじいのです)ではなくて、単にプロセッサモードをVMX operationモードに切り替えて、いくつかの命令を適切にトラップして、最後に正しく終了するだけのものです。
一応、マルチコアに対応していたり、アセンブラを極力排除したり、日本語でコメントを書いたりしていますが、特段面白い要素はないです。Core i7のPCがあればEPTを使ってみたかったのですが、あとはIntelのマニュアルを見つつ使いどころを探して拡張していく感じですね。


ところで、本当に仮想「マシン」を作り上げるには、命令のトラップ(プロセッサの偽装)だけではなくて、ページング機構(MMU)やIOも偽装してあげなければいけません。しかしこれは簡単なものではないのです*1。夢想しないように*2


もっとも、そこまでいかつい実装をしなくても、VT-xだけでもかなり面白いことができそうな気がします。感覚的にははじめてAPIフックを知ったときに近いトキメキが☆

実装面で参考になるURL

03/20 追記

実をいうとIntelのマニュアル嫁としか(笑)。以下そのメモ。

26.4 VMX命令を使う

VMX命令は、vmx root operationモードでのみ使用できる。non-root operationモードで使用すると、VMExitを引き起こす。
VMCSポインタが正しくない場合RFLAGS.CFに1が設定される。VMCSポインタが正しい場合、RFLAGS.ZFに1が設定され、そのVMCSのエラーフィールドにエラーコードが設定される。

26.5 VMMの初期化と破棄
  1. VMX命令を実行するときはプロセッサがページングが有効なプロテクトモードでなければならない。
  2. VMMは権限レベル0(Ring-0)で動作しなければならない。
  3. CPUID命令を利用してVMX命令がサポートされているか確認する。
  4. MSRを確認してサポートされるVMX互換性を調査する。
  5. 非ページメモリにVMXON領域を確保する。この大きさはIA32_VMX_BASIC MSRによって調べることができ、4KB境界に配置されなければならず、cache-coherentメモリでなければならない。
  6. 互換性MSRによって報告されたVMCSリビジョンIDで、VMXON領域の先頭32bitを初期化する。
  7. CR0レジスタの値を設定する。PE/PGは必ず1で、ほかのビットはIA32_VMX_CR0_FIXED0とIA32_VMX_CR0_FIXED1 MSRを通して決定する。
  8. CR4レジスタの値を設定する。VMXEは必ず1で、ほかのビットはIA32_VMX_CR4_FIXED0とIA32_VMX_CR4_FIXED1 MSRを通して決定する。
  9. IA32_FEATURE_CONTROL MSRのlockビット(ビット0)が1であることを確認する。これはBIOSでのVTサポートの設定を確認することを意味する。
  10. VMXON領域の物理メモリアドレスを渡してVMXON命令を発行する。


これでプロセッサは VMX root operationモードになる。VMX root operationモードのときVMXOFF命令を発行するとVMX root operationモードを終了する。

26.6 仮想マシンの準備と起動
  1. 非ページメモリにVMCS領域を確保する。この大きさはIA32_VMX_BASIC MSRによって調べることができ、4KB境界に配置されならない。
  2. 互換性MSR(IA32_VMX_BASIC)によって報告されたVMCSリビジョンIDで、VMCS領域の先頭32bitを初期化する。
  3. VMCS領域の物理メモリアドレスを渡してVMCLEAR命令を発行する。これは現在のVMCS領域を"clear"状態にして無効にすることを意味する。
  4. VMCS領域の物理メモリアドレスを渡してVMPTRLD命令を発行する。これは現在のVMCS領域を新しいVMCSで初期化することを意味する。
  5. 現在のVMCS領域のhost-state area fieldsをVMWRITE命令によって初期化する。細則についてはCHAPTER 22を参照すること。
    1. ゲストからのVM exitで呼び出されるVMMエントリーポイント
    2. コントロールレジスタ(CR0/CR3/CR4)を含むホスト状態フィールド
    3. セグメントレジスタセレクタフィールド(CS, SS, DS, ES, FS, GS and TR)
    4. ベースアドレスフィールド( FS, GS, TR, GDTR and IDTR; RSP, RIP, fast system callsを制御するMSR)
  6. 現在のVMCS領域のVM-exit control fields、VM-entry control fields、VM-execution control fieldsをVMWRITE命令によって初期化する。これらの値はVMX互換性MSRによって報告された値を設定し、矛盾する値を設定するとVM enterの失敗を引き起こす。
  7. 現在のVMCS領域のguest-state area fieldsをVMWRITE命令によって初期化する。細則についてはCHAPTER 22を参照すること。
    1. VM entryによるゲストのエントリーポイント
  8. 仮想マシンを起動するためにVMLAUNCH命令を発行する

成功すると親ポインタとして古controlling-VMCSが保存され、現在のVMCSにcontrolling-VMCSが更新される。ゲストVMCSの状態は"clear"から"launched"になる。

*1:VTによってプロセッサの偽装が、EPTによってMMUの偽装が楽になってはいますが、それでも泥臭いコーディングが必要。

*2:その筋の大学の人とかは普通にやってのけるようですが…