そういえばAdvent Calendarの時期だったので昨日書いたスクリプトを供養してみる。
この記事はセキュリティツール系 Advent Calendar 2018 13日目の記事です。
実行パス差分ビューワーというのは同じプログラムに対して2つの入力を与えた時の実行パスの差分を可視化するツール。(名前は今適当に考えた)
例えば以下のような"AB"という文字を受理するプログラムがあるとして、
#include <stdio.h>
#include <stdlib.h>
void one_match() {
puts("One match");
}
void all_match() {
puts("Accepted!");
}
int main(int argc, char *argv[]) {
FILE *fp;
char buf[32] = {0};
if (argc < 2) {
fprintf(stderr, "usage: ./test <input>\n");
exit(0);
}
fp = fopen(argv[1], "r");
fread(buf, sizeof(char), 31, fp);
if (buf[0] == 'A') {
if (buf[1] == 'B') {
all_match();
return 0;
} else {
one_match();
}
}
puts("Not good");
return 0;
}
"AX"という文字列を与えるとone_match()の後"Not good"、
"AB"という文字列を与えるとall_match()、
とそれぞれ異なる実行パスを通るがこれを良い感じに可視化したい。
$ cat test1.in test2.in
AX
AB
$ ./test test1.in
One match
Not good
$ ./test test2.in
Accepted!
angr+Bingraphvis
今回やりたい事は主に2つ。
- 2つの入力それぞれの実行トレースを記録
- 得られたトレースの差分をCFG上に何らかの形で表示
今回はangrとBingraphvisという物を使った。
Bingraphvisというのはangrの生成したCFGを可視化するためのangr-utilsを支えているコアライブラリ。CFGのノード操作や変形、プロットをやってくれる。
これとangrのQEMURunner(トレーサー)を使って、
- QEMURunnerで2つの入力に対してトレースを記録
- CFGEmulatedでmain関数以降のCFGを生成
- Bingraphvizでトレースの差分に当たるCFGノードを色付け
- CFGをpngとして保存
というのをやった。
ラッパーのangr-utilsを使わなかった理由は、独自のトレース差分を用いたCFG変形を定義して使いたかったため。
先程例に上げたプログラムに対して実行すると下の用な画像を吐いてくれる。
$ python3 input-tracer.py -b ./test -i test1.in,test2.in -v
[+] Opening binary ./test
[+] CFG Cache found
CFG size = 46
[+] Tracing .... test1.in
Size: 46079
[+] Tracing .... test2.in
Size: 46033
[+] CFG processing ....
Graph len= 30
[+] Complete! Saved to outd/input_trace_test_entire.png
赤がtest1.inで青がtest2.inを与えた時のパス。共通は黒。
CFGFastを使って関数ごとにプロット
UNIXユーティリティのようなプログラムに対して上記を行うと、とてつもなく巨大な(ノード数5000〜)CFGをプロットしてしまい数万ピクセルの画像が生成されてしまう。当然ビューワーも表示できずに落ちるので可視化の意味を損なう。
CFG全体でなく差分のあるノードのみ表示する事もできるが(上のコマンドから-vを抜く)、差分のみでも非常に大きい場合がある。
そこで、プログラムの関数ごとに画像を分けてCFGをプロットする機能も付けた。
- QEMURunnerで2つの入力に対してトレースを記録
- angrのCFGFastでバイナリ内で定義されている関数一覧取得。
- 各関数ごとに上記と同じ処理を実行
-fオプションを付けると有効化。
$ python3 ./input-tracer.py -b mp3_player -i invalid.mp3,1.mp3 -f
[+] Opening binary mp3_player
[+] Searching for all the functions (using CFGFast)
100% |#####################################| Elapsed Time: 0:00:02 Time: 0:00:02
==> 106 functions to process.
[+] Tracing .... invalid.mp3
Size: 1305732
[+] Tracing .... 1.mp3
Size: 6084333
[+] CFG processing ....
[+] (0/106) Computing Accurate CFG for function _init (0x8049cd8)
[+] CFG Cache found
Graph len= 0
[+] Complete! Saved to outd/input_trace_mpg321-0.3.0__init.png
[+] (1/106) Computing Accurate CFG for function sub_8049d0c (0x8049d0c)
[+] CFG Cache found
例えば"ABCD"のような明らかにinvalidなmp3データとvalidなmp3データでプレイヤーに与えてみる。
outdに各関数ごとのCFG差分がプロットされる。
calc_lengthというmp3プレイヤー内の関数の差分を見てみると以下のようになっている。
ソースコード
コードは以下にある
https://gist.github.com/RKX1209/3cb60b0fa0ba92da6575716680f32aa0