Выбрать главу

0x08000035 89e5 mov ebp, esp

0x08000037 83ec10 sub esp, 0x10

0x0800003a c745f8000000. mov dword [ebp - 8], 0

0x08000041 c745fc000000. mov dword [ebp - 4], 0

,=< 0x08000048 eb08 jmp 0x8000052

.--> 0x0800004a 8345f801 add dword [ebp - 8], 1

:| 0x0800004e 8345fc01 add dword [ebp - 4], 1

:`-> 0x08000052 837dfc09 cmp dword [ebp - 4], 9

`==< 0x08000056 7ef2 jle 0x800004a

0x08000058 8b45f8 mov eax, dword [ebp - 8]

0x0800005b c9 leave

0x0800005c c3 ret

our goal is to hand craft a function with the following structure

create a function at 0x8000034 named code_block:

[0x8000034]> af+ 0x8000034 code_block

In most cases, we use jump or call instructions as code block boundaries. so the range of first block is from 0x08000034 push ebp to 0x08000048 jmp 0x8000052. use afb+ command to add it.

[0x08000034]> afb+ code_block 0x8000034 0x800004a-0x8000034 0x8000052

note that the basic syntax of afb+ is afb+ function_address block_address block_size [jump] [fail]. the final instruction of this block points to a new address(jmp 0x8000052), thus we add the address of jump target (0x8000052) to reflect the jump info.

the next block (0x08000052 ~ 0x08000056) is more likeyly an if conditional statement which has two branches. It will jump to 0x800004a if jle-less or equal, otherwise (the fail condition) jump to next instruction -- 0x08000058.:

[0x08000034]> afb+ code_block 0x8000052 0x8000058-0x8000052 0x800004a 0x8000058

follow the control flow and create the remaining two blocks (two branches) :

[0x08000034]> afb+ code_block 0x800004a 0x8000052-0x800004a 0x8000052

[0x08000034]> afb+ code_block 0x8000058 0x800005d-0x8000058

check our work:

[0x08000034]> afb

0x08000034 0x0800004a 00:0000 22 j 0x08000052

0x0800004a 0x08000052 00:0000 8 j 0x08000052

0x08000052 0x08000058 00:0000 6 j 0x0800004a f 0x08000058

0x08000058 0x0800005d 00:0000 5

[0x08000034]> VV

There are two very important commands for this: afc and afB. The latter is a must-know command for some platforms like ARM. It provides a way to change the "bitness" of the particular function. Basically, allowing to select between ARM and Thumb modes.

afc on the other side, allows to manually specify function calling convention. You can find more information on its usage in calling_conventions.

There are 5 important program wide half-automated analysis commands:

   • aab - perform basic-block analysis ("Nucleus" algorithm)

   • aac - analyze function calls from one (selected or current function)

   • aaf - analyze all function calls

   • aar - analyze data references

   • aad - analyze pointers to pointers references

Those are only generic semi-automated reference searching algorithms. Radare2 provides a wide choice of manual references' creation of any kind. For this fine-grained control you can use ax commands.

Usage: ax[?d-l*] # see also 'afx?'

| ax list refs

| ax* output radare commands

| ax addr [at] add code ref pointing to addr (from curseek)

| ax- [at] clean all refs/refs from addr

| ax-* clean all refs/refs

| axc addr [at] add generic code ref

| axC addr [at] add code call ref

| axg [addr] show xrefs graph to reach current function

| axg* [addr] show xrefs graph to given address, use .axg*;aggv

| axgj [addr] show xrefs graph to reach current function in json format

| axd addr [at] add data ref

| axq list refs in quiet/human-readable format

| axj list refs in json format

| axF [flg-glob] find data/code references of flags

| axm addr [at] copy data/code references pointing to addr to also point to curseek (or at)

| axt [addr] find data/code references to this address

| axf [addr] find data/code references from this address

| axv [addr] list local variables read-write-exec references

| ax. [addr] find data/code references from and to this address

| axff[j] [addr] find data/code references from this function

| axs addr [at] add string ref

The most commonly used ax commands are axt and axf, especially as a part of various r2pipe scripts. Lets say we see the string in the data or a code section and want to find all places it was referenced from, we should use axt:

[0x0001783a]> pd 2

;-- str.02x:

; STRING XREF from 0x00005de0 (sub.strlen_d50)

; CODE XREF from 0x00017838 (str.._s_s_s + 7)

0x0001783a .string "%%%02x" ; len=7

;-- str.src_ls.c:

; STRING XREF from 0x0000541b (sub.free_b04)

; STRING XREF from 0x0000543a (sub.__assert_fail_41f + 27)

; STRING XREF from 0x00005459 (sub.__assert_fail_41f + 58)

; STRING XREF from 0x00005f9e (sub._setjmp_e30)

; CODE XREF from 0x0001783f (str.02x + 5)

0x00017841 .string "src/ls.c" ; len=9

[0x0001783a]> axt

sub.strlen_d50 0x5de0 [STRING] lea rcx, str.02x

(nofunc) 0x17838 [CODE] jae str.02x

There are also some useful commands under axt. Use axtg to generate radare2 commands which will help you to create graphs according to the XREFs.

[0x08048320]> s main

[0x080483e0]> axtg

agn 0x8048337 "entry0 + 23"

agn 0x80483e0 "main"

age 0x8048337 0x80483e0

Use axt* to split the radare2 commands and set flags on those corresponding XREFs.

Also under ax is axg, which finds the path between two points in the file by showing an XREFs graph to reach the location or function. For example:

:> axg sym.imp.printf

- 0x08048a5c fcn 0x08048a5c sym.imp.printf

- 0x080483e5 fcn 0x080483e0 main

- 0x080483e0 fcn 0x080483e0 main

- 0x08048337 fcn 0x08048320 entry0

- 0x08048425 fcn 0x080483e0 main

Use axg* to generate radare2 commands which will help you to create graphs using agn and age commands, according to the XREFs.

Apart from predefined algorithms to identify functions there is a way to specify a function prelude with a configuration option anal.prelude. For example, like e anal.prelude = 0x554889e5 which means

push rbp

mov rbp, rsp

on x86_64 platform. It should be specified before any analysis commands.

Radare2 allows to change the behavior of almost any analysis stages or commands. There are different kinds of the configuration options:

   • Flow control

   • Basic blocks control

   • References control

   • IO/Ranges

   • Jump tables analysis control

   • Platform/target specific options

Two most commonly used options for changing the behavior of control flow analysis in radare2 are anal.hasnext and anal.jmp.after. The first one allows forcing radare2 to continue the analysis after the end of the function, even if the next chunk of the code wasn't called anywhere, thus analyzing all of the available functions. The latter one allows forcing radare2 to continue the analysis even after unconditional jumps.

In addition to those we can also set anal.jmp.indir to follow the indirect jumps, continuing analysis; anal.pushret to analyze push ...; ret sequence as a jump; anal.nopskip to skip the NOP sequences at a function beginning.