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

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.

Configuration

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

Control flow configuration

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.

For now, radare2 also allows you to change the maximum basic block size with anal.bb.maxsize option . The default value just works in most use cases, but it's useful to increase that for example when dealing with obfuscated code. Beware that some of basic blocks control options may disappear in the future in favor of more automated ways to set those.

For some unusual binaries or targets, there is an option anal.noncode. Radare2 doesn't try to analyze data sections as a code by default. But in some cases - malware, packed binaries, binaries for embedded systems, it is often a case. Thus - this option.

Reference control

The most crucial options that change the analysis results drastically. Sometimes some can be disabled to save the time and memory when analyzing big binaries.

   • anal.jmp.ref - to allow references creation for unconditional jumps

   • anal.jmp.cref - same, but for conditional jumps

   • anal.datarefs - to follow the data references in code

   • anal.refstr - search for strings in data references

   • anal.strings - search for strings and creating references

Note that strings references control is disabled by default because it increases the analysis time.

Analysis ranges

There are a few options for this:

   • anal.limits - enables the range limits for analysis operations

   • anal.from - starting address of the limit range

   • anal.to - the corresponding end of the limit range

   • anal.in - specify search boundaries for analysis. You can set it to io.maps, io.sections.exec, dbg.maps and many more. For example:

      • To analyze a specific memory map with anal.from and anal.to, set anal.in = dbg.maps.

      • To analyze in the boundaries set by anal.from and anal.to, set anal.in=range.

      • To analyze in the current mapped segment or section, you can put anal.in=bin.segment or anal.in=bin.section, respectively.

      • To analyze in the current memory map, specify anal.in=dbg.map.

      • To analyze in the stack or heap, you can set anal.in=dbg.stack or anal.in=dbg.heap.

      • To analyze in the current function or basic block, you can specify anal.in=anal.fcn or anal.in=anal.bb.

Please see e anal.in=?? for the complete list.

Jump tables

Jump tables are one of the trickiest targets in binary reverse engineering. There are hundreds of different types, the end result depending on the compiler/linker and LTO stages of optimization. Thus radare2 allows enabling some experimental jump tables detection algorithms using anal.jmp.tbl option. Eventually, algorithms moved into the default analysis loops once they start to work on every supported platform/target/testcase. Two more options can affect the jump tables analysis results too:

   • anal.jmp.indir - follow the indirect jumps, some jump tables rely on them

   • anal.datarefs - follow the data references, some jump tables use those

Platform specific controls

There are two common problems when analyzing embedded targets: ARM/Thumb detection and MIPS GP value. In case of ARM binaries radare2 supports some auto-detection of ARM/Thumb mode switches, but beware that it uses partial ESIL emulation, thus slowing the analysis process. If you will not like the results, particular functions' mode can be overridden with afB command.

The MIPS GP problem is even trickier. It is a basic knowledge that GP value can be different not only for the whole program, but also for some functions. To partially solve that there are options anal.gp and anal.gpfixed. The first one sets the GP value for the whole program or particular function. The latter allows to "constantify" the GP value if some code is willing to change its value, always resetting it if the case. Those are heavily experimental and might be changed in the future in favor of more automated analysis.

Visuals

One of the easiest way to see and check the changes of the analysis commands and variables is to perform a scrolling in a Vv special visual mode, allowing functions preview: