ケーススタディ：
BitVisor におけるバイナリBLOB
USB ドライバの利用

北村 朋宏／株式会社イーゲル
2013/12/6

1

BitVisor powered by IGEL
 導入サポート– プラットフォーム選定、動作検証– インストール、セキュリティポリシー設定

 受託カスタマイズ– 新たなデバイス対応、新機能実装

 ソリューション開拓– USBデバイス隠ぺい（偽装）– ICカード連携画面ロック

 オープンソース・コミュニティ活動– インストールマニュアル作成– Realtek RTL8169ドライバ、 ADvisorのマージ

2013/12/6

BitVisor Summit 22

イーゲルの BitVisor との関わり
USB対応(USBメモリ暗号化、libusb互換API for ICカード）

ATAPI対応(光学メディア暗号化）

保護ドメイン
(VMM内Ring-3で動作）

ATA piggyback

サスペンド対応
バックグラウンドFDE(VMMからディスクI/O)

TPM対応(Trusted Boot、乱数生成器)(OS動作中のFull DiskEncryption)

USBドングル利用

最新VT機能の対応(バイナリライブラリのリンク)(性能向上)UEFI 対応2013/12/6：私が関わったもの
BitVisor Summit 2

3

背景USB ドングル（Rockey6）を BitVisor 内部で利用したい。しかし、ライブラリはソースコード未公開。何か方法はないか？
ライブラリをバイナリ BLOB としてBitVisor に取り込めれば利用できる可能性がある。

2013/12/6

BitVisor Summit 2

4

Rockey6 とは？

Rockey6 は、プロセッサを内蔵したUSBドングル製品

対象ソフトウェアの重要なロジックをドングル内に移すことでプロテクトを行う。
ＰＣとのインターフェースは、USB1.1、HID デバイスとして認識され、インタラプト転送で通信
2013/12/6

BitVisor Summit 2

5

Rockey6 アクセス API
Rockey6 デバイスへのアクセスは、以下 API が使われる。
•Rockey6 デバイスアクセスのAPI シーケンス

1. DIC_Find で Rockey6 デバイス有無の検査
2. DIC_Open でデバイスを Open3. DIC_Command，DIC_Set でデバイスを操作
4. DIC_Close でデバイスを Close

2013/12/6

BitVisor Summit 2

6

Rockey6 ＡＰＩ を含むオブジェクト
Rockey6 API は、 Rockey6 SDK 付属ライブラリアーカ
イブファイル（libRockey6SmartPlus.a）にて提供

RockeySmart.o
xxx.o
yyy.o…

アーカイブから ar –x でオブジェクトを取り出し、APIの含まれるオブジェクトを探した結果、 RockeySmart.oに含まれる。

libRockey6SmartPlus.a

この RockeySmart.o を BitVisor にリンクできれば、BitVisor 内から Rockey6 API を使えるのでは？
2013/12/6

BitVisor Summit 2

7

Rockey6 ライブラリが依存するシンボル（１）RockeySmart.o を objdump –x し、”UND”で grep
オブジェクト内の未定義シンボルを検索
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000*UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_*UND* 0000000000000000 memcpy*UND* 0000000000000000 memset*UND* 0000000000000000 time
*UND* 0000000000000000 memcmp*UND* 0000000000000000 __stack_chk_fail*UND* 0000000000000000 pthread_mutex_init*UND* 0000000000000000 usb_bulk_write*UND* 0000000000000000 usb_bulk_read*UND* 0000000000000000 lock
*UND* 0000000000000000 pthread_mutex_lock*UND* 0000000000000000 pthread_mutex_unlock*UND* 0000000000000000 unlock（以下略）2013/12/6

BitVisor Summit 28Rockey6 ライブラリが依存するシンボル（２）Rockey6 ライブラリの未定義シンボルを整理し、それらを分類•••••

C ライブラリ関数 (atol, atoi, memcpy, strlen …)
pthread 関数 (pthread_mutex_lock …)
libusb 関数 (usb_init, usb_find_bussees …)
stack guard (__stack_chk_fail)Global offset table (__GLOBAL_OFFSET_TABLE)

これらの未定義シンボルを解決すれば、Rockey6 ライブラリを BitVisor に取り込めるはず。
2013/12/6

BitVisor Summit 29実装方針（１）
未定義シンボルの解決方法について考慮
Cライブラリ関数、pthread 関数••BitVisor 内に同等の実装、代用品があるものは、それを利用（sprintf -> snprintf、pthread_mutex_lock ->spinlock_lock）
同等品が無い場合は、スタブとして追加実装 （atoi、atol）

libusb 関数•

2013/12/6

BitVisor は libusb 互換実装を既に持っているため、それを利用
libusb互換実装は、本来の libusb との差異により問題が生じる可能性があるが。
BitVisor Summit 210

BitVisor libusb 互換実装
libusb は、USB を利用するためのＣライブラリ
BitVisor は、 BitVisor の IC カード実装から利用するため、libusb 互換実装を備えている。
• drivers/usb/usb.h （一部抜粋）/* usb.c */usb_dev_handle *usb_open(struct usb_device *dev);
int usb_close(usb_dev_handle *dev);
int usb_get_string(usb_dev_handle *dev, int index,int langid, char *buf, size_t buflen);

usb_init、usb_find_busses、usb_find_devices、usb_interrupt_write(read)などがRockey6 API から使われていた。
2013/12/6

BitVisor Summit 2

11実装方針（２）
残りの未定義シンボル（赤カッコ）への対応
••
•
•
•

C ライブラリ関数 ＞ スタブを実装
pthread 関数＞ 同上libusb 関数＞ 既存 libusb 実装を利用
stack guard (__stack_chk_fail)Global offset table (__GLOBAL_OFFSET_TABLE)

stack guard(__stack_chk_fail)はスタブを実装
Global offset table は、リンクエラー要因にはならないため、特に対応策を意識せず進めた。

2013/12/6

BitVisor Summit 2

12実装方針（３）実装構成図
VMM core
RockeySmart.o
スタブ実装

libusb 互換実装
USB driver
USB ホストコントローラー

Rockey 6
ライブラリ
2013/12/6

追加実装

既存部

BitVisor Summit 2

Rockey6
USB 1.1（インタラプト転送）
13

実装方針（４）追加ファイルの構成./bitvisor|-- rockey6/||-- Makefile||-- RockeySmart.o（Rockey6 API オブジェクト）||-- dic32.h（Rockey6 API ヘッダファイル）|`-- stub.c（スタブ実装）|-- core|-- rockey6_samplecodes.c （Rockey6 サンプルプログラム）|-- rockey_test.c（BitVisor デバッグシェルにrockey コマンドを追加）

Rockey6 のサンプルプログラムを core の中に追加し、BitVisor
デバッグシェルからサンプルプログラムを起動できるよう実装

2013/12/6

BitVisor Summit 2

14

Rockey6 サンプル動作イメージ
BitVisor デバッグシェルから Rockey6 プログラムを実行（イメージ）> rockeyrockey>
5
execute test 5=== rockey6_testcase5 ===Find Rockey6Smart: 1
Open Rockey6Smart:
FactoryTime 4BF6ＸＸＸＸ
HardSerial
7502ＸＸＸＸ
ShipTime
4BF6ＸＸＸＸ
COSVersion
02.02（サンプル5 は、Rockey6 デバイスのシリアル等のハード情報を取得するもの）

2013/12/6

BitVisor Summit 2

15

実装終了
一通りスタブ部の実装を終えて、Rockey6 サンプルプロ
グラムを VMM コアにリンクし、動作テスト
DIC_Find してRockey6デバイスの検出を試みると、Find で早速、問題発生•Rockey6 デバイスアクセスのAPI シーケンス

1. DIC_Find で Rockey6 デバイス有無の検査
2. DIC_Open でデバイスを Open
3. DIC_Command，DIC_Set でデバイスを操作
4. DIC_Close でデバイスを Close
2013/12/6

BitVisor Summit 2

16

DIC_Find 問題調査
DIC_Find の問題• PageFault で panic
原因
1. libusb 互換実装の usb_bus 構造体の差異
2. Rockey6 オブジェクトの stack guard
3. Rockey6 オブジェクト GOT セクションの不適切な位置への配置• Rockey6 デバイスが見つからない
原因• libusb 互換実装へ追加した usb_busses の実装

2013/12/6

BitVisor Summit 2

17

usb_bus 構造体の差異
DIC_Find で Page fault した原因は、libusb の usb_bus 構造体のメンバ devices の位置が異なっていたため。
libusb の usb_bus 構造体の内容が BitVisor では必要最小限のみ実装されていたので、本家の libusb の構造体と全く配置及びサイズになるようにメンバを追加（太字部分）
struct+++++-

usb_bus {LIST_DEFINE(usb_busses);
LIST_DBL_DEFINE(usb_busses);char filename[4097];
struct usb_device *devices;
unsigned int location;
struct usb_device *root_dev;
struct usb_host *host;struct usb_device *device;
u8 busnum;/* unused *//* unused *//* unused */};
2013/12/6

BitVisor Summit 218stack guard（１）gcc により付加されるスタック破壊検出の仕組み
stack guard は fs レジスタが指すアドレスにスタック破壊検出用のデータ（カナリア）を用意する。BitVisor 本体は stack guard を使っておらず、fs レジスタも未使用だったため、fs レジスタにカナリアを指す値が設定されておらず、 page fault した。
• 関数入口でのカナリアの読み出し
4288:
4289:
428c:
428d:
4294:
429b:
429d:

2013/12/6

55
48 89 e553
48 81 ec 88 00 00 00
64 48 8b 04 25 28 00
00 00
48 89 45 e8
mov

push
mov
push
sub
mov

%rbp
%rsp,%rbp%rbx$0x88,%rsp%fs:0x28,%rax

カナリア%rax,0xffffffffffffffe8(%rbp)

BitVisor Summit 219

stack guard（２）• 関数出口でのカナリアのチェック
771f: 48 8b 55 e87723: 64 48 33 14 25 28 00
772a: 00 00
772c: 74 05
772e: e8 00 00 00 00
772f: R_X86_64_PLT32
7733: 48 81 c4 a8 04 00 00
773a: 5b773b: c9

mov
xor

0xffffffffffffffe8(%rbp),%rdx%fs:0x28,%rdx

je7733 <DIC_Command+0x2d75>
callq 7733 <DIC_Command+0x2d75>__stack_chk_fail+0xfffffffffffffffc
add$0x4a8,%rsp
pop%rbx
leaveq

また、32 bit ビルドだと、stack guard は、 gs レジスタを使用する。gs レジスタは BitVisor も利用しており、お互いに干渉しない配慮が必要になる。
gs レジスタ干渉、カナリアの算出を考えると stack guard サポー
トは面倒なので、Rockey 6 ライブラリから stack guard オプショ
ンを除いてビルドして貰うことで対応
2013/12/6

BitVisor Summit 220

GOT セクションの問題（１）GOT(.got、.got.plt )セクションは、ライブラリがPIC（PositionIndependent Code）でビルドされている場合に追加される。
Rockey6 ライブラリが PIC であったため、bitvisor.elf のイメー
ジに .got、.got.plt セクションが追加されたが、BitVisor のリンカスクリプトには、これらの配置位置が記述されていなかった。
• bitvisor.elf の objdump –x （.data以降抜粋）
7 .data

00952000 40265000 00265000 00165000 2**12
CONTENTS, ALLOC, LOAD, DATA
8 .got00000030 40bb7000 00bb7000 00ab7000 2**3
CONTENTS, ALLOC, LOAD, DATA
9 .got.plt 00000018 40bb7030 00bb7030 00ab7030 2**3
CONTENTS, ALLOC, LOAD, DATA

2013/12/6

BitVisor Summit 221

GOT セクションの問題（２）
結果、ヒープ領域にこれらのセクションが割り当てられ、PageFault を起こしていた。
• bitvisor.lds（.data 以降を一部抜粋）
これは、リンカスクリプトで明示的に .got、.got.plt
セクション配置箇所を指定することで解決できたが、最終的には、-fPICオプションを除いてビルドして貰うことで解決

2013/12/6.data : AT (phys + (data - head)) {data = .;*(.data). = ALIGN (8);__initfunc_start = .;*(.initfunc)__initfunc_end = .;/* }.bss : AT (phys + (bss - head)) { */bss = .;*(.bss)*(COMMON). = ALIGN (4096);}
end = .;
BitVisor Summit 2

22

usb_busses の実装
DIC_Find は、libusb の global 変数 usb_busses を直接参照している。libusb 互換実装には、 usb_busses は未実装であるため、追加したが、usb_busses がusb_bus のリスト末尾を指していた。
（先頭を指しているのが正しい）※usb_busses は直接アクセスしないで、libusb のusb_get_busses API を使うべきだが、Rockey6 のライブラリは usb_get_busses を使っていなかった。
headnext

next

next

usb_busses
2013/12/6nextNULL

usb_busses
BitVisor Summit 223

DIC_Find 成功
DIC_Find で Rockey6 デバイスが見つかるようになった。DIC_Open は？
•Rockey6 デバイスアクセスのAPI シーケンス

1. DIC_Find で Rockey6 デバイス有無の検査
2. DIC_Open でデバイスを Open
3. DIC_Command，DIC_Set でデバイスを操作
4. DIC_Close でデバイスを Close

2013/12/6

BitVisor Summit 224

DIC_Open 問題発生
DIC_Open でも問題が発生•Rockey6 デバイスアクセスのAPI シーケンス

1. DIC_Find で Rockey6 デバイス有無の検査
2. DIC_Open でデバイスを Open
3. DIC_Command，DIC_Set でデバイスを操作
4. DIC_Close でデバイスを Close

2013/12/6

BitVisor Summit 225

DIC_Open 問題調査
DIC_Open の問題• PageFault で panic
原因libusb 互換実装の usb_device 構造体の差異• DIC_Open が終了しない
原因
スタブ関数での実装ミスでのループ＞バイナリ BLOB 内での問題を疑い、DIC_Open の動作をデバッグ

2013/12/6

BitVisor Summit 2

26

DIC_Open デバッグ（１）
レジスタ内容をスタックに積みダンプするコードを作成
バイナリエディットでバイナリ BLOB 中の関数入口で スタックを進める“sub $0x260, %rsp” をデバッグコードを呼ぶ “call hoge”（e8 xx xx xx xx)に置き換える。
.global hoge
hoge:
push %rax（中略）push %r15
mov %rsp,%rdi
call dump_regs
pop %r15（中略）
pop %rax
sub $0x260-8,%rsp
jmp *0x260-8(%rsp)
2013/12/6

voiddump_regs(struct stack *stack){printf ("ret = %lx¥n", stack->ret);printf ("rdi = %lx¥n", stack->rdi);（中略）}

これらのコードで、DIC_Open 内のあるルーチンが呼び出された回数及びレジスタ状況をダンプして把握：呼び出しの流れ
BitVisor Summit 227

DIC_Open デバッグ（２）ジャンプ命令 or “nop “ を “int 3” （0xcc: software
breakpoint）に置き換え、panic を起こし、スタックの中身を確認
call 命令を nop に置き換え
色々とデバッグした結果、DIC_Open 中の strcpy で実行が止まっていた。 strcpy の call を nop にバイナリでエディットし、結果を確認＞ nop に置き換えると、DIC_Open が正常終了
オープンしたデバイスで、Rockey6 のデバイス情報取得成功
2013/12/6

BitVisor Summit 228

解決
DIC_Open が動作した後、DIC_Open、DIC_Command、DIC_Close まで動作
•Rockey6 デバイスアクセスのAPI シーケンス

1. DIC_Find で Rockey6 デバイス有無の検査
2. DIC_Open でデバイスを Open
3. DIC_Command，DIC_Set でデバイスを操作
4. DIC_Close でデバイスを Close

2013/12/6

BitVisor Summit 229

まとめ
DIC_Find、DIC_Open API が動作するまで、予想外で不慣れな対応項目もあり、大変だった。
••••

2013/12/6構造体のオフセット
stack guard
GOT
トラブル時のバイナリ BLOB デバッグ

BitVisor Summit 230