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

0x000051cd push rax

0x000051ce push rsp

(S1)

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

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

z : 0x000051eb = 4464399

0x000051f0 lea rdi, loc._edata ; 0x21f248

0x000051f7 push rbp

0x000051f8 lea rax, loc._edata ; 0x21f248

0x000051ff cmp rax, rdi

0x00005202 mov rbp, rsp

Once the struct is linked, radare2 tries to propagate structure offset in the function at current offset, to run this analysis on whole program or at any targeted functions after all structs are linked you have aat command:

[0x00000000]> aa?

| aat [fcn] Analyze all/given function to convert immediate to linked structure offsets (see tl?)

Note sometimes the emulation may not be accurate, for example as below :

|0x000006da push rbp

|0x000006db mov rbp, rsp

|0x000006de sub rsp, 0x10

|0x000006e2 mov edi, 0x20 ; "@"

|0x000006e7 call sym.imp.malloc ; void *malloc(size_t size)

|0x000006ec mov qword [local_8h], rax

|0x000006f0 mov rax, qword [local_8h]

The return value of malloc may differ between two emulations, so you have to set the hint for return value manually using ahr command, so run tl or aat command after setting up the return value hint.

[0x000006da]> ah?

| ahr val set hint for return value of a function

There is one more important aspect of using types in radare2 - using aht you can change the immediate in the opcode to the structure offset. Lets see a simple example of [R]SI-relative addressing

[0x000052f0]> pd 1

0x000052f0 mov rax, qword [rsi + 8] ; [0x8:8]=0

Here 8 - is some offset in the memory, where rsi probably holds some structure pointer. Imagine that we have the following structures

[0x000052f0]> "td struct ms { char b[8]; int member1; int member2; };"

[0x000052f0]> "td struct ms1 { uint64_t a; int member1; };"

[0x000052f0]> "td struct ms2 { uint16_t a; int64_t b; int member1; };"

Now we need to set the proper structure member offset instead of 8 in this instruction. At first, we need to list available types matching this offset:

[0x000052f0]> ahts 8

ms.member1

ms1.member1

Note, that ms2 is not listed, because it has no members with offset 8. After listing available options we can link it to the chosen offset at the current address:

[0x000052f0]> aht ms1.member1

[0x000052f0]> pd 1

0x000052f0 488b4608 mov rax, qword [rsi + ms1.member1] ; [0x8:8]=0

   • Printing all fields in enum using te command

[0x00000000]> "td enum Foo {COW=1,BAR=2};"

[0x00000000]> te Foo

COW = 0x1

BAR = 0x2

   • Finding matching enum member for given bitfield and vice-versa

[0x00000000]> te Foo 0x1

COW

[0x00000000]> teb Foo COW

0x1

To see the internal representation of the types you can use tk command:

[0x000051c0]> tk~S1

S1=struct

struct.S1=x,y,z

struct.S1.x=int32_t,0,3

struct.S1.x.meta=4

struct.S1.y=int32_t,12,4

struct.S1.y.meta=4

struct.S1.z=int32_t,28,0

struct.S1.z.meta=0

[0x000051c0]>

Defining primitive types requires an understanding of basic pf formats, you can find the whole list of format specifier in pf??:

-----------------------------------------------------

| format | explanation |

|---------------------------------------------------|

| b | byte (unsigned) |

| c | char (signed byte) |

| d | 0x%%08x hexadecimal value (4 bytes) |

| f | float value (4 bytes) |

| i | %%i integer value (4 bytes) |

| o | 0x%%08o octal value (4 byte) |

| p | pointer reference (2, 4 or 8 bytes) |

| q | quadword (8 bytes) |

| 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 | 0x%%08x hex value and flag (fd @ addr) |

| X | show formatted hexpairs |

| z | \0 terminated string |

| Z | \0 terminated wide string |

-----------------------------------------------------

there are basically 3 mandatory keys for defining basic data types: X=type type.X=format_specifier type.X.size=size_in_bits For example, let's define UNIT, according to Microsoft documentation UINT is just equivalent of standard C unsigned int (or uint32_t in terms of TCC engine). It will be defined as:

UINT=type

type.UINT=d

type.UINT.size=32

Now there is an optional entry:

X.type.pointto=Y

This one may only be used in case of pointer type.X=p, one good example is LPFILETIME definition, it is a pointer to _FILETIME which happens to be a structure. Assuming that we are targeting only 32-bit windows machine, it will be defined as the following:

LPFILETIME=type

type.LPFILETIME=p

type.LPFILETIME.size=32

type.LPFILETIME.pointto=_FILETIME

This last field is not mandatory because sometimes the data structure internals will be proprietary, and we will not have a clean representation for it.

There is also one more optional entry:

type.UINT.meta=4

This entry is for integration with C parser and carries the type class information: integer size, signed/unsigned, etc.

Those are the basic keys for structs (with just two elements):

X=struct

struct.X=a,b

struct.X.a=a_type,a_offset,a_number_of_elements

struct.X.b=b_type,b_offset,b_number_of_elements

The first line is used to define a structure called X, the second line defines the elements of X as comma separated values. After that, we just define each element info.

For example. we can have a struct like this one:

struct _FILETIME {

DWORD dwLowDateTime;

DWORD dwHighDateTime;

}

assuming we have DWORD defined, the struct will look like this

_FILETIME=struct

struct._FILETIME=dwLowDateTime,dwHighDateTime

struct._FILETIME.dwLowDateTime=DWORD,0,0

struct._FILETIME.dwHighDateTime=DWORD,4,0

Note that the number of elements field is used in case of arrays only to identify how many elements are in arrays, other than that it is zero by default.

Unions are defined exactly like structs the only difference is that you will replace the word struct with the word union.

Function prototypes representation is the most detail oriented and the most important one of them all. Actually, this is the one used directly for type matching

X=func

func.X.args=NumberOfArgs