PEMUを動かしてみる
Dynamic Binary Instrumentation(DBI)を行うフレームワークの一つ,PEMUを使ってみた.githubの説明が簡潔すぎて動くまでに時間がかかったから,手順を書き残す.
pemu自体はqemuをベースに作成されているから,作業の所々にQEMU関連の操作が入る.
OSの準備
Linux上作業を行う.今回はVirtualboxでUbuntuの仮想マシンを作成し,その上でPEMUを動かした.
仮想マシン(VirtualBox)上で仮想マシン(QEMU)を動かすことになるから,動作が重かった.
以降,ホストOSは仮想マシン上のUbuntu 12.04 32bit.ゲストにも同じバージョンのUbuntuを使った.下手に新しいバージョンのUbuntuを使うとPEMUのコンパイルエラーが起きて厄介.
ホストOSのインストール
http://releases.ubuntu.com/12.04/
辺りからISOファイルをダウンロードして,仮想マシンを作成する.Desktop版がおすすめ.
$ uname -a Linux pemu-VirtualBox 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:50:54 UTC 2014 i686 i686 i386 GNU/Linux
ホスト側での作業
$ sudo apt-get update && sudo apt-get upgrade $ sudo apt-get install p7zip-full qemu-utils git qemu
QEMUで動かすゲストをダウンロード.
一から作成するもの手間だったから,出来合いのものを使った.
https://virtualboxes.org/images/ubuntu-server/
から適当なイメージを取ってくる.バージョン違いによる諸問題を避けるため,12.04(32bit)を使った.他のバージョンは試していない.Desktop版だと重すぎるからServer版を使用.
$ wget http://sourceforge.net/projects/virtualboximage/files/UbuntuServer/12.04/ubuntu-12.04-server-i386.7z $ 7z x ubuntu-12.04-server-i386.7z VBox向けのイメージをQEMU用に変換. $ qemu-img convert ubuntu-12.04-server-i386.vdi -O qcow2 ubuntu32_1204.qcow2 $ qemu-system-i386 -m 512 ubuntu32_1204.qcow2 -monitor stdio # ゲストOSの起動
ゲストのログインパスワードは
Username: ubuntu Password: reverse
PEMUの準備
PEMUのREADME.mdの一番最初に書いてあることをやっていく.task-infoのカーネルモジュールを実行して,結果をlinux.cへと移植する.
移植を忘れるとプロセスの解析が上手く行かない.
ゲストOS上での作業
<guest>$ sudo loadkeys us # キー配列がおかしくなっていたときのみ <guest>$ sudo apt-get update <guest>$ sudo apt-get -y install git gcc make linux-generic この部分は時間がかかるから,CPU数を増やすかのんびり待つ. <guest>$ sudo reboot <guest>$ git clone https://github.com/utds3lab/pemu <guest>$ cd pemu/task-info/task-info.c && make <guest>$ sudo /sbin/insmod task-info.ko
メッセージとして
insmod: error inserting 'task-info.ko': -1 Operation not permitted
と出れば成功のようだ.
<guest>$ dmesg | tail -n10
出力の最後にカーネルモジュールからのメッセージが入っている.今回は
0xC1820FE0, /* task struct root */ 444, /* offset of task_struct list */ 472, /* offset of mm */ 44, /* offset of pgd in mm */ 748, /* offset of comm */ }
これをメモして,一旦ゲストを終了.メッセージの値はゲストによって違うようだ.
以降,ホストに戻る.
ホスト上でPEMUのコンパイル
$ git clone https://github.com/utds3lab/pemu $ cd pemu $ vim ./target-i386/PEMU/linux.c
出力に合わせて./target-i386/PEMU/linux.c
の25行目以降を書き換える.
次にPEMUをコンパイルしていく
$ sudo apt-get -y install autoconf dh-autoreconf # READMEに書いてはないが,いれないとエラーが出る $ sudo apt-get -y build-dep qemu $ mkdir build && cd build $ ../myconfig && make install $ cd ../plugins && make $ cd ../build/bin && ./qemu-system-i386 -m 512 ubuntu32_1204.qcow2 -monitor stdio (qemu)> pemu ls strace.so # qemuのプロンプトで実行 <guest>$ ls
qemuを動かしているターミナル上で,PEMU_start
から始まる何らかの出力は得られる.これで一応動いたことになる.
ソース解析
出力のPEMU_start
はmonitor.cに書かれている.その後の数字の出力はplugins/strace.cの
VOID SysBefore(ADDRINT ip, ADDRINT num) { fprintf(stdout,"0x%lx: %ld\n", (unsigned long)ip, (long)num); }
のようだ.strace.c の main は
int main(int argc, char*argv[]) { PIN_AddSyscallEntryFunction(SyscallEntry, 0); PIN_AddFiniFunction(Fini, 0); PIN_StartProgram(); return 0; }
のように,いつ関数を呼び出すかを指定している.
出力のip
, num
について詳しく見ると,値PIN_GetSyscallReturn
とPIN_GetSyscallNumber
の戻り値が入っている.
VOID SyscallEntry(THREADID threadIndex, CONTEXT*ctxt, SYSCALL_STANDARD std, VOID*v) { SysBefore(PIN_GetSyscallReturn(ctxt, std), PIN_GetSyscallNumber(ctxt, std)); }
実装はsystemcall_api.c
にある.出力しているのはシステムコールの戻り値(PIN_GetSyscallReturn: systemcall_api.cでreturn EAXしてる)と,システムコール番号(正確にはLinux/Win/Macのどれかを調べた後,Linuxなら呼び出し時のEAXを返している.まあ,システムコール番号)のようだ.
Linuxで実行する限り,二つの関数で得られる値は同じになるんじゃ・・・.
pemu ls <plugin>
によって,plugins/
のプログラムを呼び出していて,自作も簡単そうだ.
実装としては,monitor.cからplugins以下のプログラムが呼び出される仕組み.
反省
論文に書かれた検証環境のOSバージョンはしっかり守る
所感
とりあえず動くようにはなったが,PEMUならではな機能がいまいち理解できていない.
気が向いたら色々試してみたい.
参考
[SOLVED] insmod error "operation not permitted" as root
第278回 Ubuntuカーネルとの付き合い方:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社