[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 メーリングリストの案内