カテゴリー: Technique

Radare2のインストールと使い方

Published / by draco

バイナリ解析(リバースエンジニアリング)に便利なツール、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」コマンドで行えます。

他にもデバッグモードでの実行などをすることが出来ますが、今回はここまでにしたいと思います。