BioErrorLog Tech Blog

試行錯誤の記録

radare2の使い方 | リバースエンジニアリング入門#5

radare2のインストール方法から簡単な使い方までを記録しました。
直感では使いにくい面も感じましたが、使い慣れれば強力な武器となってくれるでしょう。

はじめに

おはよう。@bioerrorlogです。

これまでは、gdbやobjdumpコマンドなどを用いてバイナリコードのリバースエンジニアリングを試みてきました。

www.bioerrorlog.work

今回は、radare2という便利なツールを使ってバイナリコードを解析していきます。 radare2を使えば、バイナリコードの逆アセンブルからプログラム分岐の可視化、デバッグを一手に行うことができます。

さっそくやっていきます。

作業環境

Ubuntu18.04.1 LTS を
Windows10の上に、VMwareによって構築した仮想環境で起動しています。
www.bioerrorlog.work

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

たくさんの面白そうな関数名が表示されました。 ここから、putsprintfstrcmp関数などが使われていることがわかるわけです。

逆アセンブル | 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)。

f:id:BioErrorLog:20190211135605p:plain
Fig. 1 Visualモードのスクリーンショット

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)。

f:id:BioErrorLog:20190211162736p:plain
Fig. 2 ";--rip:"が1ステップずつ進んでいく

細かい機能はもちろんたくさんありますが、とりあえず以上のようにして、デバッグを行うことができます。


おわりに

今回は、radare2の使い方を記録しました。

これまでgdbをつかって自力でやっていたことを自動化できるため、大変強力なツールだと感じました。 また、随所にみられる製作者の遊び心には趣深いものがあります。 ただ、挙動や機能に慣れていないせいか、直感では使いづらい面もありました。 ほかにも類似のツールはたくさんあるのでしょうが、それでもとりあえずはこのradare2で遊んでいきたいと思っています。

[関連記事]

www.bioerrorlog.work

www.bioerrorlog.work

www.bioerrorlog.work

参考

Simple Tools and Techniques for Reversing a binary - bin 0x06 - YouTube

メイク - Wikipedia


  1. cloneとは、製作者の公開するソフトウェア(正しくはリポジトリ)を自分のPCに複製することを言います。

  2. gitはバージョン管理システムです。GitHubはこのgitを利用して、ソフトウェア開発者が自分のソフトウェアを公開できる有名なプラットフォームです。

  3. こんなにもエラーが多かった理由は、新しく仮想環境で立ち上げた、生まれたてのUbuntuで再度インストールを行ったからです。ふつうはmakeやgccは既にインストールしたことがあるでしょうから、こんなに躓くことはないでしょう。

  4. 選択ノードは青枠で表示されます。

  5. 実に多様で美しい色彩が実装されています。製作者の遊び心でしょうか。

  6. “rip"は処理中のアドレスを指し示すレジスタの名前です。