バイナリ解析(リバースエンジニアリング)に便利なツール、Radare2についてです。
Radare2(r2)
バイナリ解析(リバースエンジニアリング)の際に、逆アセンブルやデバッグなどを行えるフレームワークです。Radare2以外には、IDA、Ghidra、gdbなどがあります。略称はr2です。
インストール
UbuntuなどのLinuxを使っている方は次のコマンドでインストール出来ます。
または、githubからインストール出来ます。
$ sudo apt install radare2
or
$ git clone https://github.com/radareorg/radare2
$ radare2/sys/install.sh
https://github.com/radareorg/radare2
インストールが出来たらradare2を起動してみます。
$ radare2
or
$ r2
これで起動は完了です。バージョンを確認していきます。
$ r2 -v
radare2 5.6.7 27683 @ linux-x86-64 git.5.6.6
使い方
本記事では、略称のr2で進めていきます。
$ r2 <実行ファイル>
書き込みモードで行う場合
$ r2 -w <実行ファイル>
デバッグモードで行う場合
$ r2 -d <実行ファイル>
これでradare2に実行ファイルが読み込まれます。
書き込みやデバッグの指定がない場合は、読み取り専用モードで開きます。
「-h」オプションをつけることでヘルプを表示出来ます。
$ r2 -h
Usage: r2 [-ACdfLMnNqStuvwzX] [-P patch] [-p prj] [-a arch] [-b bits] [-i file]
[-s addr] [-B baddr] [-m maddr] [-c cmd] [-e k=v] file|pid|-|--|=
-- run radare2 without opening any file
- same as 'r2 malloc://512'
= read file from stdin (use -i and -c to run cmds)
-= perform !=! command to run all commands remotely
-0 print \x00 after init and every command
-2 close stderr file descriptor (silent warning messages)
-a [arch] set asm.arch
-A run 'aaa' command to analyze all referenced code
-b [bits] set asm.bits
-B [baddr] set base address for PIE binaries
-c 'cmd..' execute radare command
-C file is host:port (alias for -c+=http://%s/cmd/)
-d debug the executable 'file' or running process 'pid'
-D [backend] enable debug mode (e cfg.debug=true)
-e k=v evaluate config var
-f block size = file size
-F [binplug] force to use that rbin plugin
-h, -hh show help message, -hh for long
-H ([var]) display variable
-i [file] run script file
-I [file] run script file before the file is opened
-j use json for -v, -L and maybe others
-k [OS/kern] set asm.os (linux, macos, w32, netbsd, ...)
-l [lib] load plugin file
-L list supported IO plugins
-m [addr] map file at given address (loadaddr)
-M do not demangle symbol names
-n, -nn do not load RBin info (-nn only load bin structures)
-N do not load user settings and scripts
-NN do not load any script or plugin
-q quiet mode (no prompt) and quit after -i
-qq quit after running all -c and -i
-Q quiet mode (no prompt) and quit faster (quickLeak=true)
-p [prj] use project, list if no arg, load if no file
-P [file] apply rapatch file and quit
-r [rarun2] specify rarun2 profile to load (same as -e dbg.profile=X)
-R [rr2rule] specify custom rarun2 directive
-s [addr] initial seek
-S start r2 in sandbox mode
-T do not compute file hashes
-u set bin.filter=false to get raw sym/sec/cls names
-v, -V show radare2 version (-V show lib versions)
-w open file in write mode
-x open without exec-flag (asm.emu will not work), See io.exec
-X same as -e bin.usextr=false (useful for dyldcache)
-z, -zz do not load strings or load them even in ra
それでは、実行ファイルを読み込んでみます。本記事ではaccessという実行ファイルを使用します。
$ r2 access
[0x00001070]>
これでr2に実行ファイルが読み込まれました。
「?」コマンドでヘルプを表示することが出来ます。
[0x00001070]> ?
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
| ,[?] [/jhr] create a dummy table import from file and query it to filter/sort
| _[?] Print last output
| =[?] [cmd] send/listen for remote commands (rap://, raps://, udp://, 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
| C[?] code metadata (comments, format, hints, ..)
| d[?] debugger commands
| e[?] [a[=b]] list/get/set config evaluable vars
| f[?] [name][sz][at] add flag at current address
| g[?] [arg] generate shellcodes with r_egg
| i[?] [file] get info about opened file from r_bin
| k[?] [sdb-query] run sdb-query. see k? for help, 'k *', 'k **' ...
| l[?] [filepattern] list files and directories
| L[?] [-] [plugin] list, unload load r2 plugins
| m[?] mountpoints commands
| o[?] [file] ([offset]) open file at optional address
| p[?] [len] print current block with format and length
| P[?] project management utilities
| q[?] [ret] quit program with a return value
| r[?] [len] resize file
| s[?] [addr] seek to address (also for '0x', '0x1' == 's 0x1')
| t[?] types, noreturn, signatures, C parser and more
| T[?] [-] [num|msg] Text log utility (used to chat, sync, log, ...)
| u[?] uname/undo seek/write
| v panels mode
| V visual mode (Vv = func/var anal, VV = graph mode, ...)
| w[?] [str] multiple write operations
| x[?] [len] alias for 'px' (print hexadecimal)
| y[?] [len] [[[@]addr Yank/paste bytes from/to memory
| z[?] zignatures management
| ?[??][expr] Help or evaluate math expression
| ?$? show available '$' variables and aliases
| ?@? misc help for '@' (seek), '~' (grep) (see ~?""?)
| ?>? output redirection
| ?|? help for '|' (pipe)
任意のアルファベット文字に「?」を入力することにより、任意のアルファベットから始ま
るコマンドの一覧を表示することが出来ます。
[0x00001070]> a?
Usage: a [abdefFghoprxstc] [...]
| a alias for aai - analysis information
| a* same as afl*;ah*;ax*
| aa[?] analyze all (fcns + bbs) (aa0 to avoid sub renaming)
| a8 [hexpairs] analyze bytes
| ab[?] analyze basic block
| ac[?] manage classes
| aC[?] analyze function call
| ad[?] analyze data trampoline (wip)
| ad [from] [to] analyze data pointers to (from-to)
| ae[?] [expr] analyze opcode eval expression (see ao)
| af[?] analyze functions
| aF same as above, but using anal.depth=1
| ag[?] [options] draw graphs in various formats
| ah[?] analysis hints (force opcode size, ...)
| ai [addr] address information (show perms, stack, heap, ...)
| aj same as a* but in json (aflj)
| aL list all asm/anal plugins (e asm.arch=?)
| an[?] [name] show/rename/create whatever var/flag/function is used in current instruction
| ao[?] [len] analyze Opcodes (or emulate it)
| aO[?] [len] Analyze N instructions in M bytes
| ap find prelude for current offset
| ar[?] like 'dr' but for the esil vm. (registers)
| as[?] [num] analyze syscall using dbg.reg
| av[?] [.] show vtables
| avg[?] [.] manage global variables
| ax[?] manage refs/xrefs (see also afx?)
実行ファイルの情報が知りたい場合は、「iI」と入力します。
[0x00001070]> iI
arch x86
baddr 0x0
binsz 14239
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Debian 11.2.0-18) 11.2.0
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
nx true
os linux
pic true
relocs true
relro partial
rpath NONE
sanitize false
static false
stripped false
subsys linux
va true
可読部分を表示するには「iz」コマンドを使用します。
stringsコマンドに相当します。
[0x00001070]> iz
[Strings]
nth paddr vaddr len size section type string
――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00002008 0x00002008 23 46 .rodata utf8 Usage: <プログラム名> <パスワード> blocks=Basic Latin,Katakana,CJK Unified Ideographs
1 0x00002036 0x00002036 6 19 .rodata utf8 アクセス許可 blocks=Katakana,CJK Unified Ideographs
2 0x00002049 0x00002049 6 19 .rodata utf8 アクセス拒否 blocks=Katakana,CJK Unified Ideographs
コード分析
分析することで基本ブロック、関数ツリーを識別し、オペコードレベルの情報を抽出するための内部データ構造が表示されます。Radare2では手動解析する必要があります。「aa」コマンドで実行することが出来ます。
より多くの情報を抽出したい場合は「aaa」と増やすことが出来ます。「aa?」と入力することで、詳細を確認することが出来ます。
[0x00001070]> aa?
Usage: aa[0*?] # see also 'af' and 'afna'
| aa alias for 'af@@ sym.*;af@entry0;afva'
| aaa[?] autoname functions after aa (see afna)
| aab abb across bin.sections.rx
| aac [len] analyze function calls (af @@ `pi len~call[1]`)
| aac* [len] flag function calls without performing a complete analysis
| aad [len] analyze data references to code
| aae [len] ([addr]) analyze references with ESIL (optionally to address)
| aaef analyze references with ESIL in all functions
| aaf[?][efrt] analyze all functions relationships with flags, type matching and consecutive
| aaF [sym*] set anal.in=block for all the spaces between flags matching glob
| aaFa [sym*] same as aaF but uses af/a2f instead of af+/afb+ (slower but more accurate)
| aai[j] show info of all analysis parameters
| aan[?][gr] autoname functions (aang = golang, aanr = noreturn propagation)
| aao analyze all objc references
| aap find and analyze function preludes
| aar[?] [len] analyze len bytes of instructions for references
| aas [len] analyze symbols (af @@= `isq~[0]`)
| aaS analyze all flags starting with sym. (af @@ sym.*)
| aat [fcn] Analyze all/given function to convert immediate to linked structure offsets (see tl?)
| aaT [len] analyze code after trap-sleds
| aau [len] list mem areas (larger than len bytes) not covered by functions
| aav [sat] find values referencing a specific section or map
| aaw analyze all meta words (Cd) and add r. named flags for referenced pointers
今回は「aa」だけで十分ですので、「aa」と入力します。
[0x00001070]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze all functions arguments/locals
「afl」コマンドで分析で見つかった関数を一覧表示することが出来ます。
[0x00001070]> afl
0x00001070 1 42 entry0
0x000010a0 4 41 -> 34 sym.deregister_tm_clones
0x000010d0 4 57 -> 51 sym.register_tm_clones
0x00001110 5 57 -> 54 sym.__do_global_dtors_aux
0x00001060 1 6 sym.imp.__cxa_finalize
0x00001150 1 9 entry.init0
0x00001000 3 23 sym._init
0x00001250 1 1 sym.__libc_csu_fini
0x00001254 1 9 sym._fini
0x000011f0 4 93 sym.__libc_csu_init
0x00001159 6 145 main
0x00001040 1 6 sym.imp.puts
0x00001050 1 6 sym.imp.exit
0x00001030 1 6 sym.imp.strncmp
main関数の逆アセンブルされた結果を表示したいので「pdf @ 関数名」と入力します。
[0x00001070]> pdf @ main
; DATA XREF from entry0 @ 0x108d
┌ 145: int main (int argc, char **argv);
│ ; var int64_t var_30h @ rbp-0x30
│ ; var int64_t var_24h @ rbp-0x24
│ ; var int64_t var_12h @ rbp-0x12
│ ; var int64_t var_ah @ rbp-0xa
│ ; var int64_t var_8h @ rbp-0x8
│ ; arg int argc @ rdi
│ ; arg char **argv @ rsi
│ 0x00001159 55 push rbp
│ 0x0000115a 4889e5 mov rbp, rsp
│ 0x0000115d 4883ec30 sub rsp, 0x30
│ 0x00001161 897ddc mov dword [var_24h], edi ; argc
│ 0x00001164 488975d0 mov qword [var_30h], rsi ; argv
│ 0x00001168 837ddc01 cmp dword [var_24h], 1
│ ┌─< 0x0000116c 7f19 jg 0x1187
│ │ 0x0000116e 488d05930e00. lea rax, str.Usage:______ ; 0x2008 ; "Usage: <\u30d7\u30ed\u30b0\u30e9\u30e0\u540d> <\u30d1\u30b9\u30ef\u30fc\u30c9>"
│ │ 0x00001175 4889c7 mov rdi, rax
│ │ 0x00001178 e8c3feffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x0000117d bf00000000 mov edi, 0
│ │ 0x00001182 e8c9feffff call sym.imp.exit ; void exit(int status)
│ └─> 0x00001187 488b45d0 mov rax, qword [var_30h]
│ 0x0000118b 488b4008 mov rax, qword [rax + 8]
│ 0x0000118f 488945f8 mov qword [var_8h], rax
│ 0x00001193 48b841523425. movabs rax, 0x526d303225345241 ; 'AR4%20mR'
│ 0x0000119d 488945ee mov qword [var_12h], rax
│ 0x000011a1 66c745f66a00 mov word [var_ah], 0x6a ; 'j'
│ 0x000011a7 488d4dee lea rcx, [var_12h]
│ 0x000011ab 488b45f8 mov rax, qword [var_8h]
│ 0x000011af ba0a000000 mov edx, 0xa
│ 0x000011b4 4889ce mov rsi, rcx
│ 0x000011b7 4889c7 mov rdi, rax
│ 0x000011ba e871feffff call sym.imp.strncmp ; int strncmp(const char *s1, const char *s2, size_t n)
│ 0x000011bf 85c0 test eax, eax
│ ┌─< 0x000011c1 7511 jne 0x11d4
│ │ 0x000011c3 488d056c0e00. lea rax, [0x00002036] ; "\u30a2\u30af\u30bb\u30b9\u8a31\u53ef"
│ │ 0x000011ca 4889c7 mov rdi, rax
│ │ 0x000011cd e86efeffff call sym.imp.puts ; int puts(const char *s)
│ ┌──< 0x000011d2 eb0f jmp 0x11e3
│ │└─> 0x000011d4 488d056e0e00. lea rax, str. ; 0x2049
│ │ 0x000011db 4889c7 mov rdi, rax
│ │ 0x000011de e85dfeffff call sym.imp.puts ; int puts(const char *s)
│ │ ; CODE XREF from main @ 0x11d2
│ └──> 0x000011e3 b800000000 mov eax, 0
│ 0x000011e8 c9 leave
└ 0x000011e9 c3 ret
また、「s <address>」で指定したアドレスに飛ぶことが出来ます。
main関数に飛んでみます。
[0x00001070]> afl
0x00001070 1 42 entry0
0x000010a0 4 41 -> 34 sym.deregister_tm_clones
0x000010d0 4 57 -> 51 sym.register_tm_clones
0x00001110 5 57 -> 54 sym.__do_global_dtors_aux
0x00001060 1 6 sym.imp.__cxa_finalize
0x00001150 1 9 entry.init0
0x00001000 3 23 sym._init
0x00001250 1 1 sym.__libc_csu_fini
0x00001254 1 9 sym._fini
0x000011f0 4 93 sym.__libc_csu_init
0x00001159 6 145 main
0x00001040 1 6 sym.imp.puts
0x00001050 1 6 sym.imp.exit
0x00001030 1 6 sym.imp.strncmp
[0x00001070]> s main
[0x00001159]>
「ii」コマンドでインポートされたライブラリを表示できます。
[0x00001159]> ii
[Imports]
nth vaddr bind type lib name
―――――――――――――――――――――――――――――――――――――
1 0x00001030 GLOBAL FUNC strncmp
2 0x00000000 WEAK NOTYPE _ITM_deregisterTMCloneTable
3 0x00001040 GLOBAL FUNC puts
4 0x00000000 GLOBAL FUNC __libc_start_main
5 0x00000000 WEAK NOTYPE __gmon_start__
6 0x00001050 GLOBAL FUNC exit
7 0x00000000 WEAK NOTYPE _ITM_registerTMCloneTable
8 0x00001060 WEAK FUNC __cxa_finalize
「px」コマンドで16進数で表示することが出来ます。
[0x00001159]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00001159 5548 89e5 4883 ec30 897d dc48 8975 d083 UH..H..0.}.H.u..
0x00001169 7ddc 017f 1948 8d05 930e 0000 4889 c7e8 }....H......H...
0x00001179 c3fe ffff bf00 0000 00e8 c9fe ffff 488b ..............H.
0x00001189 45d0 488b 4008 4889 45f8 48b8 4152 3425 E.H.@.H.E.H.AR4%
0x00001199 3230 6d52 4889 45ee 66c7 45f6 6a00 488d 20mRH.E.f.E.j.H.
0x000011a9 4dee 488b 45f8 ba0a 0000 0048 89ce 4889 M.H.E......H..H.
0x000011b9 c7e8 71fe ffff 85c0 7511 488d 056c 0e00 ..q.....u.H..l..
0x000011c9 0048 89c7 e86e feff ffeb 0f48 8d05 6e0e .H...n.....H..n.
0x000011d9 0000 4889 c7e8 5dfe ffff b800 0000 00c9 ..H...].........
0x000011e9 c366 0f1f 4400 0041 574c 8d3d ef2b 0000 .f..D..AWL.=.+..
0x000011f9 4156 4989 d641 5549 89f5 4154 4189 fc55 AVI..AUI..ATA..U
0x00001209 488d 2de0 2b00 0053 4c29 fd48 83ec 08e8 H.-.+..SL).H....
0x00001219 e3fd ffff 48c1 fd03 741b 31db 0f1f 004c ....H...t.1....L
0x00001229 89f2 4c89 ee44 89e7 41ff 14df 4883 c301 ..L..D..A...H...
0x00001239 4839 dd75 ea48 83c4 085b 5d41 5c41 5d41 H9.u.H...[]A\A]A
0x00001249 5e41 5fc3 0f1f 00c3 0000 0048 83ec 0848 ^A_........H...H
「pdc」でデコンパイルされた結果を表示出来ます。
[0x00001159]> pdc
int main (int esi, int edx) {
loc_0x1159:
// DATA XREF from entry0 @ 0x108d
push (rbp)
rbp = rsp
rsp -= 0x30
dword [var_24h] = edi // argc
qword [var_30h] = rsi // argv
var = dword [var_24h] - 1
if (var > 0) goto loc_0x1187 // unlikely
loc_0x1187:
rax = qword [var_30h]
rax = qword [rax + 8]
qword [var_8h] = rax
rax = 0x526d303225345241 // 'AR4%20mR'
qword [var_12h] = rax
word [var_ah] = 0x6a // 'j'
rcx = var_12h // "AR4%20mRj"
rax = qword [var_8h]
edx = 0xa
rsi = rcx // "AR4%20mRj"
rdi = rax
sym.imp.strncmp ()
// int strncmp(-1, "AR4%20mRj", 0x00000000)
var = eax & eax
if (var) goto loc_0x11d4 // unlikely
loc_0x11d4:
rax = rip + str. // 0x2049 // "\u30a2\u30af\u30bb\u30b9\u62d2\u5426"
rdi = rax // "\u30a2\u30af\u30bb\u30b9\u62d2\u5426" str.
sym.imp.puts ()
// int puts("\xe3\x82\xa2\xe3\x82\xaf\xe3\x82\xbb\xe3\x82\xb9\xe6\x8b\x92")
// do {
loc_0x11e3:
// CODE XREF from main @ 0x11d2
eax = 0
leav // rsp
re
// } while (?);
// } while (?);
// } while (?);
}
return eax;
}
「V」コマンドでビジュアルモードにすることができ、「p」で表示を切り替えることが出来ます。
「VV」コマンドでIDAのようなグラフビューを表示することが出来ます。
この状態で「Shift + R」を押すことで色合いの変更が出来ます。
またグラフをずらしたい場合は、Vimの操作と同じように「hjkl」で出来ます。
ビジュアルモードから抜けたい場合は、「q」コマンドで行えます。
他にもデバッグモードでの実行などをすることが出来ますが、今回はここまでにしたいと思います。