§ 始めに
そろそろ寒くなってきましたね。
こんな時期をなんて言うか知っていますか?
そう!バイナリ解析の季節です!と言うことで今回は今まで逃げ続けてきたバイナリ解析について勉強してみたいと思います。
今回使うのは"radare2"です。
このツールは 静的解析(逆アセンブル) と 動的解析(デバッグ) の両方が扱える便利ツールです!
§ インストール
git clone https://github.com/radareorg/radare2
cd radare2
sys/install.sh
この三つ入れたらいけそうですね。
gitが入っていないといけなそうです...
gitが入っていない人は下記のURLから直接ダウンロードしましょう。
https://github.com/radareorg/radare2
静的解析(逆アセンブル)
§ 最初の手順
静的解析(逆アセンブル)
r2 <ファイル名> #ファイルの読み込み
aa #全てを解析するコマンド(Analyze Allの略)
"#"以降はコメントアウト
§ よく使うコマンド(今回は2個)
静的解析(逆アセンブル)
afl #関数をリスト表示するコマンド(Analyze Functions Listの略)
pdf @ <関数名> #特定の関数の逆アセンブルの結果を表示するコマンド(Print Disassembly Functionの略)
"@"はシンボル名を指定する記号
例:pdf @ my_function = pdf 0x0x00401080
つまりC言語の&<変数名> = <変数のアドレス>みたいな感じ。
この記事では@ <関数名>の方で書くことにする。
動的解析(デバッグ)
§ 最初の手順
動的解析(デバッグ)
r2 -d <ファイル名> #デバッグモードでファイルの読み込み
§ よく使うコマンド(今回は5個)
動的解析(デバッグ)
dc # プログラムを次のbreakpointまで(無ければ最後まで)実行する(Debug Continueの略)
db <関数名または関数のアドレス> # breakpoint(強制終了する位置)を設定する(Debug Breakpointの略)
ds # 1命令だけ実行する(Debug Stepの略)
dr <レジスタ名> # レジスタの値の確認(Debug Registerの略)
dr <レジスタ名> = <変更したい値> # レジスタの値の変更(Debug Registerの略)
s @ <関数名> #指定したアドレスに移動するコマンド(seekの略)
実験してみた
始めにこんな簡単なプログラムを書いてみた。
#include <stdio.h>
int main(void){
puts("flag{This_is_answer}");
return 0;
}
これからこのファイルをコンパイルして得られるバイナリファイルfind_flagを解析する。
また、ファイルのコードは上に提示したが、設定としてはこのファイルの中身を知らずにバイナリファイルのみが既知の物とする。
まず始めにこのバイナリファイルに対してr2 と aaを行う。
~$ r2 find_flag
[0x00001060]> aa
次にどんな関数があるか見てみよう。 afl(Analyze Functions List) を使う。
[0x00001060]> afl
§ aflの見方
0x00001050 1 11 sym.imp.puts
0x00001060 1 37 entry0
0x00001090 4 34 sym.deregister_tm_clones
0x000010c0 4 51 sym.register_tm_clones
0x00001100 5 54 entry.fini0
0x00001040 1 11 fcn.00001040
0x00001140 1 9 entry.init0
0x00001168 1 13 sym._fini
0x00001149 1 30 main
0x00001000 3 27 sym._init
いろいろ関数が出てくるが実際のコードではmain関数しかなかったことを思い出して欲しい。
entry0関数 はプログラム実行の準備をしてくれる関数で、書かなくても自動で組み込まれていそうだ。そんな感じで書いてないのに心強い仲間たちが自動登録されていそうだ。もちろん、本来のプログラムに書いた main関数 もいらっしゃることを確認しておいて欲しい。
ちなみに初期値リスポーンのカレントアドレス [0x00001060] ([0x00001060]> aflとか入力するときの現在地のアドレス)はentry0のアドレスで設定されている。
結局main関数以外の関数は書いてもないのに勝手に居候しているだけであまり気にしなくて良さそうだ。
今度はmain関数に着目してみる。
0x00001149 1 30 main
0x00001149はmain関数のアドレスとわかっても1や30と言う数字は何の数字なのか気になった人もいるのではないだろうか。結論1の位置にある数字は 解析回数 、30の位置にある数字はその関数の バイト数 である。
この例のmain関数は解析回数:1回、バイト数:30byteと言うことである。
解析回数はaaで解析するときに一回で理解できなかったときに何周かしましたと言うだけなので複雑さの指標になりそうではあるが、結局あまり気にしなくても良さそう...
§ キーワード検索めっちゃ便利やん in CTF
/ <キーワード> # キーワードを含む関数や文字列を検索
これ実は実話の話めっちゃ便利。
[0x00001060]> / flag
0x00002004 hit0_0 .flag{This_is_answer}.
今回のプログラムに対してflagで検索掛けたら一発で該当。
おまけ
ph <md5 または sha1 または sha256> # ファイルのhash値を所得する(Print Hashの略)
これを使えばファイルのhash値を取得でき、VirusTotal などのツールに投げればバイナリファイルのウイルススキャンに役立ちそうかも。
まとめ
静的解析(逆アセンブル)
・r2
・aa
・afl
・pdf
・/
動的解析(デバッグ)
・r2 -d
・dc
・db
・ds
・dr
・s
今回はバイナリ解析のお勉強をしました。(苦手分野干からびた...)
radare2のコマンドは大体ここら辺使えたらなんとか使えそうな気がするので必要最低限の勉強と紹介をしました。
NextActionとしては アセンブリ言語 を少しでも読めるようになることと、radare2の VisualMode を使えるようになることが次の目標です。
これからも頑張りましょう。メリークリスマス。