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

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.

Structures

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

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

Function prototypes

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

func.x.arg0=Arg_type,arg_name

.

.

.

func.X.ret=Return_type

func.X.cc=calling_convention

It should be self-explanatory. Let's do strncasecmp as an example for x86 arch for Linux machines. According to man pages, strncasecmp is defined as the following:

int strcasecmp(const char *s1, const char *s2, size_t n);

When converting it into its sdb representation it will look like the following:

strcasecmp=func

func.strcasecmp.args=3

func.strcasecmp.arg0=char *,s1

func.strcasecmp.arg1=char *,s2

func.strcasecmp.arg2=size_t,n

func.strcasecmp.ret=int

func.strcasecmp.cc=cdecl

Note that the .cc part is optional and if it didn't exist the default calling-convention for your target architecture will be used instead. There is one extra optional key

func.x.noreturn=true/false

This key is used to mark functions that will not return once called, such as exit and _exit.

Calling Conventions

Radare2 uses calling conventions to help in identifying function formal arguments and return types. It is used also as a guide for basic function prototype and type propagation.

[0x00000000]> afc?

|Usage: afc[agl?]

| afc convention Manually set calling convention for current function

| afc Show Calling convention for the Current function

| afc=([cctype]) Select or show default calling convention

| afcr[j] Show register usage for the current function

| afca Analyse function for finding the current calling convention

| afcf[j] [name] Prints return type function(arg1, arg2...), see afij

| afck List SDB details of call loaded calling conventions

| afcl List all available calling conventions

| afco path Open Calling Convention sdb profile from given path

| afcR Register telescoping using the calling conventions order

[0x00000000]>

   • To list all available calling conventions for current architecture using afcl command

[0x00000000]> afcl

amd64

ms

   • To display function prototype of standard library functions you have afcf command

[0x00000000]> afcf printf

int printf(const char *format)

[0x00000000]> afcf fgets

char *fgets(char *s, int size, FILE *stream)

All this information is loaded via sdb under /libr/anal/d/cc-[arch]-[bits].sdb

default.cc=amd64


ms=cc

cc.ms.name=ms

cc.ms.arg1=rcx

cc.ms.arg2=rdx

cc.ms.arg3=r8

cc.ms.arg3=r9

cc.ms.argn=stack

cc.ms.ret=rax

cc.x.argi=rax is used to set the ith argument of this calling convention to register name rax

cc.x.argn=stack means that all the arguments (or the rest of them in case there was argi for any i as counting number) will be stored in stack from left to right

cc.x.argn=stack_rev same as cc.x.argn=stack except for it means argument are passed right to left

Virtual Tables

There is a basic support of virtual tables parsing (RTTI and others). The most important thing before you start to perform such kind of analysis is to check if the anal.cpp.abi option is set correctly, and change if needed.

All commands to work with virtual tables are located in the av namespace. Currently, the support is very basic, allowing you only to inspect parsed tables.

|Usage: av[?jr*] C++ vtables and RTTI

| av search for vtables in data sections and show results

| avj like av, but as json

| av* like av, but as r2 commands

| avr[j@addr] try to parse RTTI at vtable addr (see anal.cpp.abi)

| avra[j] search for vtables and try to parse RTTI at each of them

The main commands here are av and avr. av lists all virtual tables found when r2 opened the file. If you are not happy with the result you may want to try to parse virtual table at a particular address with avr command. avra performs the search and parsing of all virtual tables in the binary, like r2 does during the file opening.

Syscalls

Radare2 allows manual search for assembly code looking like a syscall operation. For example on ARM platform usually they are represented by the svc instruction, on the others can be a different instructions, e.g. syscall on x86 PC.

[0x0001ece0]> /ad/ svc

...

0x000187c2 # 2: svc 0x76

0x000189ea # 2: svc 0xa9

0x00018a0e # 2: svc 0x82

...

Syscalls detection is driven by asm.os, asm.bits, and asm.arch. Be sure to setup those configuration options accordingly. You can use asl command to check if syscalls' support is set up properly and as you expect. The command lists syscalls supported for your platform.

[0x0001ece0]> asl

...

sd_softdevice_enable = 0x80.16

sd_softdevice_disable = 0x80.17

sd_softdevice_is_enabled = 0x80.18

...

If you setup ESIL stack with aei or aeim, you can use /as command to search the addresses where particular syscalls were found and list them.

[0x0001ece0]>