The Official Radare2 Book — страница 34 из 64

Radare2 provides a wide set of a features to automate boring work. It ranges from the simple sequencing of the commands to the calling scripts/another programs via IPC (Inter-Process Communication), called r2pipe.

As mentioned a few times before there is an ability to sequence commands using ; semicolon operator.

[0x00404800]> pd 1 ; ao 1

0x00404800 b827e66100 mov eax, 0x61e627 ; "tab"

address: 0x404800

opcode: mov eax, 0x61e627

prefix: 0

bytes: b827e66100

ptr: 0x0061e627

refptr: 0

size: 5

type: mov

esil: 6415911,rax,=

stack: null

family: cpu

[0x00404800]>

It simply runs the second command after finishing the first one, like in a shell.

The second important way to sequence the commands is with a simple pipe |

ao|grep address

Note, the | pipe only can pipe output of r2 commands to external (shell) commands, like system programs or builtin shell commands. There is a similar way to sequence r2 commands, using the backtick operator `command`. The quoted part will undergo command substitution and the output will be used as an argument of the command line.

For example, we want to see a few bytes of the memory at the address referred to by the 'mov eax, addr' instruction. We can do that without jumping to it, using a sequence of commands:

[0x00404800]> pd 1

0x00404800 b827e66100 mov eax, 0x61e627 ; "tab"

[0x00404800]> ao

address: 0x404800

opcode: mov eax, 0x61e627

prefix: 0

bytes: b827e66100

ptr: 0x0061e627

refptr: 0

size: 5

type: mov

esil: 6415911,rax,=

stack: null

family: cpu

[0x00404800]> ao~ptr[1]

0x0061e627

0

[0x00404800]> px 10 @ `ao~ptr[1]`

- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF

0x0061e627 7461 6200 2e69 6e74 6572 tab..inter

[0x00404800]>

And of course it's possible to redirect the output of an r2 command into a file, using the > and >> commands

[0x00404800]> px 10 @ `ao~ptr[1]` > example.txt

[0x00404800]> px 10 @ `ao~ptr[1]` >> example.txt

Radare2 also provides quite a few Unix type file processing commands like head, tail, cat, grep and many more. One such command is Uniq, which can be used to filter a file to display only non-duplicate content. So to make a new file with only unique strings, you can do:

[0x00404800]> uniq file > uniq_file

The head command can be used to see the first N number of lines in the file, similarly tail command allows the last N number of lines to be seen.

[0x00404800]> head 3 foodtypes.txt

1 Protein

2 Carbohydrate

3 Fat

[0x00404800]> tail 2 foodtypes.txt

3 Shake

4 Milk

The join command could be used to merge two different files with common first field.

[0x00404800]> cat foodtypes.txt

1 Protein

2 Carbohydrate

3 Fat

[0x00404800]> cat foods.txt

1 Cheese

2 Potato

3 Butter

[0x00404800]> join foodtypes foods.txt

1 Protein Cheese

2 Carbohydrate Potato

3 Fat Butter

Similarly, sorting the content is also possible with the sort command. A typical example could be:

[0x00404800]> sort file

eleven

five

five

great

one

one

radare

The ?$? command describes several helpful variables you can use to do similar actions even more easily, like the $v "immediate value" variable, or the $m opcode memory reference variable.

Loops

One of the most common task in automation is looping through something, there are multiple ways to do this in radare2.

We can loop over flags:

@@ flagname-regex

For example, we want to see function information with afi command:

[0x004047d6]> afi

#

offset: 0x004047d0

name: entry0

size: 42

realsz: 42

stackframe: 0

call-convention: amd64

cyclomatic-complexity: 1

bits: 64

type: fcn [NEW]

num-bbs: 1

edges: 0

end-bbs: 1

call-refs: 0x00402450 C

data-refs: 0x004136c0 0x00413660 0x004027e0

code-xrefs:

data-xrefs:

locals:0

args: 0

diff: type: new

[0x004047d6]>

Now let's say, for example, that we'd like see a particular field from this output for all functions found by analysis. We can do that with a loop over all function flags (whose names begin with fcn.):

[0x004047d6]> fs functions

[0x004047d6]> afi @@ fcn.* ~name

This command will extract the name field from the afi output of every flag with a name matching the regexp fcn.*. There are also a predefined loop called @@f, which runs your command on every functions found by r2:

[0x004047d6]> afi @@f ~name

We can also loop over a list of offsets, using the following syntax:

@@=1 2 3 ... N

For example, say we want to see the opcode information for 2 offsets: the current one, and at current + 2:

[0x004047d6]> ao @@=$$ $$+2

