Rizin
unix-like reverse engineering framework and cli tools
vtable.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
2 // SPDX-FileCopyrightText: 2009-2020 maijin <maijin21@gmail.com>
3 // SPDX-FileCopyrightText: 2009-2020 thestr4ng3r <info@florianmaerkl.de>
4 // SPDX-License-Identifier: LGPL-3.0-only
5 
6 #include "rz_util.h"
7 #include "rz_analysis.h"
8 
9 #define VTABLE_BUFF_SIZE 10
10 
11 #define VTABLE_READ_ADDR_FUNC(fname, read_fname, sz) \
12  static bool fname(RzAnalysis *analysis, ut64 addr, ut64 *buf) { \
13  ut8 tmp[sz]; \
14  if (!analysis->iob.read_at(analysis->iob.io, addr, tmp, sz)) { \
15  return false; \
16  } \
17  *buf = read_fname(tmp); \
18  return true; \
19  }
20 VTABLE_READ_ADDR_FUNC(vtable_read_addr_le8, rz_read_le8, 1)
21 VTABLE_READ_ADDR_FUNC(vtable_read_addr_le16, rz_read_le16, 2)
22 VTABLE_READ_ADDR_FUNC(vtable_read_addr_le32, rz_read_le32, 4)
23 VTABLE_READ_ADDR_FUNC(vtable_read_addr_le64, rz_read_le64, 8)
24 VTABLE_READ_ADDR_FUNC(vtable_read_addr_be8, rz_read_be8, 1)
25 VTABLE_READ_ADDR_FUNC(vtable_read_addr_be16, rz_read_be16, 2)
26 VTABLE_READ_ADDR_FUNC(vtable_read_addr_be32, rz_read_be32, 4)
27 VTABLE_READ_ADDR_FUNC(vtable_read_addr_be64, rz_read_be64, 8)
28 
29 RZ_API void rz_analysis_vtable_info_free(RVTableInfo *vtable) {
30  if (!vtable) {
31  return;
32  }
33  rz_vector_clear(&vtable->methods);
34  free(vtable);
35 }
36 
37 RZ_API ut64 rz_analysis_vtable_info_get_size(RVTableContext *context, RVTableInfo *vtable) {
38  return (ut64)vtable->methods.len * context->word_size;
39 }
40 
41 RZ_API bool rz_analysis_vtable_begin(RzAnalysis *analysis, RVTableContext *context) {
42  context->analysis = analysis;
43  context->abi = analysis->cpp_abi;
44  context->word_size = (ut8)(analysis->bits / 8);
45  const bool is_arm = analysis->cur->arch && rz_str_startswith(analysis->cur->arch, "arm");
46  if (is_arm && context->word_size < 4) {
47  context->word_size = 4;
48  }
49  switch (context->word_size) {
50  case 1:
51  context->read_addr = analysis->big_endian ? vtable_read_addr_be8 : vtable_read_addr_le8;
52  break;
53  case 2:
54  context->read_addr = analysis->big_endian ? vtable_read_addr_be16 : vtable_read_addr_le16;
55  break;
56  case 4:
57  context->read_addr = analysis->big_endian ? vtable_read_addr_be32 : vtable_read_addr_le32;
58  break;
59  case 8:
60  context->read_addr = analysis->big_endian ? vtable_read_addr_be64 : vtable_read_addr_le64;
61  break;
62  default:
63  return false;
64  }
65  return true;
66 }
67 
68 static bool vtable_addr_in_text_section(RVTableContext *context, ut64 curAddress) {
69  // section of the curAddress
70  RzBinSection *value = context->analysis->binb.get_vsect_at(context->analysis->binb.bin, curAddress);
71  // If the pointed value lies in .text section
72  return value && strstr(value->name, "text") && (value->perm & 1) != 0;
73 }
74 
75 static bool vtable_is_value_in_text_section(RVTableContext *context, ut64 curAddress, ut64 *value) {
76  // value at the current address
77  ut64 curAddressValue;
78  if (!context->read_addr(context->analysis, curAddress, &curAddressValue)) {
79  return false;
80  }
81  // if the value is in text section
82  bool ret = vtable_addr_in_text_section(context, curAddressValue);
83  if (value) {
84  *value = curAddressValue;
85  }
86  return ret;
87 }
88 
90  if (section->is_segment) {
91  return false;
92  }
93  return !strcmp(section->name, ".rodata") ||
94  !strcmp(section->name, ".rdata") ||
95  !strcmp(section->name, ".data.rel.ro") ||
96  !strcmp(section->name, ".data.rel.ro.local") ||
97  rz_str_endswith(section->name, "__const");
98 }
99 
101  if (!section) {
102  return false;
103  }
104  if (section->is_data) {
105  return true;
106  }
107  return !strcmp(section->name, ".data.rel.ro") ||
108  !strcmp(section->name, ".data.rel.ro.local") ||
109  rz_str_endswith(section->name, "__const");
110 }
111 
112 static bool vtable_is_addr_vtable_start_itanium(RVTableContext *context, RzBinSection *section, ut64 curAddress) {
113  ut64 value;
114  if (!curAddress || curAddress == UT64_MAX) {
115  return false;
116  }
117  if (curAddress && !vtable_is_value_in_text_section(context, curAddress, NULL)) { // Vtable beginning referenced from the code
118  return false;
119  }
120  if (!context->read_addr(context->analysis, curAddress - context->word_size, &value)) { // get the RTTI pointer
121  return false;
122  }
123  RzBinSection *rtti_section = context->analysis->binb.get_vsect_at(context->analysis->binb.bin, value);
124  if (value && !section_can_contain_rtti(rtti_section)) { // RTTI ptr must point somewhere in the data section
125  return false;
126  }
127  if (!context->read_addr(context->analysis, curAddress - 2 * context->word_size, &value)) { // Offset to top
128  return false;
129  }
130  if ((st32)value > 0) { // Offset to top has to be negative
131  return false;
132  }
133  return true;
134 }
135 
136 static bool vtable_is_addr_vtable_start_msvc(RVTableContext *context, ut64 curAddress) {
137  RzAnalysisXRef *xref;
138  RzListIter *xrefIter;
139 
140  if (!curAddress || curAddress == UT64_MAX) {
141  return false;
142  }
143  if (curAddress && !vtable_is_value_in_text_section(context, curAddress, NULL)) {
144  return false;
145  }
146  // total xref's to curAddress
147  RzList *xrefs = rz_analysis_xrefs_get_to(context->analysis, curAddress);
148  if (rz_list_empty(xrefs)) {
149  rz_list_free(xrefs);
150  return false;
151  }
152  rz_list_foreach (xrefs, xrefIter, xref) {
153  // section in which currenct xref lies
156  context->analysis->iob.read_at(context->analysis->iob.io, xref->from, buf, sizeof(buf));
157 
158  RzAnalysisOp analop = { 0 };
159  rz_analysis_op(context->analysis, &analop, xref->from, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_BASIC);
160 
162  rz_list_free(xrefs);
164  return true;
165  }
166 
168  }
169  }
170  rz_list_free(xrefs);
171  return false;
172 }
173 
174 static bool vtable_is_addr_vtable_start(RVTableContext *context, RzBinSection *section, ut64 curAddress) {
175  if (context->abi == RZ_ANALYSIS_CPP_ABI_MSVC) {
176  return vtable_is_addr_vtable_start_msvc(context, curAddress);
177  }
178  if (context->abi == RZ_ANALYSIS_CPP_ABI_ITANIUM) {
180  }
182 }
183 
184 RZ_API RVTableInfo *rz_analysis_vtable_parse_at(RVTableContext *context, ut64 addr) {
185  ut64 offset_to_top;
186  if (!context->read_addr(context->analysis, addr - 2 * context->word_size, &offset_to_top)) {
187  return NULL;
188  }
189 
190  RVTableInfo *vtable = calloc(1, sizeof(RVTableInfo));
191  if (!vtable) {
192  return NULL;
193  }
194 
195  vtable->saddr = addr;
196 
197  rz_vector_init(&vtable->methods, sizeof(RVTableMethodInfo), NULL, NULL);
198 
199  RVTableMethodInfo meth;
200  while (vtable_is_value_in_text_section(context, addr, &meth.addr)) {
201  meth.vtable_offset = addr - vtable->saddr;
202  if (!rz_vector_push(&vtable->methods, &meth)) {
203  break;
204  }
205 
206  addr += context->word_size;
207 
208  // a ref means the vtable has ended
209  RzList *ll = rz_analysis_xrefs_get_to(context->analysis, addr);
210  if (!rz_list_empty(ll)) {
211  rz_list_free(ll);
212  break;
213  }
214  rz_list_free(ll);
215  }
216  return vtable;
217 }
218 
219 RZ_API RzList /*<RVTableInfo *>*/ *rz_analysis_vtable_search(RVTableContext *context) {
220  RzAnalysis *analysis = context->analysis;
221  if (!analysis) {
222  return NULL;
223  }
224 
226  if (!vtables) {
227  return NULL;
228  }
229 
230  RzList *sections = analysis->binb.get_sections(analysis->binb.bin);
231  if (!sections) {
232  rz_list_free(vtables);
233  return NULL;
234  }
235 
237 
238  RzListIter *iter;
240  rz_list_foreach (sections, iter, section) {
241  if (rz_cons_is_breaked()) {
242  break;
243  }
244 
246  continue;
247  }
248 
249  ut64 startAddress = section->vaddr;
250  ut64 endAddress = startAddress + (section->vsize) - context->word_size;
251  ut64 ss = endAddress - startAddress;
252  if (ss > ST32_MAX) {
253  break;
254  }
255  while (startAddress <= endAddress) {
256  if (rz_cons_is_breaked()) {
257  break;
258  }
259  if (!analysis->iob.is_valid_offset(analysis->iob.io, startAddress, 0)) {
260  break;
261  }
262 
263  if (vtable_is_addr_vtable_start(context, section, startAddress)) {
264  RVTableInfo *vtable = rz_analysis_vtable_parse_at(context, startAddress);
265  if (vtable) {
266  rz_list_append(vtables, vtable);
268  if (size > 0) {
269  startAddress += size;
270  continue;
271  }
272  }
273  }
274  startAddress += context->word_size;
275  }
276  }
277 
279 
280  if (rz_list_empty(vtables)) {
281  // stripped binary?
282  rz_list_free(vtables);
283  return NULL;
284  }
285  return vtables;
286 }
287 
289  RVTableContext context;
290  rz_analysis_vtable_begin(analysis, &context);
291 
292  const char *noMethodName = "No Name found";
293  RVTableMethodInfo *curMethod;
294  RzListIter *vtableIter;
295  RVTableInfo *table;
296 
298 
299  if (mode == RZ_OUTPUT_MODE_JSON) {
300  PJ *pj = pj_new();
301  if (!pj) {
302  rz_list_free(vtables);
303  return;
304  }
305  pj_a(pj);
306  rz_list_foreach (vtables, vtableIter, table) {
307  pj_o(pj);
308  pj_kN(pj, "offset", table->saddr);
309  pj_ka(pj, "methods");
310  rz_vector_foreach(&table->methods, curMethod) {
311  RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(analysis, curMethod->addr, 0);
312  const char *const name = fcn ? fcn->name : NULL;
313  pj_o(pj);
314  pj_kN(pj, "offset", curMethod->addr);
315  pj_ks(pj, "name", name ? name : noMethodName);
316  pj_end(pj);
317  }
318  pj_end(pj);
319  pj_end(pj);
320  }
321  pj_end(pj);
323  pj_free(pj);
324  } else if (mode == RZ_OUTPUT_MODE_RIZIN) {
325  rz_list_foreach (vtables, vtableIter, table) {
326  rz_cons_printf("f vtable.0x%08" PFMT64x " %" PFMT64d " @ 0x%08" PFMT64x "\n",
327  table->saddr,
329  table->saddr);
330  rz_vector_foreach(&table->methods, curMethod) {
331  rz_cons_printf("Cd %d @ 0x%08" PFMT64x "\n", context.word_size, table->saddr + curMethod->vtable_offset);
332  RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(analysis, curMethod->addr, 0);
333  const char *const name = fcn ? fcn->name : NULL;
334  if (name) {
335  rz_cons_printf("f %s @ 0x%08" PFMT64x "\n", name, curMethod->addr);
336  } else {
337  rz_cons_printf("f method.virtual.0x%08" PFMT64x " @ 0x%08" PFMT64x "\n", curMethod->addr, curMethod->addr);
338  }
339  }
340  }
341  } else {
342  rz_list_foreach (vtables, vtableIter, table) {
343  ut64 vtableStartAddress = table->saddr;
344  rz_cons_printf("\nVtable Found at 0x%08" PFMT64x "\n", vtableStartAddress);
345  rz_vector_foreach(&table->methods, curMethod) {
346  RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(analysis, curMethod->addr, 0);
347  const char *const name = fcn ? fcn->name : NULL;
348  rz_cons_printf("0x%08" PFMT64x " : %s\n", vtableStartAddress, name ? name : noMethodName);
349  vtableStartAddress += context.word_size;
350  }
351  rz_cons_newline();
352  }
353  }
354  rz_list_free(vtables);
355 }
static int analop(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask)
RzList * sections(RzBinFile *bf)
Definition: bin_ne.c:110
static int value
Definition: cmd_api.c:93
RZ_API void rz_cons_newline(void)
Definition: cons.c:1274
RZ_API void rz_cons_break_pop(void)
Definition: cons.c:361
RZ_API void rz_cons_break_push(RzConsBreak cb, void *user)
Definition: cons.c:357
RZ_API int rz_cons_printf(const char *format,...)
Definition: cons.c:1202
RZ_API bool rz_cons_is_breaked(void)
Definition: cons.c:373
RZ_API void rz_cons_println(const char *str)
Definition: cons.c:233
#define RZ_API
#define NULL
Definition: cris-opc.c:27
#define ut8
Definition: dcpu16.h:8
RZ_DEPRECATE RZ_API RzAnalysisFunction * rz_analysis_get_fcn_in(RzAnalysis *analysis, ut64 addr, int type)
Definition: fcn.c:1687
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void uLong size
Definition: ioapi.h:138
const char int mode
Definition: ioapi.h:137
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
void * calloc(size_t number, size_t size)
Definition: malloc.c:102
RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op)
Definition: op.c:37
RZ_API int rz_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask)
Definition: op.c:96
static int is_arm(RzBinPEObj *bin)
Definition: pe_info.c:12
@ RZ_ANALYSIS_CPP_ABI_ITANIUM
Definition: rz_analysis.h:542
@ RZ_ANALYSIS_CPP_ABI_MSVC
Definition: rz_analysis.h:543
@ RZ_ANALYSIS_OP_MASK_BASIC
Definition: rz_analysis.h:440
@ RZ_ANALYSIS_OP_TYPE_MOV
Definition: rz_analysis.h:390
@ RZ_ANALYSIS_OP_TYPE_LEA
Definition: rz_analysis.h:417
#define rz_return_val_if_reached(val)
Definition: rz_assert.h:122
static ut64 rz_read_be64(const void *src)
Definition: rz_endian.h:108
static ut16 rz_read_le16(const void *src)
Definition: rz_endian.h:206
static ut32 rz_read_le32(const void *src)
Definition: rz_endian.h:239
static ut8 rz_read_le8(const void *src)
Definition: rz_endian.h:187
static ut64 rz_read_le64(const void *src)
Definition: rz_endian.h:266
static ut8 rz_read_be8(const void *src)
Definition: rz_endian.h:34
static ut32 rz_read_be32(const void *src)
Definition: rz_endian.h:87
static ut16 rz_read_be16(const void *src)
Definition: rz_endian.h:50
void(* RzListFree)(void *ptr)
Definition: rz_list.h:11
RZ_API PJ * pj_ka(PJ *j, const char *k)
Definition: pj.c:163
RZ_API PJ * pj_new(void)
Definition: pj.c:25
RZ_API PJ * pj_end(PJ *j)
Definition: pj.c:87
RZ_API const char * pj_string(PJ *pj)
Definition: pj.c:57
RZ_API void pj_free(PJ *j)
Definition: pj.c:34
RZ_API PJ * pj_o(PJ *j)
Definition: pj.c:75
RZ_API PJ * pj_ks(PJ *j, const char *k, const char *v)
Definition: pj.c:170
RZ_API PJ * pj_a(PJ *j)
Definition: pj.c:81
RZ_API PJ * pj_kN(PJ *j, const char *k, st64 n)
Definition: pj.c:128
RZ_API bool rz_str_startswith(RZ_NONNULL const char *str, RZ_NONNULL const char *needle)
Checks if a string starts with a specifc sequence of characters (case sensitive)
Definition: str.c:3286
RZ_API bool rz_str_endswith(RZ_NONNULL const char *str, RZ_NONNULL const char *needle)
Checks if a string ends with a specifc sequence of characters (case sensitive)
Definition: str.c:3329
#define PFMT64d
Definition: rz_types.h:394
RzOutputMode
Enum to describe the way data are printed.
Definition: rz_types.h:38
@ RZ_OUTPUT_MODE_JSON
Definition: rz_types.h:40
@ RZ_OUTPUT_MODE_RIZIN
Definition: rz_types.h:41
#define PFMT64x
Definition: rz_types.h:393
#define ST32_MAX
Definition: rz_types_base.h:97
#define UT64_MAX
Definition: rz_types_base.h:86
#define st32
Definition: rz_types_base.h:12
RZ_API void * rz_vector_push(RzVector *vec, void *x)
Definition: vector.c:197
#define rz_vector_foreach(vec, it)
Definition: rz_vector.h:169
RZ_API void rz_vector_clear(RzVector *vec)
Definition: vector.c:68
RZ_API void rz_vector_init(RzVector *vec, size_t elem_size, RzVectorFree free, void *free_user)
Definition: vector.c:33
Definition: z80asm.h:102
Definition: rz_pj.h:12
RzAnalysisCPPABI cpp_abi
Definition: rz_analysis.h:560
struct rz_analysis_plugin_t * cur
Definition: rz_analysis.h:586
RzBinBind binb
Definition: rz_analysis.h:579
RzIOBind iob
Definition: rz_analysis.h:574
RzBin * bin
Definition: rz_bin.h:807
RzBinGetSections get_sections
Definition: rz_bin.h:810
RzIOIsValidOff is_valid_offset
Definition: rz_io.h:257
RzIO * io
Definition: rz_io.h:232
static bool vtable_addr_in_text_section(RVTableContext *context, ut64 curAddress)
Definition: vtable.c:68
RZ_API void rz_analysis_list_vtables(RzAnalysis *analysis, RzOutputMode mode)
Definition: vtable.c:288
static bool section_can_contain_rtti(RzBinSection *section)
Definition: vtable.c:100
static bool vtable_is_addr_vtable_start_msvc(RVTableContext *context, ut64 curAddress)
Definition: vtable.c:136
static bool vtable_section_can_contain_vtables(RzBinSection *section)
Definition: vtable.c:89
static bool vtable_is_addr_vtable_start(RVTableContext *context, RzBinSection *section, ut64 curAddress)
Definition: vtable.c:174
RZ_API ut64 rz_analysis_vtable_info_get_size(RVTableContext *context, RVTableInfo *vtable)
Definition: vtable.c:37
#define VTABLE_BUFF_SIZE
Definition: vtable.c:9
RZ_API void rz_analysis_vtable_info_free(RVTableInfo *vtable)
Definition: vtable.c:29
static bool vtable_is_value_in_text_section(RVTableContext *context, ut64 curAddress, ut64 *value)
Definition: vtable.c:75
static bool vtable_is_addr_vtable_start_itanium(RVTableContext *context, RzBinSection *section, ut64 curAddress)
Definition: vtable.c:112
RZ_API RVTableInfo * rz_analysis_vtable_parse_at(RVTableContext *context, ut64 addr)
Definition: vtable.c:184
#define VTABLE_READ_ADDR_FUNC(fname, read_fname, sz)
Definition: vtable.c:11
RZ_API RzList * rz_analysis_vtable_search(RVTableContext *context)
Definition: vtable.c:219
RZ_API bool rz_analysis_vtable_begin(RzAnalysis *analysis, RVTableContext *context)
Definition: vtable.c:41
if(dbg->bits==RZ_SYS_BITS_64)
Definition: windows-arm64.h:4
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
RZ_API RzList * rz_analysis_xrefs_get_to(RzAnalysis *analysis, ut64 addr)
Definition: xrefs.c:173
static int addr
Definition: z80asm.c:58