Most command names in radare are derived from action names. They should be easy to remember, as they are short. Actually, all commands are single letters. Subcommands or related commands are specified using the second character of the command name. For example, / foo is a command to search plain string, while /x 90 90 is used to look for hexadecimal pairs.
The general format for a valid command (as explained in the Command Format chapter) looks like this:
[.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ...
For example,
> 3s +1024 ; seeks three times 1024 from the current seek
If a command starts with =!, the rest of the string is passed to the currently loaded IO plugin (a debugger, for example). Most plugins provide help messages with =!? or =!help.
$ r2 -d /bin/ls
> =!help ; handled by the IO plugin
If a command starts with !, posix_system() is called to pass the command to your shell. Check !? for more options and usage examples.
> !ls ; run `ls` in the shell
The meaning of the arguments (iter, addr, size) depends on the specific command. As a rule of thumb, most commands take a number as an argument to specify the number of bytes to work with, instead of the currently defined block size. Some commands accept math expressions or strings.
> px 0x17 ; show 0x17 bytes in hexs at current seek
> s base+0x33 ; seeks to flag 'base' plus 0x33
> / lib ; search for 'lib' string.
The @ sign is used to specify a temporary offset location or a seek position at which the command is executed, instead of current seek position. This is quite useful as you don't have to seek around all the time.
> p8 10 @ 0x4010 ; show 10 bytes at offset 0x4010
> f patata @ 0x10 ; set 'patata' flag at offset 0x10
Using @@ you can execute a single command on a list of flags matching the glob. You can think of this as a foreach operation:
> s 0
> / lib ; search 'lib' string
> p8 20 @@ hit0_* ; show 20 hexpairs at each search hit
The > operation is used to redirect the output of a command into a file (overwriting it if it already exists).
> pr > dump.bin ; dump 'raw' bytes of current block to file named 'dump.bin'
> f > flags.txt ; dump flag list to 'flags.txt'
The | operation (pipe) is similar to what you are used to expect from it in a *NIX shell: an output of one command as input to another.
[0x4A13B8C0]> f | grep section | grep text
0x0805f3b0 512 section._text
0x080d24b0 512 section._text_end
You can pass several commands in a single line by separating them with a semicolon ;:
> px ; dr
Using _, you can print the result that was obtained by the last command.
[0x00001060]> axt 0x00002004
main 0x1181 [DATA] lea rdi, str.argv__2d_:__s
[0x00001060]> _
main 0x1181 [DATA] lea rdi, str.argv__2d_:__s
To move around the file we are inspecting we will need to change the offset at which we are using the s command.
The argument is a math expression that can contain flag names, parenthesis, addition, substraction, multiplication of immediates of contents of memory using brackets.
Some example commands:
[0x00000000]> s 0x10
[0x00000010]> s+4
[0x00000014]> s-
[0x00000010]> s+
[0x00000014]>
Observe how the prompt offset changes. The first line moves the current offset to the address 0x10.
The second does a relative seek 4 bytes forward.
And finally, the last 2 commands are undoing, and redoing the last seek operations.
Instead of using just numbers, we can use complex expressions, or basic arithmetic operations to represent the address to seek.
To do this, check the ?$? Help message which describes the internal variables that can be used in the expressions. For example, this is the same as doing s+4 .
[0x00000000]> s $$+4
From the debugger (or when emulating) we can also use the register names as references. They are loaded as flags with the .dr* command, which happens under the hood.
[0x00000000]> s rsp+0x40
Here's the full help of the s command. We will explain in more detail below.
[0x00000000]> s?
Usage: s # Help for the seek commands. See ?$? to see all variables
| s Print current address
| s.hexoff Seek honoring a base from core->offset
| s:pad Print current address with N padded zeros (defaults to 8)
| s addr Seek to address
| s- Undo seek
| s-* Reset undo seek history
| s- n Seek n bytes backward
| s--[n] Seek blocksize bytes backward (/=n)
| s+ Redo seek
| s+ n Seek n bytes forward
| s++[n] Seek blocksize bytes forward (/=n)
| s[j*=!] List undo seek history (JSON, =list, *r2, !=names, s==)
| s/ DATA Search for next occurrence of 'DATA'
| s/x 9091 Search for next occurrence of \x90\x91
| sa [[+-]a] [asz] Seek asz (or bsize) aligned to addr
| sb Seek aligned to bb start
| sC[?] string Seek to comment matching given string
| sf Seek to next function (f->addr+f->size)
| sf function Seek to address of specified function
| sf. Seek to the beginning of current function
| sg/sG Seek begin (sg) or end (sG) of section or file
| sl[?] [+-]line Seek to line
| sn/sp ([nkey]) Seek to next/prev location, as specified by scr.nkey
| so [N] Seek to N next opcode(s)
| sr pc Seek to register
| ss Seek silently (without adding an entry to the seek history)
> 3s++ ; 3 times block-seeking
> s 10+0x80 ; seek at 0x80+10
If you want to inspect the result of a math expression, you can evaluate it using the ? command. Simply pass the expression as an argument. The result can be displayed in hexadecimal, decimal, octal or binary formats.
> ? 0x100+200
0x1C8 ; 456d ; 710o ; 1100 1000
There are also subcommands of ? that display the output in one specific format (base 10, base 16 ,...). See ?v and ?vi.
In the visual mode, you can press u (undo) or U (redo) inside the seek history to return back to previous or forward to the next location.
As a test file, let's use a simple hello_world.c compiled in Linux ELF format. After we compile it let's open it with radare2:
$ r2 hello_world
Now we have the command prompt:
[0x00400410]>
And it is time to go deeper.
All seeking commands that take an address as a command parameter can use any numeral base such as hex, octal, binary or decimal.
Seek to an address 0x0. An alternative command is simply 0x0
[0x00400410]> s 0x0
[0x00000000]>
Print current address:
[0x00000000]> s
0x0
[0x00000000]>
There is an alternate way to print current position: ?v $$.
Seek N positions forward, space is optional:
[0x00000000]> s+ 128
[0x00000080]>
Undo last two seeks to return to the initial address:
[0x00000080]> s-
[0x00000000]> s-
[0x00400410]>
We are back at 0x00400410.
There's also a command to show the seek history:
[0x00400410]> s*
f undo_3 @ 0x400410
f undo_2 @ 0x40041a
f undo_1 @ 0x400410
f undo_0 @ 0x400411
# Current undo/redo position.
f redo_0 @ 0x4005b4
The block size determines how many bytes radare2 commands will process when not given an explicit size argument. You can temporarily change the block size by specifying a numeric argument to the print commands. For example px 20.
[0x00000000]> b?
Usage: b[f] [arg] # Get/Set block size
| b 33 set block size to 33
| b eip+4 numeric argument can be an expression
| b display current block size
| b+3 increase blocksize by 3
| b-16 decrease blocksize by 16
| b* display current block size in r2 command
| bf foo set block size to flag size
| bj display block size information in JSON
| bm 1M set max block size
The b command is used to change the block size:
[0x00000000]> b 0x100 # block size = 0x100
[0x00000000]> b+16 # ... = 0x110
[0x00000000]> b-32 # ... = 0xf0
The bf command is used to change the block size to value specified by a flag. For example, in symbols, the block size of the flag represents the size of the function. To make that work, you have to either run function analysis af (which is included in aa) or manually seek and define some functions e.g. via Vd.
[0x00000000]> bf sym.main # block size = sizeof(sym.main)
[0x00000000]> pD @ sym.main # disassemble sym.main
You can combine two operations in a single pdf command. Except that pdf neither uses nor affects global block size.
[0x00000000]> pdf @ sym.main # disassemble sym.main
Another way around is to use special variables $FB and $FS which denote Function's Beginning and Size at the current seek. Read more about Usable variables.
[0x00000000]> s sym.main + 0x04
[0x00001ec9]> pD @ $FB !$FS # disassemble current function
╭ 211: int main (int argc, char **argv, char **envp);
│ 0x00001ec5 55 push rbp
│ 0x00001ec6 4889e5 mov rbp, rsp
│ 0x00001ec9 4881ecc0000000 sub rsp, 0xc0
...
╰ 0x00001f97 c3 ret
Note: don't put space after ! size designator. See also Command Format.
The concept of sections is tied to the information extracted from the binary. We can display this information by using the i command.
Displaying information about sections:
[0x00005310]> iS
[Sections]
00 0x00000000 0 0x00000000 0 ----
01 0x00000238 28 0x00000238 28 -r-- .interp
02 0x00000254 32 0x00000254 32 -r-- .note.ABI_tag
03 0x00000278 176 0x00000278 176 -r-- .gnu.hash
04 0x00000328 3000 0x00000328 3000 -r-- .dynsym
05 0x00000ee0 1412 0x00000ee0 1412 -r-- .dynstr
06 0x00001464 250 0x00001464 250 -r-- .gnu.version
07 0x00001560 112 0x00001560 112 -r-- .gnu.version_r
08 0x000015d0 4944 0x000015d0 4944 -r-- .rela.dyn
09 0x00002920 2448 0x00002920 2448 -r-- .rela.plt
10 0x000032b0 23 0x000032b0 23 -r-x .init
...
As you may know, binaries have sections and maps. The sections define the contents of a portion of the file that can be mapped in memory (or not). What is mapped is defined by the segments.
Before the IO refactoring done by condret, the S command was used to manage what we now call maps. Currently the S command is deprecated because iS and om should be enough.
Firmware images, bootloaders and binary files usually place various sections of a binary at different addresses in memory. To represent this behavior, radare offers the iS. Use iS? to get the help message. To list all created sections use iS (or iSj to get the json format). The iS= will show the region bars in ascii-art.
You can create a new mapping using the om subcommand as follows:
om fd vaddr [size] [paddr] [rwx] [name]
For Example:
[0x0040100]> om 4 0x00000100 0x00400000 0x0001ae08 rwx test
You can also use om command to view information about mapped sections:
[0x00401000]> om
6 fd: 4 +0x0001ae08 0x00000100 - 0x004000ff rwx test
5 fd: 3 +0x00000000 0x00000000 - 0x0000055f r-- fmap.LOAD0
4 fd: 3 +0x00001000 0x00001000 - 0x000011e4 r-x fmap.LOAD1
3 fd: 3 +0x00002000 0x00002000 - 0x0000211f r-- fmap.LOAD2
2 fd: 3 +0x00002de8 0x00003de8 - 0x0000402f r-- fmap.LOAD3
1 fd: 4 +0x00000000 0x00004030 - 0x00004037 rw- mmap.LOAD3
Use om? to get all the possible subcommands. To list all the defined maps use om (or omj to get the json format or om* to get the r2 commands format). To get the ascii art view use om=.
It is also possible to delete the mapped section using the om-mapid command.
For Example:
[0x00401000]> om-6
Radare's I/O subsystem allows you to map the contents of files into the same I/O space used to contain a loaded binary. New contents can be placed at random offsets.
The o command permits the user to open a file, this is mapped at offset 0 unless it has a known binary header and then the maps are created in virtual addresses.
Sometimes, we want to rebase a binary, or maybe we want to load or map the file in a different address.
When launching r2, the base address can be changed with the -B flag. But you must notice the difference when opening files with unknown headers, like bootloaders, so we need to map them using the -m flag (or specifying it as argument to the o command).
radare2 is able to open files and map portions of them at random places in memory specifying attributes like permissions and name. It is the perfect basic tooling to reproduce an environment like a core file, a debug session, by also loading and mapping all the libraries the binary depends on.
Opening files (and mapping them) is done using the o (open) command. Let's read the help:
[0x00000000]> o?
|Usage: o [com- ] [file] ([offset])
| o list opened files
| o-1 close file descriptor 1
| o-!* close all opened files
| o-- close all files, analysis, binfiles, flags, same as !r2 --
| o [file] open [file] file in read-only
| o+ [file] open file in read-write mode
| o [file] 0x4000 rwx map file at 0x4000
| oa[-] [A] [B] [filename] Specify arch and bits for given file
| oq list all open files
| o* list opened files in r2 commands
| o. [len] open a malloc://[len] copying the bytes from current offset
| o= list opened files (ascii-art bars)
| ob[?] [lbdos] [...] list opened binary files backed by fd
| oc [file] open core file, like relaunching r2
| of [file] open file and map it at addr 0 as read-only
| oi[-|idx] alias for o, but using index instead of fd
| oj[?] list opened files in JSON format
| oL list all IO plugins registered
| om[?] create, list, remove IO maps
| on [file] 0x4000 map raw file at 0x4000 (no r_bin involved)
| oo[?] reopen current file (kill+fork in debugger)
| oo+ reopen current file in read-write
| ood[r] [args] reopen in debugger mode (with args)
| oo[bnm] [...] see oo? for help
| op [fd] prioritize given fd (see also ob)
| ox fd fdx exchange the descs of fd and fdx and keep the mapping
Prepare a simple layout:
$ rabin2 -l /bin/ls
[Linked libraries]
libselinux.so.1
librt.so.1
libacl.so.1
libc.so.6
4 libraries
Map a file:
[0x00001190]> o /bin/zsh 0x499999
List mapped files:
[0x00000000]> o
- 6 /bin/ls @ 0x0 ; r
- 10 /lib/ld-linux.so.2 @ 0x100000000 ; r
- 14 /bin/zsh @ 0x499999 ; r
Print hexadecimal values from /bin/zsh:
[0x00000000]> px @ 0x499999
Unmap files using the o- command. Pass the required file descriptor to it as an argument:
[0x00000000]> o-14
You can also view the ascii table showing the list of the opened files:
[0x00000000]> ob=