One of the key features of radare2 is displaying information in many formats. The goal is to offer a selection of display choices to interpret binary data in the best possible way.
Binary data can be represented as integers, shorts, longs, floats, timestamps, hexpair strings, or more complex formats like C structures, disassembly listings, decompilation listing, be a result of an external processing...
Below is a list of available print modes listed by p?:
[0x00005310]> p?
|Usage: p[=68abcdDfiImrstuxz] [arg|len] [@addr]
| p[b|B|xb] [len] ([S]) bindump N bits skipping S bytes
| p[iI][df] [len] print N ops/bytes (f=func) (see pi? and pdi)
| p[kK] [len] print key in randomart (K is for mosaic)
| p-[?][jh] [mode] bar|json|histogram blocks (mode: e?search.in)
| p2 [len] 8x8 2bpp-tiles
| p3 [file] print stereogram (3D)
| p6[de] [len] base64 decode/encode
| p8[?][j] [len] 8bit hexpair list of bytes
| p=[?][bep] [N] [L] [b] show entropy/printable chars/chars bars
| pa[edD] [arg] pa:assemble pa[dD]:disasm or pae: esil from hex
| pA[n_ops] show n_ops address and type
| pb[?] [n] bitstream of N bits
| pB[?] [n] bitstream of N bytes
| pc[?][p] [len] output C (or python) format
| pC[aAcdDxw] [rows] print disassembly in columns (see hex.cols and pdi)
| pd[?] [sz] [a] [b] disassemble N opcodes (pd) or N bytes (pD)
| pf[?][.nam] [fmt] print formatted data (pf.name, pf.name $
| pF[?][apx] print asn1, pkcs7 or x509
| pg[?][x y w h] [cmd] create new visual gadget or print it (see pg? for details)
| ph[?][=|hash] ([len]) calculate hash for a block
| pj[?] [len] print as indented JSON
| pm[?] [magic] print libmagic data (see pm? and /m?)
| po[?] hex print operation applied to block (see po?)
| pp[?][sz] [len] print patterns, see pp? for more help
| pq[?][is] [len] print QR code with the first Nbytes
| pr[?][glx] [len] print N raw bytes (in lines or hexblocks, 'g'unzip)
| ps[?][pwz] [len] print pascal/wide/zero-terminated strings
| pt[?][dn] [len] print different timestamps
| pu[?][w] [len] print N url encoded bytes (w=wide)
| pv[?][jh] [mode] show variable/pointer/value in memory
| pwd display current working directory
| px[?][owq] [len] hexdump of N bytes (o=octal, w=32bit, q=64bit)
| pz[?] [len] print zoom view (see pz? for help)
[0x00005310]>
Tip: when using json output, you can append the ~{} to the command to get a pretty-printed version of the output:
[0x00000000]> oj
[{"raised":false,"fd":563280,"uri":"malloc://512","from":0,"writable":true,"size":512,"overlaps":false}]
[0x00000000]> oj~{}
[
{
"raised": false,
"fd": 563280,
"uri": "malloc://512",
"from": 0,
"writable": true,
"size": 512,
"overlaps": false
}
]
For more on the magical powers of ~ see the help in ?@?, and the Command Format chapter earlier in the book.
px gives a user-friendly output showing 16 pairs of numbers per row with offsets and raw representations:
[0x00404888]> p8 16
31ed4989d15e4889e24883e4f0505449
Currently supported timestamp output modes are:
[0x00404888]> pt?
|Usage: pt [dn] print timestamps
| pt. print current time
| pt print UNIX time (32 bit `cfg.bigendian`) Since January 1, 1970
| ptd print DOS time (32 bit `cfg.bigendian`) Since January 1, 1980
| pth print HFS time (32 bit `cfg.bigendian`) Since January 1, 1904
| ptn print NTFS time (64 bit `cfg.bigendian`) Since January 1, 1601
For example, you can 'view' the current buffer as timestamps in the ntfs time:
[0x08048000]> e cfg.bigendian = false
[0x08048000]> pt 4
29:04:32948 23:12:36 +0000
[0x08048000]> e cfg.bigendian = true
[0x08048000]> pt 4
20:05:13001 09:29:21 +0000
As you can see, the endianness affects the result. Once you have printed a timestamp, you can grep the output, for example, by year:
[0x08048000]> pt ~1974 | wc -l
15
[0x08048000]> pt ~2022
27:04:2022 16:15:43 +0000
The default date format can be configured using the cfg.datefmt variable. Formatting rules for it follow the well known strftime(3) format. Check the manpage for more details, but these are the most important:
%a The abbreviated name of the day of the week according to the current locale.
%A The full name of the day of the week according to the current locale.
%d The day of the month as a decimal number (range 01 to 31).
%D Equivalent to %m/%d/%y. (Yecch—for Americans only).
%H The hour as a decimal number using a 24-hour clock (range 00 to 23).
%I The hour as a decimal number using a 12-hour clock (range 01 to 12).
%m The month as a decimal number (range 01 to 12).
%M The minute as a decimal number (range 00 to 59).
%p Either "AM" or "PM" according to the given time value.
%s The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). (TZ)
%S The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds.)
%T The time in 24-hour notation (%H:%M:%S). (SU)
%y The year as a decimal number without a century (range 00 to 99).
%Y The year as a decimal number including the century.
%z The +hhmm or -hhmm numeric timezone (that is, the hour and minute offset from UTC). (SU)
%Z The timezone name or abbreviation.
There are print modes available for all basic types. If you are interested in a more complex structure, type pf?? for format characters and pf??? for examples:
[0x00499999]> pf??
|pf: pf[.k[.f[=v]]|[v]]|[n]|[0|cnt][fmt] [a0 a1 ...]
| Format:
| b byte (unsigned)
| B resolve enum bitfield (see t?)
| c char (signed byte)
| C byte in decimal
| d 0xHEX value (4 bytes) (see 'i' and 'x')
| D disassemble one opcode
| e temporally swap endian
| E resolve enum name (see t?)
| f float value (4 bytes)
| F double value (8 bytes)
| i signed integer value (4 bytes) (see 'd' and 'x')
| n next char specifies size of signed value (1, 2, 4 or 8 byte(s))
| N next char specifies size of unsigned value (1, 2, 4 or 8 byte(s))
| o octal value (4 byte)
| p pointer reference (2, 4 or 8 bytes)
| q quadword (8 bytes)
| r CPU register `pf r (eax)plop`
| s 32bit pointer to string (4 bytes)
| S 64bit pointer to string (8 bytes)
| t UNIX timestamp (4 bytes)
| T show Ten first bytes of buffer
| u uleb128 (variable length)
| w word (2 bytes unsigned short in hex)
| x 0xHEX value and flag (fd @ addr) (see 'd' and 'i')
| X show formatted hexpairs
| z null terminated string
| Z null terminated wide string
| ? data structure `pf ? (struct_name)example_name`
| * next char is pointer (honors asm.bits)
| + toggle show flags for each offset
| : skip 4 bytes
| . skip 1 byte
| ; rewind 4 bytes
| , rewind 1 byte
Use triple-question-mark pf??? to get some examples using print format strings.
[0x00499999]>
pf???
|pf: pf[.k[.f[=v]]|[v]]|[n]|[0|cnt][fmt] [a0 a1 ...]
| Examples:
| pf 3xi foo bar 3-array of struct, each
with named fields: 'foo' as hex, and 'bar' as int
| pf B (BitFldType)arg_name` bitfield type
| pf E (EnumType)arg_name` enum type
| pf.obj xxdz prev next size name Define the obj format as
xxdz
| pf obj=xxdz prev next size name Same as above
| pf *z*i*w nb name blob Print the pointers with
given labels
| pf iwq foo bar troll Print the iwq format with
foo, bar, troll as the respective names for the fields
| pf 0iwq foo bar troll Same as above, but
considered as a union (all fields at offset 0)
| pf.plop ? (troll)mystruct Use structure troll
previously defined
| pfj.plop @ 0x14 Apply format object at
the given offset
| pf 10xiz pointer length string Print a size 10 array of
the xiz struct with its field names
| pf 5sqw string quad word Print an array with sqw
struct along with its field names
| pf {integer}? (bifc) Print integer times the
following format (bifc)
| pf [4]w[7]i Print an array of 4 words
and then an array of 7 integers
| pf ic...?i foo bar "(pf xw yo foo)troll" yo Print nested anonymous
structures
| pf ;..x Print value located 6
bytes from current offset
| pf [10]z[3]i[10]Zb Print an fixed size str,
widechar, and var
| pfj +F @ 0x14 Print the content at
given offset with flag
| pf n2 print signed short (2
bytes) value. Use N instead of n for printing unsigned values
| pf [2]? (plop)structname @ 0 Prints an array of
structs
| pf eqew bigWord beef Swap endianness and print
with given labels
| pf.foo rr (eax)reg1 (eip)reg2 Create object referencing
to register values
| pf tt troll plop print time stamps with
labels troll and plop
Some examples are below:
[0x4A13B8C0]> pf i
0x00404888 = 837634441
[0x4A13B8C0]> pf
0x00404888 = 837634432.000000
Valid print code formats for human-readable languages are:
• pc C
• pc* print 'wx' r2 commands
• pch C half-words (2 byte)
• pcw C words (4 byte)
• pcd C dwords (8 byte)
• pci C array of bytes with instructions
• pca GAS .byte blob
• pcA .bytes with instructions in comments
• pcs string
• pcS shellscript that reconstructs the bin
• pcj json
• pcJ javascript
• pco Objective-C
• pcp python
• pck kotlin
• pcr rust
• pcv JaVa
• pcV V (vlang.io)
• pcy yara
• pcz Swift
If we need to create a .c file containing a binary blob, use the pc command, that creates this output. The default size is like in many other commands: the block size, which can be changed with the b command.
We can also just temporarily override this block size by expressing it as an argument.
[0xB7F8E810]>
pc 32
#define _BUFFER_SIZE 32
unsigned char buffer[_BUFFER_SIZE] = {
0x89, 0xe0, 0xe8, 0x49, 0x02, 0x00, 0x00, 0x89, 0xc7, 0xe8, 0xe2, 0xff,
0xff, 0xff, 0x81, 0xc3, 0xd6, 0xa7, 0x01, 0x00, 0x8b, 0x83, 0x00, 0xff,
0xff, 0xff, 0x5a, 0x8d, 0x24, 0x84, 0x29, 0xc2 };
That cstring can be used in many programming languages, not just C.
[0x7fcd6a891630]>
pcs
"\x48\x89\xe7\xe8\x68\x39\x00\x00\x49\x89\xc4\x8b\x05\xef\x16\x22\x00\x5a\x48\x8d\x24\xc4\x29\xc2\x52\x48\x89\xd6\x49\x89\xe5\x48\x83\xe4\xf0\x48\x8b\x3d\x06\x1a
Strings are probably one of the most important entry points when starting to reverse engineer a program because they usually reference information about functions' actions (asserts, debug or info messages...). Therefore, radare supports various string formats:
[0x00000000]> ps?
|Usage: ps[bijqpsuwWxz+] [N] Print String
| ps print string
| ps+[j] print libc++ std::string (same-endian, ascii, zero-terminated)
| psb print strings in current block
| psi print string inside curseek
| psj print string in JSON format
| psp[j] print pascal string
| psq alias for pqs
| pss print string in screen (wrap width)
| psu[zj] print utf16 unicode (json)
| psw[j] print 16bit wide string
| psW[j] print 32bit wide string
| psx show string with escaped chars
| psz[j] print zero-terminated string
Most strings are zero-terminated. Below there is an example using the debugger to continue the execution of a program until it executes the 'open' syscall. When we recover the control over the process, we get the arguments passed to the syscall, pointed by %ebx. In the case of the 'open' call, it is a zero terminated string which we can inspect using psz.
[0x4A13B8C0]> dcs open
0x4a14fc24 syscall(5) open ( 0x4a151c91 0x00000000 0x00000000 ) = 0xffffffda
[0x4A13B8C0]> dr
eax 0xffffffda esi 0xffffffff eip 0x4a14fc24
ebx 0x4a151c91 edi 0x4a151be1 oeax 0x00000005
ecx 0x00000000 esp 0xbfbedb1c eflags 0x200246
edx 0x00000000 ebp 0xbfbedbb0 cPaZstIdor0 (PZI)
[0x4A13B8C0]>
[0x4A13B8C0]> psz @ 0x4a151c91
/etc/ld.so.cache
It is also possible to print various packed data types using the pf command:
[0xB7F08810]> pf xxS @ rsp
0x7fff0d29da30 = 0x00000001
0x7fff0d29da34 = 0x00000000
0x7fff0d29da38 = 0x7fff0d29da38 -> 0x0d29f7ee /bin/ls
This can be used to look at the arguments passed to a function. To achieve this, simply pass a 'format memory string' as an argument to pf, and temporally change the current seek position/offset using @. It is also possible to define arrays of structures with pf. To do this, prefix the format string with a numeric value. You can also define a name for each field of the structure by appending them as a space-separated arguments list.
[0x4A13B8C0]> pf 2*xw pointer type @ esp
0x00404888 [0] {
pointer :
(*0xffffffff8949ed31) type : 0x00404888 = 0x8949ed31
0x00404890 = 0x48e2
}
0x00404892 [1] {
(*0x50f0e483) pointer : 0x00404892 = 0x50f0e483
type : 0x0040489a = 0x2440
}
A practical example for using pf on a binary of a GStreamer plugin:
$ radare2 /usr/lib/gstreamer-1.0/libgstflv.so
[0x00006020]> aa; pdf @ sym.gst_plugin_flv_get_desc
[x] Analyze all flags starting with sym. and entry0 (aa)
sym.gst_plugin_flv_get_desc ();
[...]
0x00013830 488d0549db0000 lea rax, section..data.rel.ro ; 0x21380
0x00013837 c3 ret
[0x00006020]> s section..data.rel.ro
[0x00021380]> pf ii*z*zp*z*z*z*z*z*z major minor name desc init version license source package origin release_datetime
major : 0x00021380 = 1
minor : 0x00021384 = 18
name : (*0x19cf2)0x00021388 = "flv"
desc : (*0x1b358)0x00021390 = "FLV muxing and demuxing plugin"
init : 0x00021398 = (qword)0x0000000000013460
version : (*0x19cae)0x000213a0 = "1.18.2"
license : (*0x19ce1)0x000213a8 = "LGPL"
source : (*0x19cd0)0x000213b0 = "gst-plugins-good"
package : (*0x1b378)0x000213b8 = "GStreamer Good Plugins (Arch Linux)"
origin : (*0x19cb5)0x000213c0 = "https://www.archlinux.org/"
release_datetime : (*0x19cf6)0x000213c8 = "2020-12-06"
The pd command is used to disassemble code. It accepts a numeric value to specify how many instructions should be disassembled. The pD command is similar but instead of a number of instructions, it decompiles a given number of bytes.
• d : disassembly N opcodes count of opcodes
• D : asm.arch disassembler bsize bytes
[0x00404888]> pd 1
;-- entry0:
0x00404888 31ed xor ebp, ebp
The architecture flavor for the disassembler is defined by the asm.arch eval variable. You can use e asm.arch=?? to list all available architectures.
[0x00005310]> e asm.arch=??
_dAe _8_16 6502 LGPL3 6502/NES/C64/Tamagotchi/T-1000 CPU
_dAe _8 8051 PD 8051 Intel CPU
_dA_ _16_32 arc GPL3 Argonaut RISC Core
a___ _16_32_64 arm.as LGPL3 as ARM Assembler (use ARM_AS environment)
adAe _16_32_64 arm BSD Capstone ARM disassembler
_dA_ _16_32_64 arm.gnu GPL3 Acorn RISC Machine CPU
_d__ _16_32 arm.winedbg LGPL2 WineDBG's ARM disassembler
adAe _8_16 avr GPL AVR Atmel
adAe _16_32_64 bf LGPL3 Brainfuck
_dA_ _32 chip8 LGPL3 Chip8 disassembler
_dA_ _16 cr16 LGPL3 cr16 disassembly plugin
_dA_ _32 cris GPL3 Axis Communications 32-bit embedded processor
adA_ _32_64 dalvik LGPL3 AndroidVM Dalvik
ad__ _16 dcpu16 PD Mojang's DCPU-16
_dA_ _32_64 ebc LGPL3 EFI Bytecode
adAe _16 gb LGPL3 GameBoy(TM) (z80-like)
_dAe _16 h8300 LGPL3 H8/300 disassembly plugin
_dAe _32 hexagon LGPL3 Qualcomm Hexagon (QDSP6) V6
_d__ _32 hppa GPL3 HP PA-RISC
_dAe _0 i4004 LGPL3 Intel 4004 microprocessor
_dA_ _8 i8080 BSD Intel 8080 CPU
adA_ _32 java Apache Java bytecode
_d__ _32 lanai GPL3 LANAI
...
There are multiple options which can be used to configure the output of the disassembler. All these options are described in e? asm.
[0x00005310]> e? asm.
asm.anal: Analyze code and refs while disassembling (see anal.strings)
asm.arch: Set the arch to be used by asm
asm.assembler: Set the plugin name to use when assembling
asm.bbline: Show empty line after every basic block
asm.bits: Word size in bits at assembler
asm.bytes: Display the bytes of each instruction
asm.bytespace: Separate hexadecimal bytes with a whitespace
asm.calls: Show callee function related info as comments in disasm
asm.capitalize: Use camelcase at disassembly
asm.cmt.col: Column to align comments
asm.cmt.flgrefs: Show comment flags associated to branch reference
asm.cmt.fold: Fold comments, toggle with Vz
...
Currently there are 136 asm. configuration variables so we do not list them all.
The asm.syntax variable is used to change the flavor of the assembly syntax used by a disassembler engine. To switch between Intel and AT&T representations:
e asm.syntax = intel
e asm.syntax = att
You can also check asm.pseudo, which is an experimental pseudocode view, and asm.esil which outputs ESIL ('Evaluable Strings Intermediate Language'). ESIL's goal is to have a human-readable representation of every opcode semantics. Such representations can be evaluated (interpreted) to emulate effects of individual instructions.
Flags are conceptually similar to bookmarks. They associate a name with a given offset in a file. Flags can be grouped into 'flag spaces'. A flag space is a namespace for flags, grouping together flags of similar characteristics or type. Examples for flag spaces: sections, registers, symbols.
To create a flag:
[0x4A13B8C0]> f flag_name @ offset
You can remove a flag by appending the - character to command. Most commands accept - as argument-prefix as an indication to delete something.
[0x4A13B8C0]> f-flag_name
To switch between or create new flagspaces use the fs command:
[0x00005310]> fs?
|Usage: fs [*] [+-][flagspace|addr] # Manage flagspaces
| fs display flagspaces
| fs* display flagspaces as r2 commands
| fsj display flagspaces in JSON
| fs * select all flagspaces
| fs flagspace select flagspace or create if it doesn't exist
| fs-flagspace remove flagspace
| fs-* remove all flagspaces
| fs+foo push previous flagspace and set
| fs- pop to the previous flagspace
| fs-. remove the current flagspace
| fsq list flagspaces in quiet mode
| fsm [addr] move flags at given address to the current flagspace
| fss display flagspaces stack
| fss* display flagspaces stack in r2 commands
| fssj display flagspaces stack in JSON
| fsr newname rename selected flagspace
[0x00005310]> fs
0 439 * strings
1 17 * symbols
2 54 * sections
3 20 * segments
4 115 * relocs
5 109 * imports
[0x00005310]>
Here there are some command examples:
[0x4A13B8C0]> fs symbols ; select only flags in symbols flagspace
[0x4A13B8C0]> f ; list only flags in symbols flagspace
[0x4A13B8C0]> fs * ; select all flagspaces
[0x4A13B8C0]> f myflag ; create a new flag called 'myflag'
[0x4A13B8C0]> f-myflag ; delete the flag called 'myflag'
You can rename flags with fr.
Every flag name should be unique for addressing reasons. But it is quite a common need to have the flags, for example inside the functions, with simple and ubiquitous names like loop or return. For this purpose you can use so called "local" flags, which are tied to the function where they reside. It is possible to add them using f. command:
[0x00003a04]> pd 10
│ 0x00003a04 48c705c9cc21. mov qword [0x002206d8], 0xffffffffffffffff ;
[0x2206d8:8]=0
│ 0x00003a0f c60522cc2100. mov byte [0x00220638], 0 ; [0x220638:1]=0
│ 0x00003a16 83f802 cmp eax, 2
│ .─< 0x00003a19 0f84880d0000 je 0x47a7
│ │ 0x00003a1f 83f803 cmp eax, 3
│ .──< 0x00003a22 740e je 0x3a32
│ ││ 0x00003a24 83e801 sub eax, 1
│.───< 0x00003a27 0f84ed080000 je 0x431a
││││ 0x00003a2d e8fef8ffff call sym.imp.abort ; void abort(void)
││││ ; CODE XREF from main (0x3a22)
││╰──> 0x00003a32 be07000000 mov esi, 7
[0x00003a04]> f. localflag @ 0x3a32
[0x00003a04]> f.
0x00003a32 localflag [main + 210]
[0x00003a04]> pd 10
│ 0x00003a04 48c705c9cc21. mov qword [0x002206d8], 0xffffffffffffffff ;
[0x2206d8:8]=0
│ 0x00003a0f c60522cc2100. mov byte [0x00220638], 0 ; [0x220638:1]=0
│ 0x00003a16 83f802 cmp eax, 2
│ .─< 0x00003a19 0f84880d0000 je 0x47a7
│ │ 0x00003a1f 83f803 cmp eax, 3
│ .──< 0x00003a22 740e je 0x3a32 ; main.localflag
│ ││ 0x00003a24 83e801 sub eax, 1
│.───< 0x00003a27 0f84ed080000 je 0x431a
││││ 0x00003a2d e8fef8ffff call sym.imp.abort ; void abort(void)
││││ ; CODE XREF from main (0x3a22)
││`──> .localflag:
││││ ; CODE XREF from main (0x3a22)
││`──> 0x00003a32 be07000000 mov esi, 7
[0x00003a04]>
radare2 offers flag zones, which lets you label different offsets on the scrollbar, for making it easier to navigate through large binaries. You can set a flag zone on the current seek using:
[0x00003a04]> fz flag-zone-name
Set scr.scrollbar=1 and go to the Visual mode, to see your flag zone appear on the scrollbar on the right end of the window.
See fz? for more information.
Radare can manipulate a loaded binary file in many ways. You can resize the file, move and copy/paste bytes, insert new bytes (shifting data to the end of the block or file), or simply overwrite bytes. New data may be given as a wide-string, assembler instructions, or the data may be read in from another file.
Resize the file using the r command. It accepts a numeric argument. A positive value sets a new size for the file. A negative one will truncate the file to the current seek position minus N bytes.
r 1024 ; resize the file to 1024 bytes
r -10 @ 33 ; strip 10 bytes at offset 33
Write bytes using the w command. It accepts multiple input formats like inline assembly, endian-friendly dwords, files, hexpair files, wide strings:
[0x00404888]> w?
Usage: w[x] [str] [ | w[1248][+-][n] increment/decrement byte,word.. | w foobar write string 'foobar' | w0 [len] write 'len' bytes with value 0x00 | w6[de] base64/hex write base64 [d]ecoded or [e]ncoded string | wa[?] push ebp write opcode, separated by ';' (use '"' around the command) | waf f.asm assemble file and write bytes | waF f.asm assemble file and write bytes and show 'wx' op with hexpair bytes of assembled code | wao[?] op modify opcode (change conditional of jump. nop, etc) | wA[?] r 0 alter/modify opcode at current seek (see wA?) | wb 010203 fill current block with cyclic hexpairs | wB[-]0xVALUE set or unset bits with given value | wc list all write changes | wc[?][jir+-*?] write cache undo/commit/reset/list (io.cache) | wd [off] [n] duplicate N bytes from offset at current seek (memcpy) (see y?) | we[?] [nNsxX] [arg] extend write operations (insert instead of replace) | wf[fs] -|file write contents of file at current offset | wh r2 whereis/which shell command | wm f0ff set binary mask hexpair to be used as cyclic write mask | wo[?] hex write in block with operation. 'wo?' fmi | wp[?] -|file apply radare patch file. See wp? fmi | wr 10 write 10 random bytes | ws pstring write 1 byte for length and then the string | wt[f][?] file [sz] write to file (from current seek, blocksize or sz bytes) | wts host:port [sz] send data to remote host:port via tcp:// | ww foobar write wide string 'f\x00o\x00o\x00b\x00a\x00r\x00' | wx[?][fs] 9090 write two intel nops (from wxfile or wxseek) | wv[?] eip+34 write 32-64 bit value honoring cfg.bigendian | wz string write zero terminated string (like w + \x00) Some examples: [0x00000000]> wx 123456 @ 0x8048300 [0x00000000]> wv 0x8048123 @ 0x8049100 [0x00000000]> wa jmp 0x8048320
The wo command (write over) has many subcommands, each combines the existing data with the new data using an operator. The command is applied to the current block. Supported operators include XOR, ADD, SUB...
[0x4A13B8C0]> wo?
|Usage: wo[asmdxoArl24] [hexpairs] @ addr[:bsize]
|Example:
| wox 0x90 ; xor cur block with 0x90
| wox 90 ; xor cur block with 0x90
| wox 0x0203 ; xor cur block with 0203
| woa 02 03 ; add [0203][0203][...] to curblk
| woe 02 03 ; create sequence from 2 to 255 with step 3
|Supported operations:
| wow == write looped value (alias for 'wb')
| woa += addition
| wos -= substraction
| wom *= multiply
| wod /= divide
| wox ^= xor
| woo |= or
| woA &= and
| woR random bytes (alias for 'wr $b'
| wor >>= shift right
| wol <<= shift left
| wo2 2= 2 byte endian swap
| wo4 4= 4 byte endian swap
It is possible to implement cipher-algorithms using radare core primitives and wo. A sample session performing xor(90) + add(01, 02):
[0x7fcd6a891630]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x7fcd6a891630 4889 e7e8 6839 0000 4989 c48b 05ef 1622
0x7fcd6a891640 005a 488d 24c4 29c2 5248 89d6 4989 e548
0x7fcd6a891650 83e4 f048 8b3d 061a 2200 498d 4cd5 1049
0x7fcd6a891660 8d55 0831 ede8 06e2 0000 488d 15cf e600
[0x7fcd6a891630]> wox 90
[0x7fcd6a891630]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x7fcd6a891630 d819 7778 d919 541b 90ca d81d c2d8 1946
0x7fcd6a891640 1374 60d8 b290 d91d 1dc5 98a1 9090 d81d
0x7fcd6a891650 90dc 197c 9f8f 1490 d81d 95d9 9f8f 1490
0x7fcd6a891660 13d7 9491 9f8f 1490 13ff 9491 9f8f 1490
[0x7fcd6a891630]> woa 01 02
[0x7fcd6a891630]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x7fcd6a891630 d91b 787a 91cc d91f 1476 61da 1ec7 99a3
0x7fcd6a891640 91de 1a7e d91f 96db 14d9 9593 1401 9593
0x7fcd6a891650 c4da 1a6d e89a d959 9192 9159 1cb1 d959
0x7fcd6a891660 9192 79cb 81da 1652 81da 1456 a252 7c77
The zoom is a print mode that allows you to get a global view of the whole file or a memory map on a single screen. In this mode, each byte represents file_size/block_size bytes of the file. Use the pz command, or just use Z in the visual mode to toggle the zoom mode.
The cursor can be used to scroll faster through the zoom out view. Pressing z again will zoom-in at the cursor position.
[0x004048c5]> pz?
|Usage: pz [len] print zoomed blocks (filesize/N)
| e zoom.maxsz max size of block
| e zoom.from start address
| e zoom.to end address
| e zoom.byte specify how to calculate each byte
| pzp number of printable chars
| pzf count of flags in block
| pzs strings in range
| pz0 number of bytes with value '0'
| pzF number of bytes with value 0xFF
| pze calculate entropy and expand to 0-255 range
| pzh head (first byte value); This is the default mode
Let's see some examples:
[0x08049790]> e zoom.byte=h
[0x08049790]> pz // or default pzh
0x00000000 7f00 0000 e200 0000 146e 6f74 0300 0000
0x00000010 0000 0000 0068 2102 00ff 2024 e8f0 007a
0x00000020 8c00 18c2 ffff 0080 4421 41c4 1500 5dff
0x00000030 ff10 0018 0fc8 031a 000c 8484 e970 8648
0x00000040 d68b 3148 348b 03a0 8b0f c200 5d25 7074
0x00000050 7500 00e1 ffe8 58fe 4dc4 00e0 dbc8 b885
[0x08049790]> e zoom.byte=p
[0x08049790]> pz // or pzp
0x00000000 2f47 0609 070a 0917 1e9e a4bd 2a1b 2c27
0x00000010 322d 5671 8788 8182 5679 7568 82a2 7d89
0x00000020 8173 7f7b 727a 9588 a07b 5c7d 8daf 836d
0x00000030 b167 6192 a67d 8aa2 6246 856e 8c9b 999f
0x00000040 a774 96c3 b1a4 6c8e a07c 6a8f 8983 6a62
0x00000050 7d66 625f 7ea4 7ea6 b4b6 8b57 a19f 71a2
[0x08049790]> eval zoom.byte = flags
[0x08049790]> pz // or pzf
0x00406e65 48d0 80f9 360f 8745 ffff ffeb ae66 0f1f
0x00406e75 4400 0083 f801 0f85 3fff ffff 410f b600
0x00406e85 3c78 0f87 6301 0000 0fb6 c8ff 24cd 0026
0x00406e95 4100 660f 1f84 0000 0000 0084 c074 043c
0x00406ea5 3a75 18b8 0500 0000 83f8 060f 95c0 e9cd
0x00406eb5 feff ff0f 1f84 0000 0000 0041 8801 4983
0x00406ec5 c001 4983 c201 4983 c101 e9ec feff ff0f
[0x08049790]> e zoom.byte=F
[0x08049790]> pO // or pzF
0x00000000 0000 0000 0000 0000 0000 0000 0000 0000
0x00000010 0000 2b5c 5757 3a14 331f 1b23 0315 1d18
0x00000020 222a 2330 2b31 2e2a 1714 200d 1512 383d
0x00000030 1e1a 181b 0a10 1a21 2a36 281e 1d1c 0e11
0x00000040 1b2a 2f22 2229 181e 231e 181c 1913 262b
0x00000050 2b30 4741 422f 382a 1e22 0f17 0f10 3913
You can limit zooming to a range of bytes instead of the whole bytespace. Change zoom.from and zoom.to eval variables:
[0x00003a04]> e? zoom.
zoom.byte: Zoom callback to calculate each byte (See pz? for help)
zoom.from: Zoom start address
zoom.in: Specify boundaries for zoom
zoom.maxsz: Zoom max size of block
zoom.to: Zoom end address
[0x00003a04]> e zoom.
zoom.byte = h
zoom.from = 0
zoom.in = io.map
zoom.maxsz = 512
zoom.to = 0
Radare2 has an internal clipboard to save and write portions of memory loaded from the current io layer.
This clipboard can be manipulated with the y command.
The two basic operations are
• copy (yank)
• paste
The yank operation will read N bytes (specified by the argument) into the clipboard. We can later use the yy command to paste what we read before into a file.
You can yank/paste bytes in visual mode selecting them with the cursor mode (Vc) and then using the y and Y key bindings which are aliases for y and yy commands of the command-line interface.
[0x00000000]> y?
Usage: y[ptxy] [len] [[@]addr] # See wd? for memcpy, same as 'yf'.
| y! open cfg.editor to edit the clipboard
| y 16 0x200 copy 16 bytes into clipboard from 0x200
| y 16 @ 0x200 copy 16 bytes into clipboard from 0x200
| y 16 copy 16 bytes into clipboard
| y show yank buffer information (srcoff len bytes)
| y* print in r2 commands what's been yanked
| yf 64 0x200 copy file 64 bytes from 0x200 from file
| yfa file copy copy all bytes from file (opens w/ io)
| yfx 10203040 yank from hexpairs (same as ywx)
| yj print in JSON commands what's been yanked
| yp print contents of clipboard
| yq print contents of clipboard in hexpairs
| ys print contents of clipboard as string
| yt 64 0x200 copy 64 bytes from current seek to 0x200
| ytf file dump the clipboard to given file
| yw hello world yank from string
| ywx 10203040 yank from hexpairs (same as yfx)
| yx print contents of clipboard in hexadecimal
| yy 0x3344 paste clipboard
| yz [len] copy nul-terminated string (up to blocksize) into clipboard
Sample session:
[0x00000000]> s 0x100 ; seek at 0x100
[0x00000100]> y 100 ; yanks 100 bytes from here
[0x00000200]> s 0x200 ; seek 0x200
[0x00000200]> yy ; pastes 100 bytes
You can perform a yank and paste in a single line by just using the yt command (yank-to). The syntax is as follows:
[0x4A13B8C0]> x
offset 0 1 2 3 4 5 6 7 8 9 A B 0123456789AB
0x4A13B8C0, 89e0 e839 0700 0089 c7e8 e2ff ...9........
0x4A13B8CC, ffff 81c3 eea6 0100 8b83 08ff ............
0x4A13B8D8, ffff 5a8d 2484 29c2 ..Z.$.).
[0x4A13B8C0]> yt 8 0x4A13B8CC @ 0x4A13B8C0
[0x4A13B8C0]> x
offset 0 1 2 3 4 5 6 7 8 9 A B 0123456789AB
0x4A13B8C0, 89e0 e839 0700 0089 c7e8 e2ff ...9........
0x4A13B8CC, 89e0 e839 0700 0089 8b83 08ff ...9........
0x4A13B8D8, ffff 5a8d 2484 29c2 ..Z.$.).
For most generic reverse engineering tasks like finding the differences between two binary files, which bytes has changed, find differences in the graphs of the code analysis results, and other diffing operations you can just use radiff2:
$ radiff2 -h
Inside r2, the functionalities exposed by radiff2 are available with the c command.
c (short for "compare") allows you to compare arrays of bytes from different sources. The command accepts input in a number of formats and then compares it against values found at current seek position.
[0x00404888]> c?
Usage: c[?dfx] [argument] # Compare
| c [string] Compare a plain with escaped chars string
| c* [string] Same as above, but printing r2 commands instead
| c1 [addr] Compare 8 bits from current offset
| c2 [value] Compare a word from a math expression
| c4 [value] Compare a doubleword from a math expression
| c8 [value] Compare a quadword from a math expression
| cat [file] Show contents of file (see pwd, ls)
| cc [at] Compares in two hexdump columns of block size
| ccc [at] Same as above, but only showing different lines
| ccd [at] Compares in two disasm columns of block size
| ccdd [at] Compares decompiler output (e cmd.pdc=pdg|pdd)
| cf [file] Compare contents of file at current seek
| cg[?] [o] [file] Graphdiff current file and [file]
| cu[?] [addr] @at Compare memory hexdumps of $$ and dst in unified diff
| cud [addr] @at Unified diff disasm from $$ and given address
| cv[1248] [hexpairs] @at Compare 1,2,4,8-byte (silent return in $?)
| cV[1248] [addr] @at Compare 1,2,4,8-byte address contents (silent, return in $?)
| cw[?] [us?] [...] Compare memory watchers
| cx [hexpair] Compare hexpair string (use '.' as nibble wildcard)
| cx* [hexpair] Compare hexpair string (output r2 commands)
| cX [addr] Like 'cc' but using hexdiff output
| cd [dir] chdir
| cl|cls|clear Clear screen, (clear0 to goto 0, 0 only)
To compare memory contents at current seek position against a given string of values, use cx:
[0x08048000]> p8 4
7f 45 4c 46
[0x08048000]> cx 7f 45 90 46
Compare 3/4 equal bytes
0x00000002 (byte=03) 90 ' ' -> 4c 'L'
[0x08048000]>
Another subcommand of the c command is cc which stands for "compare code". To compare a byte sequence with a sequence in memory:
[0x4A13B8C0]> cc 0x39e8e089 @ 0x4A13B8C0
To compare contents of two functions specified by their names:
[0x08049A80]> cc sym.main2 @ sym.main
c8 compares a quadword from the current seek (in the example below, 0x00000000) against a math expression:
[0x00000000]> c8 4
Compare 1/8 equal bytes (0%)
0x00000000 (byte=01) 7f ' ' -> 04 ' '
0x00000001 (byte=02) 45 'E' -> 00 ' '
0x00000002 (byte=03) 4c 'L' -> 00 ' '
The number parameter can, of course, be math expressions which use flag names and anything allowed in an expression:
[0x00000000]> cx 7f469046
Compare 2/4 equal bytes
0x00000001 (byte=02) 45 'E' -> 46 'F'
0x00000002 (byte=03) 4c 'L' -> 90 ' '
You can use the compare command to find differences between a current block and a file previously dumped to a disk:
r2 /bin/true
[0x08049A80]> s 0
[0x08048000]> cf /bin/true
Compare 512/512 equal bytes