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

The type inference for local variables and arguments is well integrated with the command afta.

Let's see an example of this with a simple hello_world binary

[0x000007aa]> pdf

| ;-- main:

/ (fcn) sym.main 157

| sym.main ();

| ; var int local_20h @ rbp-0x20

| ; var int local_1ch @ rbp-0x1c

| ; var int local_18h @ rbp-0x18

| ; var int local_10h @ rbp-0x10

| ; var int local_8h @ rbp-0x8

| ; DATA XREF from entry0 (0x6bd)

| 0x000007aa push rbp

| 0x000007ab mov rbp, rsp

| 0x000007ae sub rsp, 0x20

| 0x000007b2 lea rax, str.Hello ; 0x8d4 ; "Hello"

| 0x000007b9 mov qword [local_18h], rax

| 0x000007bd lea rax, str.r2_folks ; 0x8da ; " r2-folks"

| 0x000007c4 mov qword [local_10h], rax

| 0x000007c8 mov rax, qword [local_18h]

| 0x000007cc mov rdi, rax

| 0x000007cf call sym.imp.strlen ; size_t strlen(const char *s)

   • After applying afta

[0x000007aa]> afta

[0x000007aa]> pdf

| ;-- main:

| ;-- rip:

/ (fcn) sym.main 157

| sym.main ();

| ; var size_t local_20h @ rbp-0x20

| ; var size_t size @ rbp-0x1c

| ; var char *src @ rbp-0x18

| ; var char *s2 @ rbp-0x10

| ; var char *dest @ rbp-0x8

| ; DATA XREF from entry0 (0x6bd)

| 0x000007aa push rbp

| 0x000007ab mov rbp, rsp

| 0x000007ae sub rsp, 0x20

| 0x000007b2 lea rax, str.Hello ; 0x8d4 ; "Hello"

| 0x000007b9 mov qword [src], rax

| 0x000007bd lea rax, str.r2_folks ; 0x8da ; " r2-folks"

| 0x000007c4 mov qword [s2], rax

| 0x000007c8 mov rax, qword [src]

| 0x000007cc mov rdi, rax ; const char *s

| 0x000007cf call sym.imp.strlen ; size_t strlen(const char *s)

It also extracts type information from format strings like printf ("fmt : %s , %u , %d", ...), the format specifications are extracted from anal/d/spec.sdb

You could create a new profile for specifying a set of format chars depending on different libraries/operating systems/programming languages like this :

win=spec

spec.win.u32=unsigned int

Then change your default specification to newly created one using this config variable e anal.spec = win

For more information about primitive and user-defined types support in radare2 refer to types chapter.

Radare2 supports the C-syntax data types description. Those types are parsed by a C11-compatible parser and stored in the internal SDB, thus are introspectable with k command.

Most of the related commands are located in t namespace:

[0x000051c0]> t?

| Usage: t # cparse types commands

| t List all loaded types

| tj List all loaded types as json

| t <type> Show type in 'pf' syntax

| t* List types info in r2 commands

| t- <name> Delete types by its name

| t-* Remove all types

| tail [filename] Output the last part of files

| tc [type.name] List all/given types in C output format

| te[?] List all loaded enums

| td[?] <string> Load types from string

| tf List all loaded functions signatures

| tk <sdb-query> Perform sdb query

| tl[?] Show/Link type to an address

| tn[?] [-][addr] manage noreturn function attributes and marks

| to - Open cfg.editor to load types

| to <path> Load types from C header file

| toe [type.name] Open cfg.editor to edit types

| tos <path> Load types from parsed Sdb database

| tp <type> [addr|varname] cast data at <address> to <type> and print it (XXX: type can contain spaces)

| tpv <type> @ [value] Show offset formatted for given type

| tpx <type> <hexpairs> Show value for type with specified byte sequence (XXX: type can contain spaces)

| ts[?] Print loaded struct types

| tu[?] Print loaded union types

| tx[f?] Type xrefs

| tt[?] List all loaded typedefs

Note that the basic (atomic) types are not those from C standard - not char, _Bool, or short. Because those types can be different from one platform to another, radare2 uses definite types like as int8_t or uint64_t and will convert int to int32_t or int64_t depending on the binary or debuggee platform/compiler.

Basic types can be listed using t command, for the structured types you need to use ts, tu or te for enums:

[0x000051c0]> t

char

char *

int

int16_t

int32_t

int64_t

int8_t

long

long long

...

There are three easy ways to define a new type:

   • Directly from the string using td command

   • From the file using to <filename> command

   • Open an $EDITOR to type the definitions in place using to -

[0x000051c0]> "td struct foo {char* a; int b;}"

[0x000051c0]> cat ~/radare2-regressions/bins/headers/s3.h

struct S1 {

int x[3];

int y[4];

int z;

};

[0x000051c0]> to ~/radare2-regressions/bins/headers/s3.h

[0x000051c0]> ts

foo

S1

Also note there is a config option to specify include directories for types parsing

[0x00000000]> e??~dir.type

dir.types: Default path to look for cparse type files

[0x00000000]> e dir.types

/usr/include

Notice below we have used ts command, which basically converts the C type description (or to be precise it's SDB representation) into the sequence of pf commands. See more about print format.

The tp command uses the pf string to print all the members of type at the current offset/given address:

[0x000051c0]> ts foo

pf zd a b

[0x000051c0]> tp foo

a : 0x000051c0 = 'hello'

b : 0x000051cc = 10

[0x000051c0]> tp foo 0x000053c0

a : 0x000053c0 = 'world'

b : 0x000053cc = 20

Also, you could fill your own data into the struct and print it using tpx command

[0x000051c0]> tpx foo 4141414144141414141442001000000

a : 0x000051c0 = AAAAD.....B

b : 0x000051cc = 16

The tp command just performs a temporary cast. But if we want to link some address or variable with the chosen type, we can use tl command to store the relationship in SDB.

[0x000051c0]> tl S1 = 0x51cf

[0x000051c0]> tll

(S1)

x : 0x000051cf = [ 2315619660, 1207959810, 34803085 ]

y : 0x000051db = [ 2370306049, 4293315645, 3860201471, 4093649307 ]

z : 0x000051eb = 4464399

Moreover, the link will be shown in the disassembly output or visual mode:

[0x000051c0 15% 300 /bin/ls]> pd $r @ entry0

;-- entry0:

0x000051c0 xor ebp, ebp

0x000051c2 mov r9, rdx

0x000051c5 pop rsi

0x000051c6 mov rdx, rsp

0x000051c9 and rsp, 0xfffffffffffffff0