address: 0x4047d6

opcode: mov rdx, rsp

prefix: 0

bytes: 4889e2

refptr: 0

size: 3

type: mov

esil: rsp,rdx,=

stack: null

family: cpu

address: 0x4047d8

opcode: loop 0x404822

prefix: 0

bytes: e248

refptr: 0

size: 2

type: cjmp

esil: 1,rcx,-=,rcx,?{,4212770,rip,=,}

jump: 0x00404822

fail: 0x004047da

stack: null

cond: al

family: cpu

[0x004047d6]>

Note we're using the $$ variable which evaluates to the current offset. Also note that $$+2 is evaluated before looping, so we can use the simple arithmetic expressions.

A third way to loop is by having the offsets be loaded from a file. This file should contain one offset per line.

[0x004047d0]> ?v $$ > offsets.txt

[0x004047d0]> ?v $$+2 >> offsets.txt

[0x004047d0]> !cat offsets.txt

4047d0

4047d2

[0x004047d0]> pi 1 @@.offsets.txt

xor ebp, ebp

mov r9, rdx

radare2 also offers various foreach constructs for looping. One of the most useful is for looping through all the instructions of a function:

[0x004047d0]> pdf

╒ (fcn) entry0 42

│; UNKNOWN XREF from 0x00400018 (unk)

│; DATA XREF from 0x004064bf (sub.strlen_460)

│; DATA XREF from 0x00406511 (sub.strlen_460)

│; DATA XREF from 0x0040b080 (unk)

│; DATA XREF from 0x0040b0ef (unk)

│0x004047d0 xor ebp, ebp

│0x004047d2 mov r9, rdx

│0x004047d5 pop rsi

│0x004047d6 mov rdx, rsp

│0x004047d9 and rsp, 0xfffffffffffffff0

│0x004047dd push rax

│0x004047de push rsp

│0x004047df mov r8, 0x4136c0

│0x004047e6 mov rcx, 0x413660 ; "AWA..AVI..AUI..ATL.%.. "

0A..AVI..AUI.

│0x004047ed mov rdi, main ; "AWAVAUATUH..S..H...." @

0

│0x004047f4 call sym.imp.__libc_start_main

╘0x004047f9 hlt

[0x004047d0]> pi 1 @@i

mov r9, rdx

pop rsi

mov rdx, rsp

and rsp, 0xfffffffffffffff0

push rax

push rsp

mov r8, 0x4136c0

mov rcx, 0x413660

mov rdi, main

call sym.imp.__libc_start_main

hlt

In this example the command pi 1 runs over all the instructions in the current function (entry0). There are other options too (not complete list, check @@? for more information):

   • @@k sdbquery - iterate over all offsets returned by that sdbquery

   • @@t- iterate over on all threads (see dp)

   • @@b - iterate over all basic blocks of current function (see afb)

   • @@f - iterate over all functions (see aflq)

The last kind of looping lets you loop through predefined iterator types:

   • symbols

   • imports

   • registers

   • threads

   • comments

   • functions

   • flags

This is done using the @@@ command. The previous example of listing information about functions can also be done using the @@@ command:

[0x004047d6]> afi @@@ functions ~name

This will extract name field from afi output and will output a huge list of function names. We can choose only the second column, to remove the redundant name: on every line:

[0x004047d6]> afi @@@ functions ~name[1]

Beware, @@@ is not compatible with JSON commands.

Macros

Apart from simple sequencing and looping, radare2 allows to write simple macros, using this construction:

[0x00404800]> (qwe; pd 4; ao)

This will define a macro called 'qwe' which runs sequentially first 'pd 4' then 'ao'. Calling the macro using syntax .(macro) is simple:

[0x00404800]> (qwe; pd 4; ao)

[0x00404800]> .(qwe)

0x00404800 mov eax, 0x61e627 ; "tab"

0x00404805 push rbp

0x00404806 sub rax, section_end.LOAD1

0x0040480c mov rbp, rsp


address: 0x404800

opcode: mov eax, 0x61e627

prefix: 0

bytes: b827e66100

ptr: 0x0061e627

refptr: 0

size: 5

type: mov

esil: 6415911,rax,=

stack: null

family: cpu

[0x00404800]>

To list available macroses simply call (*:

[0x00404800]> (*

(qwe ; pd 4; ao)

And if want to remove some macro, just add '-' before the name:

[0x00404800]> (-qwe)

Macro 'qwe' removed.

[0x00404800]>

Moreover, it's possible to create a macro that takes arguments, which comes in handy in some simple scripting situations. To create a macro that takes arguments you simply add them to macro definition.

[0x00404800]