radare2のインストール方法から簡単な使い方までを記録しました。
直感では使いにくい面も感じましたが、使い慣れれば強力な武器となってくれるでしょう。
はじめに
これまでは、gdbやobjdumpコマンドなどを用いてバイナリコードのリバースエンジニアリングを試みてきました。
今回は、radare2という便利なツールを使ってバイナリコードを解析していきます。 radare2を使えば、バイナリコードの逆アセンブルからプログラム分岐の可視化、デバッグを一手に行うことができます。
さっそくやっていきます。
作業環境
Ubuntu18.04.1 LTS を
Windows10の上に、VMwareによって構築した仮想環境で起動しています。
WindowsにLinux仮想環境を構築する - BioErrorLog Tech Blog
radare2の使い方
radare2をインストールする
radare2をインストールするには、まず製作者のGitHubからradare2を"clone"1する必要があります。 そのために、まずはgit2をインストールします。
$ sudo apt install git
これでgitがインストールされました。
それではこのgitを使って、製作者のGitHubからradare2をcloneします。
$ git clone https://github.com/radare/radare2.git
cloneが終わったら、radare2の中に入ってインストールファイルを実行します。
$ cd radare2 $ ./sys/install.sh
すると、次のようなエラーを吐かれてしまいました。
You need GNU Make to build me
ビルドするには"GNU Make"が必要ですよと怒られてしまったわけです。
"GNU Make" とは、wikipediaによると、
makeは、プログラムのビルド作業を自動化するツール。コンパイル、リンク、インストール等のルールを記述したテキストファイル (makefile) に従って、これらの作業を自動的に行う。
いまいちよくわかりませんが、ビルドに必要なツールということで今は納得しておきます。 それではこのmakeをインストールします。
$ sudo apt install make
これでmakeがインストールされたので、radare2をインストールできるはずです。
$ ./sys/install.sh
すると、順調にインストールが進んだかに見えましたが、今度は次のエラーが表示されました。
~ ERROR: gcc cannot create executables
コンパイラであるgccが実行ファイルを作成できませんでした、とのことです。 それもそのはず、gccをまだインストールしていませんでした。 gccをインストールします。
$ sudo apt install gcc
これでgccもインストールされたので、今度こそradare2がインストールできるはずです。
$ ./sys/install.sh
長い処理の後、無事インストールが完了しました3。
radare2を起動する
radare2を使って解析を始めるには、単純にバイナリコードをr2
に渡してやれば良いです。
今回も、以前作成したパスワード認証プログラムのバイナリコード"your_pass"を解析します。
$ r2 your_pass -- Stop debugging me! [0x000005d0]>
これでradare2による解析が開始しました。
>
のあとにコマンドを入力することで操作することができます。
ちなみに、radare2を開始したときに表示されたコメント-- Stop debugging me!
は、毎回変わります。
例えば、
-- Did you know that r2 is 10 years old?
-- Heisenbug: A bug that disappears or alters its behavior when one attempts to probe or isolate it
-- Please register your copy of r2 today! Only £29.90!
などなど。製作者のユーモアが感じられて私は好きです。
radare2の使い方を調べる | ? - ヘルプ
新しいツールを使い始めるときは、何よりもまず"使い方の調べ方"を知ることが大切です。
radare2では、?
でヘルプを見ることができます。
> ? Usage: [.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ... Append '?' to any char command to get detailed help Prefix with number to repeat command N times (f.ex: 3x) | %var=value alias for 'env' command | *[?] off[=[0x]value] pointer read/write data/values (see ?v, wx, wv) | (macro arg0 arg1) manage scripting macros | .[?] [-|(m)|f|!sh|cmd] Define macro or load r2, cparse or rlang file | _[?] Print last output | =[?] [cmd] send/listen for remote commands (rap://, http://, <fd>) | <[...] push escaped string into the RCons.readChar buffer | /[?] search for bytes, regexps, patterns, .. | ![?] [cmd] run given command as in system(3) | #[?] !lang [..] Hashbang to run an rlang script | a[?] analysis commands | b[?] display or change the block size | c[?] [arg] compare block with given data ~ #以下略
上の方にAppend '?' to any char command to get detailed help
とあるように、更に詳細なヘルプを見るにはコマンドに再度?
を付ければいいようです。
例えば、コマンドa
の次に何のコマンドを追加すればいいかを調べたければ、a?
とすればa
に付け加えるべきコマンドが表示されます。
このようにしてヘルプを活用すれば、コマンドの意味を調べることができるわけです。
例えばコマンドaaa
の意味をヘルプから調べるなら、
>? ~ | a[?] analysis commands ~ >a? ~ | aa[?] analyze all (fcns + bbs) (aa0 to avoid sub renaming) ~ >aa? ~ | aaa[?] autoname functions after aa (see afna) ~
つまり、aaa
は"autoname function"を"すべて" "解析"するコマンドである、ということになります。
ここまでは下準備です。 次はさっそく解析を始めていきます。
使用される関数を表示する | afl
afl
コマンドを打つことで、解析しているバイナリコードで使用される関数のリストを表示させることができます。
その前にまずradare2を開始したら、私はaaa
で関数を解析させるようにしています。
>aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [x] Type matching analysis for all functions (afta) [x] Use -AA or aaaa to perform additional experimental analysis.
そしたら、使用されている関数をafl
で表示させます。
> afl 0x00000000 6 292 -> 318 sym.imp.__libc_start_main 0x00000560 3 23 sym._init 0x00000590 1 6 sym.imp.puts 0x000005a0 1 6 sym.imp.printf 0x000005b0 1 6 sym.imp.strcmp 0x000005c0 1 6 sub.__cxa_finalize_5c0 0x000005d0 1 43 entry0 0x00000600 4 50 -> 40 sym.deregister_tm_clones 0x00000640 4 66 -> 57 sym.register_tm_clones 0x00000690 5 58 -> 51 sym.__do_global_dtors_aux 0x000006d0 1 10 entry.init0 0x000006da 6 129 main 0x00000760 3 101 -> 92 sym.__libc_csu_init 0x000007d0 1 2 sym.__libc_csu_fini 0x000007d4 1 9 sym._fini
たくさんの面白そうな関数名が表示されました。
ここから、puts
やprintf
、strcmp
関数などが使われていることがわかるわけです。
逆アセンブル | pdf
つぎに、main関数の逆アセンブルを行います。
まずは、s
コマンドでmain関数に移動します。
[0x000005d0]> s sym.main [0x000006da]>
>
の前の文字列[0x000005d0]
は現在の位置を示しており、main関数の開始地点[0x000006da]
に移動したことがわかります。
ここでpdf
と打つことによって、逆アセンブルを行うことができます。
[0x000006da]> pdf / (fcn) main 129 | main (int argc, char **argv, char **envp); | ; var char **s1 @ rbp-0x10 | ; var unsigned int local_4h @ rbp-0x4 | ; arg unsigned int argc @ rdi | ; arg char **argv @ rsi | ; DATA XREF from entry0 (0x5ed) | 0x000006da 55 push rbp | 0x000006db 4889e5 mov rbp, rsp | 0x000006de 4883ec10 sub rsp, 0x10 | 0x000006e2 897dfc mov dword [local_4h], edi ; argc | 0x000006e5 488975f0 mov qword [s1], rsi ; argv | 0x000006e9 837dfc02 cmp dword [local_4h], 2 | ,=< 0x000006ed 7559 jne 0x748 | | 0x000006ef 488b45f0 mov rax, qword [s1] | | 0x000006f3 4883c008 add rax, 8 | | 0x000006f7 488b00 mov rax, qword [rax] | | 0x000006fa 4889c6 mov rsi, rax | | 0x000006fd 488d3de40000. lea rdi, str.Checking_Your_Pass:__s ; 0x7e8 ; "Checking Your Pass: %s\n" ; const char *format | | 0x00000704 b800000000 mov eax, 0 | | 0x00000709 e892feffff call sym.imp.printf ; int printf(const char *format) | | 0x0000070e 488b45f0 mov rax, qword [s1] | | 0x00000712 4883c008 add rax, 8 | | 0x00000716 488b00 mov rax, qword [rax] | | 0x00000719 488d35e00000. lea rsi, str.Antoine_Marie_Jean_Baptiste_Roger_comte_de_Saint_Exupery ; 0x800 ; "Antoine-Marie-Jean-Baptiste-Roger_comte-de-Saint-Exupery" ; const char *s2 | | 0x00000720 4889c7 mov rdi, rax ; const char *s1 | | 0x00000723 e888feffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) | | 0x00000728 85c0 test eax, eax | ,==< 0x0000072a 750e jne 0x73a | || 0x0000072c 488d3d060100. lea rdi, str.Hello__Master. ; 0x839 ; "Hello, Master." ; const char *s | || 0x00000733 e858feffff call sym.imp.puts ; int puts(const char *s) | ,===< 0x00000738 eb1a jmp 0x754 | ||| ; CODE XREF from main (0x72a) | |`--> 0x0000073a 488d3d070100. lea rdi, str.Access_Denied. ; 0x848 ; "Access Denied." ; const char *s | | | 0x00000741 e84afeffff call sym.imp.puts ; int puts(const char *s) | |,==< 0x00000746 eb0c jmp 0x754 | ||| ; CODE XREF from main (0x6ed) | ||`-> 0x00000748 488d3df90000. lea rdi, str.Access_Denied. ; 0x848 ; "Access Denied." ; const char *s | || 0x0000074f e83cfeffff call sym.imp.puts ; int puts(const char *s) | || ; CODE XREFS from main (0x738, 0x746) | ``--> 0x00000754 b800000000 mov eax, 0 | 0x00000759 c9 leave \ 0x0000075a c3 ret
プログラムの分岐を示す矢印や、ハードコードされた文字列( "Hello, Master." など)が表示されており、以前gdbで逆アセンブルしたときより圧倒的に多くの情報が得られました。
Visualモードでプログラムの流れを表示する | VV
Visualモードを使うことによって、プログラムの流れを表示させることができます。
Visualモードに移るにはVV
と打ちます。
[0x000006da]> VV
すると、次のようなプログラムの流れが表示されます(Fig. 1)。
Visualモードで実行できることは、"?"キーを押すことで調べることができます。 例えば"p"を押せば表示モードの変更、矢印キーで画面のスクロール、"tab"キーで選択ノード4の変更、などなど。 特に、色合いを変更できる"R"は面白い機能でした5。
デバッグモードで解析する | -d / ood
radare2では、gdbのようにデバッグモードで解析を行うことができます。
改めてデバッグモードでradara2を起動するには、一旦"q"あるいは"D"(Ctrl + d)を押してradare2を終了します。
そしたら、デバッグモードでradare2を起動するため、-d
を渡してバイナリコード”your_pass”の解析を開始します。
$ r2 -d your_pass
あるいは、radare2上でコマンドood
を打てば、デバッグモードで開き直すことができます。
>ood
次に、gdbでデバッグするときと同じように、main関数の開始地点にブレークポイントを設置します。
main関数のアドレスは、上述したのと同じ手順で確認します。
しかしここで、aaa
で解析すると何故かうまく行きませんでした。
代わりにaa
で解析するとうまく行きました。
ここららへんの細かいところは正直まだ理解できていないので、今後扱い慣れていきたいものです。
>aa ~ >s sym.main ~ >pdf / (fcn) main 129 | main (int argc, char **argv, char **envp); | ; var int local_10h @ rbp-0x10 | ; var int local_4h @ rbp-0x4 | ; arg int argc @ bp | ; arg char **argv @ r15b | ; DATA XREF from entry0 (0x55e86a7d05ed) | 0x55e86a7d06da 55 push rbp | 0x55e86a7d06db 4889e5 mov rbp, rsp | 0x55e86a7d06de 4883ec10 sub rsp, 0x10 ~ #以下略
main関数の先頭のアドレスが0x55e86a7d06da
であることが分かりました。
それではdb
コマンドでブレークポイントを設置します。
> db 0x55e86a7d06da
では、さっそくVV
でVisualモードに移行し、デバッグを開始します。
Visualモードでは、vimのように” : ”を押すことでコマンドを受け付けるようになります。 " : "を押したあと、"dc"コマンドでプログラムを実行させます
:> dc hit breakpoint at: 0x55e86a7d06da
ブレークポイントに到達したら、"Shift + s"で1ステップずつ実行していきます。
処理している現在のアドレスは;-- rip:
6によって示されます(Fig. 2)。
細かい機能はもちろんたくさんありますが、とりあえず以上のようにして、デバッグを行うことができます。
おわりに
今回は、radare2の使い方を記録しました。
これまでgdbをつかって自力でやっていたことを自動化できるため、大変強力なツールだと感じました。 また、随所にみられる製作者の遊び心には趣深いものがあります。 ただ、挙動や機能に慣れていないせいか、直感では使いづらい面もありました。 ほかにも類似のツールはたくさんあるのでしょうが、それでもとりあえずはこのradare2で遊んでいきたいと思っています。
[関連記事]
参考
- cloneとは、製作者の公開するソフトウェア(正しくはリポジトリ)を自分のPCに複製することを言います。↩
- gitはバージョン管理システムです。GitHubはこのgitを利用して、ソフトウェア開発者が自分のソフトウェアを公開できる有名なプラットフォームです。↩
- こんなにもエラーが多かった理由は、新しく仮想環境で立ち上げた、生まれたてのUbuntuで再度インストールを行ったからです。ふつうはmakeやgccは既にインストールしたことがあるでしょうから、こんなに躓くことはないでしょう。↩
- 選択ノードは青枠で表示されます。↩
- 実に多様で美しい色彩が実装されています。製作者の遊び心でしょうか。↩
- "rip"は処理中のアドレスを指し示すレジスタの名前です。↩