高機能トレーサーqiraを使ってみる

qira(QEMU Interactive Runtime Analyser)は、George Hotz(geohot)氏が開発している高機能トレーサーである。 qiraは実行時のレジスタ、メモリ操作をすべて記録することにより、特定の命令アドレスを実行しているタイミングや特定アドレスのメモリ読み書きが行われているタイミングを検索することができる。 また、複数回の実行におけるトレース結果を互いに比較することができる。 ここでは、簡単なプログラムコードを用いてqiraを使った解析をやってみる。

環境

Ubuntu 14.04.4 LTS 64bit版

$ uname -a
Linux vm-ubuntu64 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.4 LTS
Release:        14.04
Codename:       trusty

$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4

qiraのインストール

GithubリポジトリのREADMEを参考にインストールする。

$ git clone https://github.com/BinaryAnalysisPlatform/qira.git
$ cd qira/
$ ./install.sh

ヘルプを表示してみると次のようになる。

$ ./qira --help
usage: qira.py [-h] [-s] [-t] [--gate-trace ADDRESS] [--flush-cache] [--pin]
               [--host HOST] [--web-port PORT] [--socat-port PORT] [-S]
               [--engine ENGINE]
               binary [args [args ...]]

Analyze binary. Like "qira /bin/ls /"

positional arguments:
  binary                path to the binary
  args                  arguments to the binary

optional arguments:
  -h, --help            show this help message and exit
  -s, --server          bind on port 4000. like socat
  -t, --tracelibraries  trace into all libraries
  --gate-trace ADDRESS  don't start tracing until this address is hit
  --flush-cache         flush all QIRA caches
  --pin                 use pin as the backend, requires ./pin_build.sh
  --host HOST           listen address for web interface and socat. 0.0.0.0 by
                        default
  --web-port PORT       listen port for web interface. 3002 by default
  --socat-port PORT     listen port for socat. 4000 by default
  -S, --static          enable static2
  --engine ENGINE       static engine to use with static2 (builtin or r2)

qiraを使って解析してみる

ここでは、「angrでシンボリック実行をやってみる」でも用いた、次のような簡単な認証を行うプログラムを用いて解析してみる。

/* test.c */
#include <stdio.h>
#include <string.h>

void succeeded()
{
    puts("auth succeeded!");
}

void failed()
{
    puts("auth failed.");
}

int main(int argc, char *argv[])
{
    char username[256];
    char password[256];
    printf("Enter username: ");
    scanf("%s", username);
    printf("Enter password: ");
    scanf("%s", password);
    if (strcmp(username, "admin") == 0 && strcmp(password, "letmein") == 0) {
        succeeded();
    } else {
        failed();
    }
    return 0;
}

上のコードをコンパイルし、qiraのもとで実行してみる。

$ gcc test.c -o test

$ ./qira -s ./test
*** program is /home/user/tmp/qira/test with hash e4f0fd7d15338ed95f7fe2914cd19152c0df1251
**** using /home/user/tmp/qira/tracers/qemu/qemu-2.1.3/x86_64-linux-user/qemu-x86_64 for 0x3e
no qira server found, starting it
*** deleting old runs
**** listening on <socket fileno=6 sock=0.0.0.0:4000>
****** starting WEB SERVER on 0.0.0.0:3002

このとき、TCP 4000番ポートでプログラムの標準入出力、TCP 3002番ポートでWeb UIの待ち受けが行われる。

TCP 4000番ポートに接続し、適当な入力を行ってみる。

$ nc -v localhost 4000
nc: connect to localhost port 4000 (tcp) failed: Connection refused
Connection to localhost 4000 port [tcp/*] succeeded!
AAAA
BBBB
Enter username: Enter password: auth failed.

ここで、Google ChromeからWeb UIを開くと、次のようなトレース結果が表示される。

f:id:inaz2:20160318001055p:plain

画面と操作方法について、簡単にまとめると次のようになる。

  • 左上からタイムライン、メモリマップ、control、命令ダンプ、レジスタ、変更リスト、strace、メモリダンプが表示されている
  • controlの各テキストボックスには、現在参照しているchangelist number(青)、fork number(グレー)、iaddr(赤、命令アドレス)、daddr(黄、データアドレス)が表示される
  • 命令ダンプは現在のchangelist number周辺が表示される
  • メモリダンプは現在のdaddr周辺が表示される
  • タイムラインにおいて
    • 緑の明るさは関数の深さ
    • 青のバーはchangelist numberのポイント
    • 赤のバーはiaddrを通過しているポイント
    • 明るい黄色のバーはdaddrに書き込んでいるポイント
    • 暗い黄色のバーはdaddrが読み込まれているポイントを表す
  • 変更リストはそのchangelist numberでのメモリの操作内容を表す
  • 赤色のアドレスについて、クリックで現在のiaddr、右クリックで現在のdaddrをそのアドレスに変更
  • 黄色のアドレスについて、クリックで現在のdaddrをそのアドレスに変更
  • 上下キーで青のバー、j/kで赤のバー、Shift+j/kで黄色のバーを前後に移動、Escで戻る
  • m/,で関数の末尾、先頭に移動
  • gでchangelist number(数字)やiaddr(アドレスあるいは名前)への移動
  • nでiaddrのリネーム、:でコメント
  • タイムラインについて、ドラッグ選択でズームイン、zでズームアウト

g mainと入力しmain関数が実行されたポイントに移動した後、適当に操作することにより、strcmp関数でAAAAadminが比較されていることが確認できる(上記スクリーンショット)。

そこで、あらためてTCP 4000番ポートに接続し、次のような入力を行ってみる。

$ nc -v localhost 4000
nc: connect to localhost port 4000 (tcp) failed: Connection refused
Connection to localhost 4000 port [tcp/*] succeeded!
admin
BBBB
Enter username: Enter password: auth failed.

少し待つと、次のスクリーンショットに示すように、Web UIの左側にあるタイムラインが2列に更新される。

f:id:inaz2:20160318001110p:plain

タイムラインの各列はそれぞれの接続ごとの実行結果を表しており、左右キーで互いに切り替えることができる。 2列目のタイムラインに切り替え、適当に操作することにより、2回目のstrcmp関数でBBBBletmeinが比較されていることが確認できる(上記スクリーンショット)。

再度TCP 4000番ポートに接続し、次のような入力を行うと、認証を通過することが確認できる。

$ nc -v localhost 4000
nc: connect to localhost port 4000 (tcp) failed: Connection refused
Connection to localhost 4000 port [tcp/*] succeeded!
admin
letmein
Enter username: Enter password: auth succeeded!

なお、次のように起動時に-Sオプションをつけることで静的解析によるグラフ表示を行うことも可能である。

$ ./qira -Ss ./test
*** using static

f:id:inaz2:20160318192154p:plain

上のスクリーンショットにおいて、右上はグラフビュー、右下はテキストビューとなっており、Spaceを押すことで操作に追従するビューを切り替えることができる。

関連リンク