Mais conteúdo relacionado
Semelhante a how to port * to BitVisor (20)
how to port * to BitVisor
- 3. 本日お話しすること
● LVisorについて
● LVisorの実装上の課題
○ BitVisorのどの部分で動かすのか
○ どうやって呼び出すのか
○ エラーが起きたらどうするのか
○ 依存ライブラリはどうするのか
○ etc.
● これらを解決する方法を検討
○ BitVisorに外部のプログラムを移植した例は多くあるが,
ソースコードが公開されているものは少ない
○ 初期実装ではひたすらアドホックに試した → 後に限界が見えてきた
■ なるべくソースコードを改変する必要がない方法を模索
○ 実装する上での具体的な手順について共有
- 4. LVisorの背景
● VMMに汎用スクリプト言語を組み込むモチベーション
○ 比較的簡単に複雑な処理を記述可能
○ eval相当の機能がある場合は動的にプログラムを追加可能
○ 通常VMMに動的に機能を追加するのは難しい
● BitVisorにVMI (Virtual Machine Introspection) があるとうれしい
○ ゲストOSから不可視
■ マルウェア解析環境として適している
○ 他のVMMに比べてパフォーマンスが優れている
■ 解析効率の向上
● BitVisorにLuaとLibVMIを組み込んだ
○ 普通のBitVisorでは処理を追加するために Cで直接VMMを書き換える必要がある
○ スクリプト言語でBitVisorを使いたい
○ LibVMI+シンボル情報でゲスト OS解析の補助
○ Luaのプログラムをルールとしてプロセスやネットワークを監視
- 5. Luaを選んだ理由
● Luaの特徴
○ (比較的) 早い
○ バイナリが小さい,依存ライブラリが少ない (最低限libc, libmのみ)
○ 記述方法が多様
■ 手続き型
■ プロトタイプベース
■ など
○ インターフェースとして有用
● BitVisorの特徴
○ 早い
○ VMMが小さい
○ 拡張のハードルが高い
■ processなどを書いてビルドし直す必要がある
● BitVisorに組み込む上でLuaは比較的相性が良い
- 7. LVisorの例
● 使用例
○ /tmp/ で始まるバイナリが起動 (execve) された時に全てのネットワーク通信を遮断
○ 現状プロセス単位の通信は制御できない
denyall = function(eth)
return DROP
end
execve_hook = function(args)
filename = args[0] -- execveの第一引数 (filename) へのポインタを取得
filename_s = pmem.read(filename, 0x100) -- filenameを文字列で取得
-- /tmp/で始まるバイナリの場合全てのネットワーク通信を遮断
if string.match(filename_s, “^/tmp/”) ~= nil then
net.eth.hooks.add(“*”, “*”, “denyall”, denyall)
end
end
syscall.hooks.add(“netproc”, execve_hook)
- 10. LVisorの実装方針
● Ring 0で動かすのは得策ではない
○ 移植するプログラムが巨大であるため
● 基本的にvpnやstorage等と同じ方針をとる
○ 必要な部分だけmsgbufでやりとりする
○ Lua,LibVMIは保護ドメインで動作させる
● まずLuaのインタプリタを組み込み,次にLibVMIを移植
● 環境
○ Lua: 5.3.3
○ LibVMI: 2016年9月時点でのGitHubの版
- 11. BitVisorのビルドシステムおさらい
● makeをベースとしたビルドシステム
○ 下の階層をビルドした結果を上の階層にリンク
○ オブジェクト (.o) を作る場合は subdirs-1 に追加
○ 静的ライブラリ (.a) を作る場合は asubdirs-1 に追加
○ objs-1 にオブジェクトファイルを追加することでビルド対象を決定
○ CFLAGSを書き換えることでコンパイルオプションを調節
○ 基本的にカレントディレクトリは一番上 (bitvisor/) になることに注意
● processのビルド
○ $(name)-objs にオブジェクトファイルを追加
○ $(name)-libs に依存ライブラリ (.a) を追加
○ bins-1 にprocessとして登録する名前を追加
- 12. リンクについて
● BitVisorは基本的にstatic link
○ dynamic linkがサポートされていない
○ そもそもサポートする必要がない (再利用されるコードが極めて小さいため )
○ バイナリのローダは存在するがライブラリのローダは存在しない
● Ring 0の部分は最終的に全て同じ階層でリンクされる
○ どこかにシンボルが存在していればよい
○ processからリンクしたい場合は process-dependsに加える必要がある
- 15. Luaの組み込み方法
● Luaは比較的移植がしやすい
○ 依存ライブラリが少ない
○ 全てのソースコードが同じ階層に存在し,コンパイルしたオブジェクトをリンクするだけ
■ ビルドシステムへの負担が少ない
○ 静的ライブラリ (.a) を作成できればよい
● BitVisorのビルドシステムに組み込む
○ BitVisorのビルドシステムを理解する
○ process/ 以下にluaのソースコードを丸ごとコピー
○ LuaのMakefileをBitVisor向けに編集 (objs-1にLuaのプログラムを追加していくだけ )
○ process/MakefileにLuaに関わる部分を追記
● これだけでは動かない
○ libcが必要
○ BitVisorは部分的にしかlibcの関数をサポートしていない
- 16. libcの組み込み方法
● 必要なlibcを移植するのは大変 → 既存のlibcを移植する
● どのlibcを使うか?
○ eglibc
○ uClibc
○ newlib
○ musl libc
○ etc.
○ 比較的サイズが小さくビルドも簡単な musl libcを採用
● BitVisorに合わせた変更が必要
○ ファイルシステムおよび fdの概念が存在しない
○ BitVisorのprocess/libでサポートしている関数に注目
■ 標準出力 (printf)
■ 標準入力 (lineinput)
■ メモリ管理 (alloc/free)
○ 最低限これらの機能と接続しないと動作しない
- 17. libcの組み込み方法
● muslの関数を編集
○ 標準出力
■ __fwritexでputcharを使う (process/lib/lib_putchar.c)
■ muslのprintfを使えるようにするため
● BitVisorのprintfは浮動小数点系のフォーマット指定子に対応していない
○ 標準入力
■ libc側で対応せずにprocessの入力を受け取るところで対処
○ メモリ管理
■ malloc
● process/lib/lib_mm.c:alloc を使うように修正
■ free
● process/lib/lib_mm.c:free を使うように修正
■ realloc
● size == 0の時のバグを修正
■ 確保可能な領域は各 processでheap, heaplenを定義して調整
- 18. libcの組み込み方法
● ビルドシステムの修正
○ muslのMakefileをBitVisorに対応させるのは困難
○ BitVisorのmake中にmuslのmakeを走らせるようにする
■ cleanを回避する場合は ifndef clean_p の中に書く (clean時に定義される変数の一つ )
● リンク方法の検討
○ プロジェクトの.aを直接リンクする (ビルド時に生成された .aを指定)
○ BitVisorのビルドシステムで.aを作る (ビルド時に生成された .oを集める) ← こちらを採用
● 例: include makefiles/musl.mak
ifndef clean_p
$(info building musl...)
$(info $(shell cd $(musl_dir) && make -j4 && cd - >/dev/null))
objs-1 += $(musl_o)
endif
- 19. Luaの組み込み方法 (続き)
● Luaのインタプリタを用意する
○ lua.cをprocess向けに編集
○ lua_writelineマクロとlua_readlineマクロをそれぞれ編集
■ printfとlineinputを使うように修正
○ main関数を修正
■ setlimitを呼び出して大きくスタックを確保するように修正
● 標準ではスタックが足りなくてクラッシュする
■ pushcfunction~reportまで削除し,代わりに doREPLを呼び出す
● argc, argvが存在しないためpushcfunctionが使えない
■ exitprocessで終了
● 浮動小数点レジスタのサポート
○ VMM起動時にCR0を操作して有効化しておく (一時的な措置)
● debugshのprocessからLuaインタプリタが使用可能に
- 20. LibVMIの組み込み方法
● LibVMIはlibcの他にGlib, json-cに依存
○ libcと同様の方法でビルドシステムに組み込む
● LibVMIのBitVisor対応
○ XenをベースにBitVisor用のdriverを作成
■ ゲストOSのCPUの読み書き
■ ゲストOSの物理メモリの読み書き
■ ゲストOSの電源操作 (pause, resume, halt)
■ メモリサイズの取得
○ CPUレジスタ操作,メモリ操作があれば理論上は動作する
○ rekallのprofileを用意
■ 現状はファイルシステムが無いのでバイナリの dataセクションに埋め込む
■ objcopyでJSONを変換
■ サイズが非常に大きい (Linuxで約3.3M) ため改善の必要あり
- 21. LibVMIの組み込み方法
● ビルドシステムの修正
○ LibVMIのビルドシステムはKVM, Xenなどの本来対応する VMM用のライブラリに深く依存
○ BitVisor用のdriverをビルドするようにMakefileを作成
■ libvmi/およびその下のarch, driver, os以下
● 例: include makefiles/libvmi.mak
CFLAGS += $(libvmi_INCLUDE)
curdir = $(libvmi_dir)/driver
subdirs-1 += bitvisor
obj = $(subst .c,.o,$(shell find $(curdir) -maxdepth 1 -name "*.c"))
objs-1 += $(obj:$(curdir)/%=%)