In this section, we will simply get introduced to radare2 and explore the basics of its commands. It is not centered on any particular aspect of radare2, but will provide you with some vital background needed to more efficiently wrap your head around the overall structure and design of the framework.
There are plenty of resources scattered around the web from which you can learn more on how you can use radare2 for various tasks.
Most of these resources can be found in this blog post.
What is left out is radare.tv, which is quite a magical place which you should check out from time to time.
There's also the more practical and focused workshop by Maijin.
So, what is radare2?
You would not be wrong if you were to say that radare2 (or r2, in short) is a disassembler. But it is so much more.
r2 can aptly be named a reverse engineering framework, with some extra features on the side.
Here is a (non-exhaustive) list of what r2 can be:
- Hex editor
- Exploit development tool
- Binary diffing tool
- Shellcode compiler
- Launcher with specific contexts
- And more...
It can run on all major operating systems and understand any gizmo which has as little as an oscillator in it.
Taking the plunge
Radare2 can be started by typing radare2 or r2 in the console. You will be prompted with the following:
r2 Usage: r2 [-dDwntLqv] [-P patch] [-p prj] [-a arch] [-b bits] [-i file] [-s addr] [-B blocksize] [-c cmd] [-e k=v] file|pid|-|--|=
The important argument here is file. A process id (pid) can also be supplied when we want to attach to a process which is already running, but in most cases we will be using files.
Let's try it out on the humble and ubiquitous /bin/ls
r2 /bin/ls -- Change the UID of the debugged process with child.uid (requires root) [0x004048c5]>
Notice that the prompt changes. We are now exploring the memory map of /bin/ls. The value between parentheses is the current address position within the current file. This is the entry point of the binary (unless radare is configured differently).
Now you may want to navigate, disassemble, search, mark and do other operations. How?
[0x004048c5]> help |ERROR| Invalid command 'help' (0x68)
Radare2 is self-documented. For a full list of commands, a simple question mark (
?) will suffice and is much faster than typing
help all the time.
[0x004048c5]> ? 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 =valueAlias 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 | = [cmd] Run this command via rap:// | / Search for bytes, regexps, patterns, .. | ! [cmd] Run given command as in system(3) | # [algo] [len] Calculate hash checksum of current block | #!lang [..] Hashbang to run an rlang script | a Perform analysis of code | b Get or change block size | c [arg] Compare block with given data | C Code metadata management | d Debugger commands | e [a[=b]] List/get/set config evaluable vars | f [name][sz][at] Set flag at current address | g [arg] Go compile shellcodes with r_egg | i [file] Get info about opened file | k [sdb-query] Run sdb-query. see k? for help, 'k *', 'k **' ... | 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') | S Io section manipulation information | t Cparse types management | T [-] [num|msg] Text log utility | u uname/undo seek/write | V Enter visual mode (vcmds=visualvisual keystrokes) | 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 | [email protected]? Misc help for '@' (seek), '~' (grep) (see ~??) | ?:? List and manage core plugins [0x004048c5]>
This is understandably a daunting sight to behold, and it will not get any easier from this point on. Thankfully, most of these are self-contained and define a specific category of subcommands. For example, all analysis commands begin with
a, all commands related to the debugger begin with
d, all printing commands begin with
Looking through commands
While learning radare2, you will iteratively consult the built-in documentation to find commands which help you accomplish your specific needs, by appending a
? after each combination of interest. For example:
[0x004048c5]> a? |Usage: a[abdefFghoprxstc] [...] | ab [hexpairs] analyze bytes | aa analyze all (fcns + bbs) (aa0 to avoid sub renaming) | ac [cycles] analyze which op could be executed in [cycles] | ad analyze data trampoline (wip) | ad [from] [to] analyze data pointers to (from-to) | ae [expr] analyze opcode eval expression (see ao) | af[rnbcsl?+-*] analyze Functions | aF same as above, but using anal.depth=1 | ag[?acgdlf] output Graphviz code | ah[?lba-] analysis hints (force opcode size, ...) | ai [addr] address information (show perms, stack, heap, ...) | ao[e?] [len] analyze Opcodes (or emulate it) | an[an-] [...] manage no-return addresses/symbols/functions | ar like 'dr' but for the esil vm. (registers) | ap find prelude for current offset | ax[?ld-*] manage refs/xrefs (see also afx?) | as [num] analyze syscall using dbg.reg | at[trd+-%*?] [.] analyze execution traces Examples: f ts @ `S*~text:0`; f t @ section..text f ds @ `S*~data:0`; f d @ section..data .ad t t+ts @ d:ds [0x004048c5]> af? |Usage: af | af ([name]) ([addr]) analyze functions (start at addr or $$) | afr ([name]) ([addr]) analyze functions recursively | af+ addr size name [type] [diff] hand craft a function (requires afb+) | af- [addr] clean all function analysis data (or function at addr) | afa[?] [idx] [name] ([type]) add function argument | af[aAv?][arg] manipulate args, fastargs and variables in function | afb+ fa a sz [j] [f] ([t]( [d])) add bb to function @ fcnaddr | afb [addr] List basic blocks of given function | afB 16 set current function as thumb (change asm.bits) | [email protected][addr] calculate the Cyclomatic Complexity (starting at addr) | afC[a] type @[addr] set calling convention for function (afC?=list cc types) | aff re-adjust function boundaries to fit | afF[1|0|] fold/unfold/toggle | afg non-interactive ascii-art basic-block graph (See VV) | afi [addr|fcn.name] show function(s) information (verbose afl) | afl[*] [fcn name] list functions (addr, size, bbs, name) | afo [fcn.name] show address for the function named like this | afn name [addr] rename name for function at address (change flag too) | afna suggest automatic name for current offset | afs [addr] [fcnsign] get/set function signature at current address | afx[cCd-] src dst add/remove code/Call/data/string reference | afv[?] [idx] [type] [name] add local var on current function [0x004048c5]> afn? Usage: afn[sa] - analyze function names afna - construct a function name for the current offset afns - list all strings associated with the current function afn [name] - rename function
While this "forest" of a documentation does a decent job of what each and every command does, it does not tell you anything about how to use them. This is what the Internet (and subsequently, this book) is for. As mentioned before, radare2 can be used in plenty of scenarios. Not everyone is interested in shellcodes or DNA sequencing, so it makes a bit of sense not to include domain-specific examples within the documentation.
Usage: [.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ;
This is the command format for radare2. Although this looks cryptic, only the command itself is mandatory, and it will operate using some default values as we will see further on.
If you have some experience working with *nix shell, Vim, sed, awk, then learning radare2's commands will be slightly more intuitive.
In general (i.e. default behavior), each command has a point of reference, which is usually the current position in memory, indicated by the prompt. Any printing, writing or analysis commands will be performed at the current offset (address) in the file. For example:
[0x004048c5]> pd 1 ;-- entry0: 0x004048c5 31ed xor ebp, ebp
Disassembles one instruction at address
0x4048c5, which is the entry point for /bin/ls.
If we do not specify a number of instructions to disassemble, the default
block size will be used instead. This can be shown or changed with the command
[0x004048c5]> b 0x100 [0x004048c5]> pd ;-- entry0: 0x004048c5 31ed xor ebp, ebp 0x004048c7 4989d1 mov r9, rdx 0x004048ca 5e pop rsi 0x004048cb 4889e2 mov rdx, rsp 0x004048ce 4883e4f0 and rsp, 0xfffffffffffffff0 0x004048d2 50 push rax 0x004048d3 54 push rsp 0x004048d4 49c7c0602541. mov r8, 0x412560 0x004048db 48c7c1f02441. mov rcx, 0x4124f0 0x004048e2 48c7c7a02840. mov rdi, 0x4028a0 ; "AWAVAUATUS..H..H...." @ 0x4028a0 0x004048e9 e802dcffff call sym.imp.__libc_start_main 0x004048ee f4 hlt 0x004048ef 90 nop 0x004048f0 b85fc66100 mov eax, 0x61c65f ; ".interp" @ 0x61c65f 0x004048f5 55 push rbp 0x004048f6 482d58c66100 sub rax, 0x61c658 0x004048fc 4883f80e cmp rax, 0xe 0x00404900 4889e5 mov rbp, rsp ┌─< 0x00404903 761b jbe 0x404920 │ 0x00404905 b800000000 mov eax, 0 │ 0x0040490a 4885c0 test rax, rax ┌──< 0x0040490d 7411 je 0x404920 ││ 0x0040490f 5d pop rbp ││ 0x00404910 bf58c66100 mov edi, 0x61c658 ; "strtab" @ 0x61c658 ││ 0x00404915 ffe0 jmp rax ││ 0x00404917 660f1f840000. nop word [rax + rax] └└─> 0x00404920 5d pop rbp 0x00404921 c3 ret 0x00404922 66666666662e. nop word cs:[rax + rax] ┌─> 0x00404930 be58c66100 mov esi, 0x61c658 ; "strtab" @ 0x61c658 │ 0x00404935 55 push rbp │ 0x00404936 4881ee58c661. sub rsi, 0x61c658 │ 0x0040493d 48c1fe03 sar rsi, 3 │ 0x00404941 4889e5 mov rbp, rsp │ 0x00404944 4889f0 mov rax, rsi │ 0x00404947 48c1e83f shr rax, 0x3f │ 0x0040494b 4801c6 add rsi, rax │ 0x0040494e 48d1fe sar rsi, 1 ┌──< 0x00404951 7415 je 0x404968 ││ 0x00404953 b800000000 mov eax, 0 ││ 0x00404958 4885c0 test rax, rax ┌───< 0x0040495b 740b je 0x404968 │││ 0x0040495d 5d pop rbp │││ 0x0040495e bf58c66100 mov edi, 0x61c658 ; "strtab" @ 0x61c658 │││ 0x00404963 ffe0 jmp rax │││ 0x00404965 0f1f00 nop dword [rax] └└──> 0x00404968 5d pop rbp │ 0x00404969 c3 ret │ 0x0040496a 660f1f440000 nop word [rax + rax] │ 0x00404970 803d417d2100. cmp byte [rip + 0x217d41], 0 ┌──< 0x00404977 7511 jne 0x40498a ││ 0x00404979 55 push rbp ││ 0x0040497a 4889e5 mov rbp, rsp ││ 0x0040497d e86effffff call 0x4048f0 ││ 0x00404982 5d pop rbp ││ 0x00404983 c6052e7d2100. mov byte [rip + 0x217d2e], 1 ; [0x61c6b8:1]=105 └──> 0x0040498a f3c3 ret │ 0x0040498c 0f1f4000 nop dword [rax] │ 0x00404990 bf00be6100 mov edi, section..jcr ; section..jcr │ 0x00404995 48833f00 cmp qword [rdi], 0 ┌──< 0x00404999 7505 jne 0x4049a0 │└─< 0x0040499b eb93 jmp 0x404930 │ 0x0040499d 0f1f00 nop dword [rax] └──> 0x004049a0 b800000000 mov eax, 0
@addr - Relative seek
A command can be issued relative to an offset via the use of
@, like so:
[0x004048c5]> pd 10 @ main ;-- main: ;-- section_end..plt: ;-- section..text: 0x004028a0 4157 push r15 ;  va=0x004028a0 pa=0x000028a0 sz=64746 vsz=64746 rwx=--r-x .text 0x004028a2 4156 push r14 0x004028a4 4155 push r13 0x004028a6 4154 push r12 0x004028a8 55 push rbp 0x004028a9 53 push rbx 0x004028aa 89fb mov ebx, edi 0x004028ac 4889f5 mov rbp, rsi 0x004028af 4881ec980300. sub rsp, 0x398 0x004028b6 488b3e mov rdi, qword [rsi]
Addresses, symbolic names and even custom set flags can be used as offsets. This type of operation does not change the current seek.
As we have seen,
pd takes an argument specifying the number of instructions to disassemble. This may not be the case with other commands, which will use the default block size for their operation (particularly block writing commands). We may want to fine tune this, but without changing the block size beforehand.
One way to do this is by using
!size after the address, as follows:
[0x004048c5]> p8 @ main 4157415641554154555389fb4889f54881ec98030000488b3e64488b042528000000488984248803000031c0e81fb00000be71854100bf06000000e830feffffbe3f514100bf28514100e851faffffbf28514100e807faffffbfc0a14000c705d89c210002000000e863fc000048b80000000000000080c7050fa8210000000000c605a8a821000148890551a921008b05979c210048c70550a921000000000048c7053da92100ffffffffc6059ea821000083f8020f848308000083f803742f83e8017405e8b6f8ffffbf01000000e80cf9ffff85c00f842c0e0000c705caa8210002000000c60563a8210001eb16be0500000031ffc705b0a8210000000000 [0x004048c5]> p8 @ main ! 32 4157415641554154555389fb4889f54881ec98030000488b3e64488b04252800
Notice that the first command will print 256 bytes, while the second one will print 32 bytes.
Like in Vim, commands can be prefixed by a number specifying the number of times you want it to execute. This is very useful when coupled with "repeatable" complex commands.
Radare2 features an internal
grep which is very handy when you want to filter search results or iterate over them in a clever fashion. It can be used by appending a tilde
~ after a command.
i prints out various info about the currently loaded binary.
[0x004048c5]> i type EXEC (Executable file) file /bin/ls fd 7 size 0x1ce08 blksz 0x0 mode -r-- block 0x100 format elf64 pic false canary true nx true crypto false va true intrp /lib64/ld-linux-x86-64.so.2 bintype elf class ELF64 lang c arch x86 bits 64 machine AMD x86-64 architecture os linux minopsz 1 maxopsz 16 pcalign 0 subsys linux endian little stripped true static false linenum false lsyms false relocs false rpath NONE binsz 119892
But this is a lot to take in. Suppose we want only a few bits of information, such as position independence of code, canary, NX. We can use the internal grep to do this:
[0x004048c5]> i~pic pic false [0x004048c5]> i~canary canary true [0x004048c5]> i~nx nx true
Some commands output their result in table form. Rows and columns can be selected as follows:
[0x004048c5]> drr rax 0x0000000000000000 section_end.GNU_STACK rbx 0x0000000000000000 section_end.GNU_STACK rcx 0x0000000000000000 section_end.GNU_STACK rdx 0x0000000000000000 section_end.GNU_STACK rsi 0x0000000000000000 section_end.GNU_STACK rdi 0x0000000000000000 section_end.GNU_STACK r8 0x0000000000000000 section_end.GNU_STACK r9 0x0000000000000000 section_end.GNU_STACK r10 0x0000000000000000 section_end.GNU_STACK r11 0x0000000000000000 section_end.GNU_STACK r12 0x0000000000000000 section_end.GNU_STACK r13 0x0000000000000000 section_end.GNU_STACK r14 0x0000000000000000 section_end.GNU_STACK r15 0x0000000000000000 section_end.GNU_STACK rip 0x0000000000000000 section_end.GNU_STACK rbp 0x0000000000000000 section_end.GNU_STACK rflags 0x0000000000000000 section_end.GNU_STACK rsp 0x0000000000000000 section_end.GNU_STACK
A particular column can be selected by using
[0x004048c5]> drr~ rax rbx rcx rdx rsi rdi r8 r9 r10 r11 r12 r13 r14 r15 rip rbp rflags rsp
And a row can be selected by using
[0x004048c5]> drr~:5 rdi 0x0000000000000000 section_end.GNU_STACK
The two can also be combined:
[0x004048c5]> drr~:5 rdi
|Pipes and >redirection
Commands can be piped over to external processing tools such as tr, awk, sed, cut, grep and so on.
[0x004048c5]> pd 10 | tr -s ' ' | cut -d ' ' -f 4 | tail -n +2 xor mov pop mov and push push mov mov mov
The output of most commands can be redirected to a file.
[0x004048c5]> pcp > demo.py
A very powerful feature of radare2 is the ability to run a command over multiple points in a binary. This is useful when you tag a series of points which require the same patch and then patching them all in one swoop.
The simple example below prints the first 4 bytes of every function.
[0x004048c5]> p8 4 @@ fcn.*
Some commands will automatically add flags which can be iterated over. For example:
[0x004048c5]> / err Searching 3 bytes from 0x00400000 to 0x0061d480: 65 72 72 Searching 3 bytes in [0x400000-0x61d480] hits: 6 0x00401094 hit0_0 "err" 0x0040117f hit0_1 "err" 0x0040124d hit0_2 "err" 0x00416137 hit0_3 "err" 0x00417470 hit0_4 "err" 0x00417695 hit0_5 "err" [0x004048c5]> pd 5 @@ hit0_*
We first look through the binary for 'err'. This results in flags being set at every corresponding 'hit' points. We can then iterate over these 'hits' and further process them.
Quick conversions can be performed via the use of
[0x004048c5]> ? 1234 1234 0x4d2 02322 1.2K 0000:04d2 1234 11010010 1234.0 0.000000f 0.000000
Other useful commands can be found using
[0x004048c5]> ??? |Usage: ?[?[?]] expression | ? eip-0x804800 show hex and dec result for this math expr | ?: list core cmd plugins | ?! [cmd] ? != 0 | ?+ [cmd] ? > 0 | ?- [cmd] ? < 0 | ?= eip-0x804800 hex and dec result for this math expr | ?? show value of operation | ?? [cmd] ? == 0 run command when math matches | ?B [elem] show range boundaries like 'e?search.in | ?P paddr get virtual address for given physical one | ?S addr return section name of given address | ?V show library version of r_core | ?X num|expr returns the hexadecimal value numeric expr | ?_ hudfile load hud menu with given file | ?b [num] show binary value of number | ?b64[-] [str] encode/decode in base64 | ?d[.] opcode describe opcode for asm.arch | ?e string echo string | ?f [num] [str] map each bit of the number as flag string index | ?h [str] calculate hash for given string | ?i[ynmkp] arg prompt for number or Yes,No,Msg,Key,Path and store in $$? | ?ik press any key input dialog | ?im message show message centered in screen | ?in prompt noyes input prompt | ?iy prompt yesno input prompt | ?l str returns the length of string | ?o num get octal value | ?p vaddr get physical address for given virtual address | ?r [from] [to] generate random number between from-to | ?s from to step sequence of numbers from to by steps | ?t cmd returns the time to run a command | ?u num get value in human units (KB, MB, GB, TB) | ?v eip-0x804800 show hex value of math expr | ?vi rsp-rbp show decimal value of math expr | ?x num|str|-hexst returns the hexpair of number or string | ?y [str] show contents of yank buffer, or set with string