[BitVisor-devel:36] BIOS によるストレージ I/O のフックができない問題について

Hideki EIRAKU hdk @ igel.co.jp
2012年 3月 21日 (水) 18:46:42 JST


イーゲル 榮樂です。

BitVisor で、BIOS によるストレージ I/O のフックができない環境があること
がわかりました。現時点でわかっている環境は、Core i7-2640M を搭載した
Lenovo ThinkPad X220 (428747J) です。フックできない理由は、BIOS が直接
AHCI や ATA コントローラーにアクセスするのではなく、BIOS が SMI
(system-management interrupt) を生成することによって SMM
(system-management mode) に移行し、SMM でストレージ I/O が行われてしま
うためです。SMM で使用されるメモリ領域は SMRAM と呼ばれていて SMM でし
かアクセスできない場所にあるため、BitVisor で管理できない部分になります。

SMI の発行は、ポート 0xB2 への書き込みによって行われます。ACPI の規格で
決められている機能がいくつかあって、このポートに書き込む値によってそれ
らの機能が実行されるようです。それ以外にも、機種依存の固有の機能がある
場合があります。

ThinkPad X220 では、このポートに 0x0A を書き込むことによって、ストレー
ジ I/O が行われるようです。これは、おそらく機種依存の機能です。この処理
は、大雑把に書くと、以下のような内容です。

------------------------------------------------------------
1. %ds, %es, %si, %di などのレジスターを適切にセットします。
   どうやら、%ds:%si および %es:%di が指すメモリに、入出力されるデータの
   転送先などが格納されているようです。

2. %dx に 0xB2 をセットします。

3. %ax に 0x0A をセットします。

4. cli - 割り込みを禁止します。

5. cmp %al,%al - 同じ値の比較により、パリティフラグの状態を偶数とします。

6. out %al,%dx - ポート 0xB2 へ 0x0A を書き込みます。

7. jpe . - パリティフラグが偶数を表している間、無限ループします。
------------------------------------------------------------

ThinkPad の場合、4 以降の手順は他の SMI を発行する処理でも同様になって
いるようです。もし 6 で SMI が発行されなかったら、7 で永遠に無限ループ
となります。7 の無限ループの間に SMM に移行して、レジスターの中身が解釈
され処理が実行されて、フラグなどに変更が加えられた上で SMM から戻ってき
て、無限ループを抜ける、という風に動作しているものと思われます。

今回、ストレージ I/O が SMI/SMM を用いていることを突き止めるため、以下
の作業を行いました。

------------------------------------------------------------
1. BIOS の領域を逆アセンブルし、SMI を発行するサブルーチンらしきものを
   見つけました。

2. ディスク BIOS の呼び出し int $0x13 からたどれる範囲で、1. で見つけた
   サブルーチンを呼び出していると見られる部分のひとつを特定しました。

3. FreeDOS を起動して、以下のプログラムを実行しました:
   mov $0x06060606,%eax
   mov %eax,%edx
   mov $0x26e,%ecx
   wrmsr
   inc %ecx
   wrmsr
   int $0x20

   これは、BIOS の領域である 0xF0000〜0xFFFFF のキャッシュの設定を
   Write-Protect から Write-Back に変更するものです。ROM 上でのブレーク
   ポイントが擬似的に使えるようになります。

4. DEBUG コマンドで、以下のようなプログラムを読み込みました:
   mov $0x0201,%ax
   mov $1,%cx
   mov $0x80,%dx
   mov $0x200,%bx
   mov %ax,%es:(%bx)
   int $0x13
   int3

   これは、ストレージの MBR をメモリに読み込み、ブレークポイント命令を
   実行するプログラムです。

5. DEBUG コマンドで、g=100 コマンドにより上記プログラムを数回実行しまし
   た。

6. DEBUG コマンドで、g=100 f000:<address> により、2. で特定したサブルー
   チン呼び出し命令まで実行しました。この時点で、d ss:200 コマンドを実
   行すると、メモリダンプの先頭に 01 02 というデータが見えます。これは、
   ストレージに記録されているデータではなく、上記プログラムにおいて
   int 命令の直前に書き込んでいる値であり、まだストレージからの読み込み
   が行われていないことがわかります。

7. その先を実行しようとすると、PC が再起動するなど不安定な状態になりま
   す。そのため、この時のレジスターの状態と、1. のサブルーチンの内容な
   どから、実際に実行される out 命令を想定して、即席でプログラムを作成
   しました。a ss:180 コマンドを実行し、以下のプログラムを入力しました。
   (DEBUG コマンドの仕様によりこの記述は Intel 表記となります。)

   mov dx,b2
   mov ax,a
   cli
   cmp al,al
   out dx,al
   jpe 18a
   int 3

8. g=ss:180 により 7. で作成したプログラムを実行し、int 3 のブレークポ
   イント命令で実行が停止しました。

9. d ss:200 によりメモリダンプを確認すると、01 02 というデータはなくな
   り、ストレージの内容が読み込まれていました。
------------------------------------------------------------

以上の手順により、SMI/SMM を用いてストレージ I/O が行われていることが確
認できました。

BitVisor に対して機種依存の実装をするならば、SMI の発行の際のレジスター
やメモリの内容がどういうふうになっているかを調査して、BitVisor が代わり
に SMI を発行できるかを確認するなどして、この SMI/SMM を用いたストレー
ジ I/O を BitVisor でフック・データを暗号化したり復号したりする、という
ことも可能ではあります。しかし、これは完全に機種依存の話になるため、今
後出てくる他の機種でどうなるかはまったく不明です。

-- 
Hideki EIRAKU <hdk @ igel.co.jp>



BitVisor-devel メーリングリストの案内