拾い物のコンパス

まともに書いたメモ

PEMUを動かしてみる

Dynamic Binary Instrumentation(DBI)を行うフレームワークの一つ,PEMUを使ってみた.githubの説明が簡潔すぎて動くまでに時間がかかったから,手順を書き残す.
pemu自体はqemuをベースに作成されているから,作業の所々にQEMU関連の操作が入る.

OSの準備

Linux上作業を行う.今回はVirtualboxUbuntu仮想マシンを作成し,その上で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_GetSyscallReturnPIN_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ならではな機能がいまいち理解できていない.
気が向いたら色々試してみたい.

参考

PinからPEMUへ | 一生あとで読んでろ

GitHub - utds3lab/pemu

[SOLVED] insmod error "operation not permitted" as root

第278回 Ubuntuカーネルとの付き合い方:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社