1 /* radare - LGPL - Copyright 2009-2018 - pancake, maijin, thestr4ng3r */
2
3 #include "r_anal.h"
4
5 #define NAME_BUF_SIZE 64
6 #define BASE_CLASSES_MAX 32
7
8
9 typedef struct rtti_complete_object_locator_t {
10 ut32 signature;
11 ut32 vtable_offset; // offset of the vtable within class
12 ut32 cd_offset; // constructor displacement offset
13 ut32 type_descriptor_addr; // only a relative offset for 64bit
14 ut32 class_descriptor_addr; // only a relative offset for 64bit
15 ut32 object_base; // only for 64bit, see rtti_msvc_read_complete_object_locator()
16 } rtti_complete_object_locator;
17
18
19 typedef struct rtti_class_hierarchy_descriptor_t {
20 ut32 signature;
21 ut32 attributes; // bit 0 set = multiple inheritance, bit 1 set = virtual inheritance
22 ut32 num_base_classes;
23 ut32 base_class_array_addr; // only a relative offset for 64bit
24 } rtti_class_hierarchy_descriptor;
25
26 typedef struct rtti_base_class_descriptor_t {
27 ut32 type_descriptor_addr; // only a relative offset for 64bit
28 ut32 num_contained_bases;
29 struct {
30 st32 mdisp; // member displacement
31 st32 pdisp; // vbtable displacement
32 st32 vdisp; // displacement inside vbtable
33 } where;
34 ut32 attributes;
35 } rtti_base_class_descriptor;
36
37 typedef struct rtti_type_descriptor_t {
38 ut64 vtable_addr;
39 ut64 spare;
40 char *name;
41 } rtti_type_descriptor;
42
rtti_type_descriptor_fini(rtti_type_descriptor * td)43 static void rtti_type_descriptor_fini(rtti_type_descriptor *td) {
44 free (td->name);
45 td->name = NULL;
46 }
47
rtti_msvc_addr(RVTableContext * context,ut64 col_addr,ut64 col_base,ut32 addr)48 static inline ut64 rtti_msvc_addr(RVTableContext *context, ut64 col_addr, ut64 col_base, ut32 addr) {
49 if (context->word_size != 8) {
50 return addr;
51 }
52 return addr + (col_addr - col_base);
53 }
54
rtti_msvc_read_complete_object_locator(RVTableContext * context,ut64 addr,rtti_complete_object_locator * col)55 static bool rtti_msvc_read_complete_object_locator(RVTableContext *context, ut64 addr, rtti_complete_object_locator *col) {
56 if (addr == UT64_MAX) {
57 return false;
58 }
59
60 ut8 buf[6 * sizeof (ut32)];
61 int colSize = 5 * sizeof (ut32);
62 if (context->word_size == 8) {
63 colSize += sizeof(ut32);
64 }
65 if (colSize > sizeof (buf)) {
66 return false;
67 }
68
69 if (!context->anal->iob.read_at (context->anal->iob.io, addr, buf, colSize)) {
70 return false;
71 }
72
73 ut32 (*read_at_32)(const void *src, size_t offset) = context->anal->big_endian ? r_read_at_be32 : r_read_at_le32;
74 col->signature = read_at_32 (buf, 0);
75 col->vtable_offset = read_at_32 (buf, 4);
76 col->cd_offset = read_at_32 (buf, 8);
77
78 int offsetSize = R_MIN (context->word_size, 4);
79 col->type_descriptor_addr = (ut32) r_read_ble (buf + 12, (bool) context->anal->big_endian, offsetSize * 8);
80 col->class_descriptor_addr = (ut32) r_read_ble (buf + 12 + offsetSize, (bool) context->anal->big_endian, offsetSize * 8);
81 if (context->word_size == 8) {
82 // 64bit is special:
83 // Type Descriptor and Class Hierarchy Descriptor addresses are computed
84 // by 32 bit values *(col+12) + *(col+0x14)
85 // and *(col+16) + *(col+0x14) respectively
86 col->object_base = read_at_32 (buf, 20);
87 } else {
88 col->object_base = 0;
89 }
90
91 return true;
92 }
93
rtti_msvc_read_class_hierarchy_descriptor(RVTableContext * context,ut64 addr,rtti_class_hierarchy_descriptor * chd)94 static bool rtti_msvc_read_class_hierarchy_descriptor(RVTableContext *context, ut64 addr, rtti_class_hierarchy_descriptor *chd) {
95 if (addr == UT64_MAX) {
96 return false;
97 }
98
99 ut8 buf[4 * sizeof (ut32)];
100 int chdSize = 3 * sizeof (ut32) + R_MIN (4, context->word_size);
101 if (chdSize > sizeof (buf)) {
102 return false;
103 }
104
105 if (!context->anal->iob.read_at (context->anal->iob.io, addr, buf, chdSize)) {
106 return false;
107 }
108
109 ut32 (*read_at_32)(const void *src, size_t offset) = context->anal->big_endian ? r_read_at_be32 : r_read_at_le32;
110 chd->signature = read_at_32 (buf, 0);
111 chd->attributes = read_at_32 (buf, 4);
112 chd->num_base_classes = read_at_32 (buf, 8);
113 if (context->word_size <= 4) {
114 chd->base_class_array_addr = (ut32) r_read_ble (buf + 12, (bool) context->anal->big_endian, context->word_size * 8);
115 } else {
116 // 64bit is special, like in Complete Object Locator.
117 // Only the offset from the base from Complete Object Locator
118 // is contained in Class Hierarchy Descriptor
119 chd->base_class_array_addr = read_at_32 (buf, 12);
120 }
121 return true;
122 }
123
rtti_msvc_base_class_descriptor_size(RVTableContext * context)124 static ut64 rtti_msvc_base_class_descriptor_size(RVTableContext *context) {
125 return context->word_size + 5 * sizeof (ut32);
126 }
127
rtti_msvc_read_base_class_descriptor(RVTableContext * context,ut64 addr,rtti_base_class_descriptor * bcd)128 static bool rtti_msvc_read_base_class_descriptor(RVTableContext *context, ut64 addr, rtti_base_class_descriptor *bcd) {
129 if (addr == UT64_MAX) {
130 return false;
131 }
132
133 ut8 buf[sizeof (ut64) + 5 * sizeof (ut32)];
134 int bcdSize = (int) rtti_msvc_base_class_descriptor_size (context);
135 if (bcdSize > sizeof (buf)) {
136 return false;
137 }
138
139 if (!context->anal->iob.read_at (context->anal->iob.io, addr, buf, bcdSize)) {
140 return false;
141 }
142
143 ut32 (*read_at_32)(const void *src, size_t offset) = context->anal->big_endian ? r_read_at_be32 : r_read_at_le32;
144 int typeDescriptorAddrSize = R_MIN (context->word_size, 4);
145 bcd->type_descriptor_addr = (ut32) r_read_ble (buf, (bool) context->anal->big_endian, typeDescriptorAddrSize * 8);
146 size_t offset = (size_t) typeDescriptorAddrSize;
147 bcd->num_contained_bases = read_at_32 (buf, offset);
148 bcd->where.mdisp = read_at_32 (buf, offset + sizeof (ut32));
149 bcd->where.pdisp = read_at_32 (buf, offset + 2 * sizeof (ut32));
150 bcd->where.vdisp = read_at_32 (buf, offset + 3 * sizeof (ut32));
151 bcd->attributes = read_at_32 (buf, offset + 4 * sizeof (ut32));
152 return true;
153 }
154
rtti_msvc_read_base_class_array(RVTableContext * context,ut32 num_base_classes,ut64 base,ut32 offset)155 static RList *rtti_msvc_read_base_class_array(RVTableContext *context, ut32 num_base_classes, ut64 base, ut32 offset) {
156 if (base == UT64_MAX || offset == UT32_MAX || num_base_classes == UT32_MAX) {
157 return NULL;
158 }
159
160 RList *ret = r_list_newf (free);
161 if (!ret) {
162 return NULL;
163 }
164
165 ut64 addr = base + offset;
166 ut64 stride = R_MIN (context->word_size, 4);
167
168 if (num_base_classes > BASE_CLASSES_MAX) {
169 if (context->anal->verbose) {
170 eprintf ("WARNING: Length of base class array at 0x%08"PFMT64x" exceeds %d.\n", addr, BASE_CLASSES_MAX);
171 }
172 num_base_classes = BASE_CLASSES_MAX;
173 }
174
175 r_cons_break_push (NULL, NULL);
176 while (num_base_classes > 0) {
177 if (r_cons_is_breaked ()) {
178 break;
179 }
180
181 ut64 bcdAddr;
182 if (context->word_size <= 4) {
183 if (!context->read_addr (context->anal, addr, &bcdAddr)) {
184 break;
185 }
186 if (bcdAddr == UT32_MAX) {
187 break;
188 }
189 } else {
190 // special offset calculation for 64bit
191 ut8 tmp[4] = {0};
192 if (!context->anal->iob.read_at(context->anal->iob.io, addr, tmp, 4)) {
193 r_list_free (ret);
194 return NULL;
195 }
196 ut32 (*read_32)(const void *src) = context->anal->big_endian ? r_read_be32 : r_read_le32;
197 ut32 bcdOffset = read_32 (tmp);
198 if (bcdOffset == UT32_MAX) {
199 break;
200 }
201 bcdAddr = base + bcdOffset;
202 }
203
204 rtti_base_class_descriptor *bcd = malloc (sizeof (rtti_base_class_descriptor));
205 if (!bcd) {
206 break;
207 }
208 if (!rtti_msvc_read_base_class_descriptor (context, bcdAddr, bcd)) {
209 free (bcd);
210 break;
211 }
212 r_list_append (ret, bcd);
213 addr += stride;
214 num_base_classes--;
215 }
216 r_cons_break_pop ();
217
218 if (num_base_classes > 0) {
219 // there was an error in the loop above
220 r_list_free (ret);
221 return NULL;
222 }
223
224 return ret;
225 }
226
rtti_msvc_read_type_descriptor(RVTableContext * context,ut64 addr,rtti_type_descriptor * td)227 static bool rtti_msvc_read_type_descriptor(RVTableContext *context, ut64 addr, rtti_type_descriptor *td) {
228 if (addr == UT64_MAX) {
229 return false;
230 }
231
232 if (!context->read_addr (context->anal, addr, &td->vtable_addr)) {
233 return false;
234 }
235 if (!context->read_addr (context->anal, addr + context->word_size, &td->spare)) {
236 return false;
237 }
238
239 ut64 nameAddr = addr + 2 * context->word_size;
240 ut8 buf[NAME_BUF_SIZE];
241 ut64 bufOffset = 0;
242 size_t nameLen = 0;
243 bool endFound = false;
244 bool endInvalid = false;
245 while (1) {
246 context->anal->iob.read_at (context->anal->iob.io, nameAddr + bufOffset, buf, sizeof (buf));
247 int i;
248 for (i=0; i<sizeof (buf); i++) {
249 if (buf[i] == '\0') {
250 endFound = true;
251 break;
252 }
253 if (buf[i] == 0xff) {
254 endInvalid = true;
255 break;
256 }
257 nameLen++;
258 }
259 if (endFound || endInvalid) {
260 break;
261 }
262 bufOffset += sizeof (buf);
263 }
264
265 if (endInvalid) {
266 return false;
267 }
268
269 td->name = malloc (nameLen + 1);
270 if (!td->name) {
271 return false;
272 }
273
274 if (bufOffset == 0) {
275 memcpy (td->name, buf, nameLen + 1);
276 } else {
277 context->anal->iob.read_at (context->anal->iob.io, nameAddr,
278 (ut8 *)td->name, (int) (nameLen + 1));
279 }
280
281 return true;
282 }
283
rtti_msvc_print_complete_object_locator(rtti_complete_object_locator * col,ut64 addr,const char * prefix)284 static void rtti_msvc_print_complete_object_locator(rtti_complete_object_locator *col, ut64 addr, const char *prefix) {
285 r_cons_printf ("%sComplete Object Locator at 0x%08"PFMT64x":\n"
286 "%s\tsignature: %#x\n"
287 "%s\tvftableOffset: %#x\n"
288 "%s\tcdOffset: %#x\n"
289 "%s\ttypeDescriptorAddr: 0x%08"PFMT32x"\n"
290 "%s\tclassDescriptorAddr: 0x%08"PFMT32x"\n",
291 prefix, addr,
292 prefix, col->signature,
293 prefix, col->vtable_offset,
294 prefix, col->cd_offset,
295 prefix, col->type_descriptor_addr,
296 prefix, col->class_descriptor_addr);
297 r_cons_printf ("%s\tobjectBase: 0x%08"PFMT32x"\n\n",
298 prefix, col->object_base);
299 }
300
rtti_msvc_print_complete_object_locator_json(PJ * pj,rtti_complete_object_locator * col)301 static void rtti_msvc_print_complete_object_locator_json(PJ *pj, rtti_complete_object_locator *col) {
302 pj_o (pj);
303 pj_kn (pj, "signature", col->signature);
304 pj_kn (pj, "vftable_offset", col->vtable_offset);
305 pj_kn (pj, "cd_offset", col->cd_offset);
306 pj_kn (pj, "type_desc_addr", col->type_descriptor_addr);
307 pj_kn (pj, "class_desc_addr", col->class_descriptor_addr);
308 pj_kn (pj, "object_base", col->object_base);
309 pj_end (pj);
310 }
311
rtti_msvc_print_type_descriptor(rtti_type_descriptor * td,ut64 addr,const char * prefix)312 static void rtti_msvc_print_type_descriptor(rtti_type_descriptor *td, ut64 addr, const char *prefix) {
313 r_cons_printf ("%sType Descriptor at 0x%08"PFMT64x":\n"
314 "%s\tvtableAddr: 0x%08"PFMT64x"\n"
315 "%s\tspare: 0x%08"PFMT64x"\n"
316 "%s\tname: %s\n\n",
317 prefix, addr,
318 prefix, td->vtable_addr,
319 prefix, td->spare,
320 prefix, td->name);
321 }
322
rtti_msvc_print_type_descriptor_json(PJ * pj,rtti_type_descriptor * td)323 static void rtti_msvc_print_type_descriptor_json(PJ *pj, rtti_type_descriptor *td) {
324 pj_o (pj);
325 pj_kn (pj, "vtable_addr", td->vtable_addr);
326 pj_kn (pj, "spare", td->spare);
327 pj_ks (pj, "name", td->name);
328 pj_end (pj);
329 }
330
rtti_msvc_print_class_hierarchy_descriptor(rtti_class_hierarchy_descriptor * chd,ut64 addr,const char * prefix)331 static void rtti_msvc_print_class_hierarchy_descriptor(rtti_class_hierarchy_descriptor *chd, ut64 addr, const char *prefix) {
332 r_cons_printf ("%sClass Hierarchy Descriptor at 0x%08"PFMT64x":\n"
333 "%s\tsignature: %#x\n"
334 "%s\tattributes: %#x\n"
335 "%s\tnumBaseClasses: %#x\n"
336 "%s\tbaseClassArrayAddr: 0x%08"PFMT32x"\n\n",
337 prefix, addr,
338 prefix, chd->signature,
339 prefix, chd->attributes,
340 prefix, chd->num_base_classes,
341 prefix, chd->base_class_array_addr);
342 }
343
rtti_msvc_print_class_hierarchy_descriptor_json(PJ * pj,rtti_class_hierarchy_descriptor * chd)344 static void rtti_msvc_print_class_hierarchy_descriptor_json(PJ *pj, rtti_class_hierarchy_descriptor *chd) {
345 pj_o (pj);
346 pj_kn (pj, "signature", chd->signature);
347 pj_kn (pj, "attributes", chd->attributes);
348 pj_kn (pj, "num_base_classes", chd->num_base_classes);
349 pj_kn (pj, "base_class_array_addr", chd->base_class_array_addr);
350 pj_end (pj);
351 }
352
rtti_msvc_print_base_class_descriptor(rtti_base_class_descriptor * bcd,const char * prefix)353 static void rtti_msvc_print_base_class_descriptor(rtti_base_class_descriptor *bcd, const char *prefix) {
354 r_cons_printf ("%sBase Class Descriptor:\n"
355 "%s\ttypeDescriptorAddr: 0x%08"PFMT32x"\n"
356 "%s\tnumContainedBases: %#x\n"
357 "%s\twhere:\n"
358 "%s\t\tmdisp: %d\n"
359 "%s\t\tpdisp: %d\n"
360 "%s\t\tvdisp: %d\n"
361 "%s\tattributes: %#x\n\n",
362 prefix,
363 prefix, bcd->type_descriptor_addr,
364 prefix, bcd->num_contained_bases,
365 prefix,
366 prefix, bcd->where.mdisp,
367 prefix, bcd->where.pdisp,
368 prefix, bcd->where.vdisp,
369 prefix, bcd->attributes);
370 }
371
rtti_msvc_print_base_class_descriptor_json(PJ * pj,rtti_base_class_descriptor * bcd)372 static void rtti_msvc_print_base_class_descriptor_json(PJ *pj, rtti_base_class_descriptor *bcd) {
373 pj_o (pj);
374 pj_kn (pj, "type_desc_addr", bcd->type_descriptor_addr);
375 pj_kn (pj, "num_contained_bases", bcd->num_contained_bases);
376 pj_ko (pj, "where");
377 pj_ki (pj, "mdisp", bcd->where.mdisp);
378 pj_ki (pj, "pdisp", bcd->where.pdisp);
379 pj_ki (pj, "vdisp", bcd->where.vdisp);
380 pj_end (pj);
381 pj_kn (pj, "attributes", bcd->attributes);
382 pj_end (pj);
383 }
384
385
386 /**
387 * Demangle a class name as found in MSVC RTTI type descriptors.
388 *
389 * Examples:
390 * .?AVClassA@@
391 * => ClassA
392 * .?AVClassInInnerNamespace@InnerNamespace@OuterNamespace@@
393 * => OuterNamespace::InnerNamespace::AVClassInInnerNamespace
394 */
r_anal_rtti_msvc_demangle_class_name(RVTableContext * context,const char * name)395 R_API char *r_anal_rtti_msvc_demangle_class_name(RVTableContext *context, const char *name) {
396 if (!name) {
397 return NULL;
398 }
399 size_t original_len = strlen (name);
400 if (original_len < 7
401 || (strncmp (name, ".?AV", 4) != 0 && strncmp (name, ".?AU", 4) != 0)
402 || strncmp (name + original_len - 2, "@@", 2) != 0) {
403 return NULL;
404 }
405 char *ret = context->anal->binb.demangle (NULL, "msvc", name, 0, false);
406 if (ret && *ret) {
407 char *n = strchr (ret, ' ');
408 if (n && *(++n)) {
409 char *tmp = strdup (n);
410 free (ret);
411 ret = tmp;
412 } else {
413 R_FREE (ret);
414 }
415 } else {
416 R_FREE (ret);
417 }
418 return ret;
419 }
420
r_anal_rtti_msvc_print_complete_object_locator(RVTableContext * context,ut64 addr,int mode)421 R_API void r_anal_rtti_msvc_print_complete_object_locator(RVTableContext *context, ut64 addr, int mode) {
422 rtti_complete_object_locator col;
423 if (!rtti_msvc_read_complete_object_locator (context, addr, &col)) {
424 eprintf ("Failed to parse Complete Object Locator at 0x%08"PFMT64x"\n", addr);
425 return;
426 }
427
428 if (mode == 'j') {
429 PJ *pj = pj_new ();
430 if (!pj) {
431 return;
432 }
433 rtti_msvc_print_complete_object_locator_json (pj, &col);
434 r_cons_print (pj_string (pj));
435 pj_free (pj);
436 } else {
437 rtti_msvc_print_complete_object_locator (&col, addr, "");
438 }
439 }
440
r_anal_rtti_msvc_print_type_descriptor(RVTableContext * context,ut64 addr,int mode)441 R_API void r_anal_rtti_msvc_print_type_descriptor(RVTableContext *context, ut64 addr, int mode) {
442 rtti_type_descriptor td = { 0 };
443 if (!rtti_msvc_read_type_descriptor (context, addr, &td)) {
444 eprintf ("Failed to parse Type Descriptor at 0x%08"PFMT64x"\n", addr);
445 return;
446 }
447
448 if (mode == 'j') {
449 PJ *pj = pj_new ();
450 if (!pj) {
451 return;
452 }
453 rtti_msvc_print_type_descriptor_json (pj, &td);
454 r_cons_print (pj_string (pj));
455 pj_free (pj);
456 } else {
457 rtti_msvc_print_type_descriptor (&td, addr, "");
458 }
459
460 rtti_type_descriptor_fini (&td);
461 }
462
r_anal_rtti_msvc_print_class_hierarchy_descriptor(RVTableContext * context,ut64 addr,int mode)463 R_API void r_anal_rtti_msvc_print_class_hierarchy_descriptor(RVTableContext *context, ut64 addr, int mode) {
464 rtti_class_hierarchy_descriptor chd;
465 if (!rtti_msvc_read_class_hierarchy_descriptor (context, addr, &chd)) {
466 eprintf ("Failed to parse Class Hierarchy Descriptor at 0x%08"PFMT64x"\n", addr);
467 return;
468 }
469
470 if (mode == 'j') {
471 PJ *pj = pj_new ();
472 if (!pj) {
473 return;
474 }
475 rtti_msvc_print_class_hierarchy_descriptor_json (pj, &chd);
476 r_cons_print (pj_string (pj));
477 pj_free (pj);
478 } else {
479 rtti_msvc_print_class_hierarchy_descriptor (&chd, addr, "");
480 }
481 }
482
r_anal_rtti_msvc_print_base_class_descriptor(RVTableContext * context,ut64 addr,int mode)483 R_API void r_anal_rtti_msvc_print_base_class_descriptor(RVTableContext *context, ut64 addr, int mode) {
484 rtti_base_class_descriptor bcd;
485 if (!rtti_msvc_read_base_class_descriptor (context, addr, &bcd)) {
486 eprintf ("Failed to parse Base Class Descriptor at 0x%08"PFMT64x"\n", addr);
487 return;
488 }
489
490 if (mode == 'j') {
491 PJ *pj = pj_new ();
492 if (!pj) {
493 return;
494 }
495 rtti_msvc_print_base_class_descriptor_json (pj, &bcd);
496 r_cons_print (pj_string (pj));
497 pj_free (pj);
498 } else {
499 rtti_msvc_print_base_class_descriptor (&bcd, "");
500 }
501 }
502
rtti_msvc_print_complete_object_locator_recurse(RVTableContext * context,ut64 atAddress,int mode,bool strict)503 static bool rtti_msvc_print_complete_object_locator_recurse(RVTableContext *context, ut64 atAddress, int mode, bool strict) {
504 bool use_json = mode == 'j';
505 PJ *pj = NULL;
506
507 ut64 colRefAddr = atAddress - context->word_size;
508 ut64 colAddr;
509 if (!context->read_addr (context->anal, colRefAddr, &colAddr)) {
510 return false;
511 }
512
513 // complete object locator
514 rtti_complete_object_locator col;
515 if (!rtti_msvc_read_complete_object_locator (context, colAddr, &col)) {
516 if (!strict) {
517 eprintf ("Failed to parse Complete Object Locator at 0x%08"PFMT64x" (referenced from 0x%08"PFMT64x")\n", colAddr, colRefAddr);
518 }
519 return false;
520 }
521
522 // type descriptor
523 ut64 typeDescriptorAddr = rtti_msvc_addr (context, colAddr, col.object_base, col.type_descriptor_addr);
524 rtti_type_descriptor td = { 0 };
525 if (!rtti_msvc_read_type_descriptor (context, typeDescriptorAddr, &td)) {
526 if (!strict) {
527 eprintf ("Failed to parse Type Descriptor at 0x%08"PFMT64x"\n", typeDescriptorAddr);
528 }
529 return false;
530 }
531
532 // class hierarchy descriptor
533 ut64 classHierarchyDescriptorAddr = rtti_msvc_addr (context, colAddr, col.object_base, col.class_descriptor_addr);
534 rtti_class_hierarchy_descriptor chd;
535 if (!rtti_msvc_read_class_hierarchy_descriptor (context, classHierarchyDescriptorAddr, &chd)) {
536 if (!strict) {
537 eprintf ("Failed to parse Class Hierarchy Descriptor at 0x%08"PFMT64x"\n", classHierarchyDescriptorAddr);
538 }
539 rtti_type_descriptor_fini (&td);
540 return false;
541 }
542
543 ut64 base = chd.base_class_array_addr;
544 ut32 baseClassArrayOffset = 0;
545 if (context->word_size == 8) {
546 base = colAddr - col.object_base;
547 baseClassArrayOffset = chd.base_class_array_addr;
548 }
549
550 RList *baseClassArray = rtti_msvc_read_base_class_array (context, chd.num_base_classes, base, baseClassArrayOffset);
551 if (!baseClassArray) {
552 if (!strict) {
553 eprintf ("Failed to parse Base Class Array starting at 0x%08"PFMT64x"\n", base + baseClassArrayOffset);
554 }
555 rtti_type_descriptor_fini (&td);
556 return false;
557 }
558
559
560 // print
561 if (use_json) {
562 pj = pj_new ();
563 if (!pj) {
564 return false;
565 }
566 pj_o (pj);
567 pj_k (pj, "complete_object_locator");
568 rtti_msvc_print_complete_object_locator_json (pj, &col);
569 pj_k (pj, "type_desc");
570 rtti_msvc_print_type_descriptor_json (pj, &td);
571 pj_k (pj, "class_hierarchy_desc");
572 rtti_msvc_print_class_hierarchy_descriptor_json (pj, &chd);
573 pj_ka (pj, "base_classes");
574 } else {
575 rtti_msvc_print_complete_object_locator (&col, colAddr, "");
576 rtti_msvc_print_type_descriptor (&td, typeDescriptorAddr, "\t");
577 rtti_msvc_print_class_hierarchy_descriptor (&chd, classHierarchyDescriptorAddr, "\t");
578 }
579
580
581 // base classes
582 RListIter *bcdIter;
583 rtti_base_class_descriptor *bcd;
584 r_list_foreach (baseClassArray, bcdIter, bcd) {
585 if (use_json) {
586 pj_o (pj);
587 pj_k (pj, "desc");
588 rtti_msvc_print_base_class_descriptor_json (pj, bcd);
589 } else {
590 rtti_msvc_print_base_class_descriptor (bcd, "\t\t");
591 }
592
593 ut64 baseTypeDescriptorAddr = rtti_msvc_addr (context, colAddr, col.object_base, bcd->type_descriptor_addr);
594 rtti_type_descriptor btd = { 0 };
595 if (rtti_msvc_read_type_descriptor (context, baseTypeDescriptorAddr, &btd)) {
596 if (use_json) {
597 pj_k (pj, "type_desc");
598 rtti_msvc_print_type_descriptor_json (pj, &btd);
599 } else {
600 rtti_msvc_print_type_descriptor (&btd, baseTypeDescriptorAddr, "\t\t\t");
601 }
602 rtti_type_descriptor_fini (&btd);
603 } else {
604 if (!strict) {
605 eprintf ("Failed to parse Type Descriptor at 0x%08"PFMT64x"\n", baseTypeDescriptorAddr);
606 }
607 }
608
609 if (use_json) {
610 pj_end (pj);
611 }
612 }
613 if (use_json) {
614 pj_end (pj);
615 pj_end (pj);
616 r_cons_print (pj_string (pj));
617 pj_free (pj);
618 }
619
620 rtti_type_descriptor_fini (&td);
621 return true;
622 }
623
r_anal_rtti_msvc_print_at_vtable(RVTableContext * context,ut64 addr,int mode,bool strict)624 R_API bool r_anal_rtti_msvc_print_at_vtable(RVTableContext *context, ut64 addr, int mode, bool strict) {
625 return rtti_msvc_print_complete_object_locator_recurse (context, addr, mode, strict);
626 }
627
628 typedef struct recovery_type_descriptor_t RecoveryTypeDescriptor;
629
630 typedef struct recovery_base_descriptor_t {
631 rtti_base_class_descriptor *bcd;
632 RecoveryTypeDescriptor *td;
633 } RecoveryBaseDescriptor;
634
635 typedef struct recovery_complete_object_locator_t {
636 ut64 addr;
637 bool valid;
638 RVTableInfo *vtable;
639 rtti_complete_object_locator col;
640 RecoveryTypeDescriptor *td;
641 rtti_class_hierarchy_descriptor chd;
642 RList *bcd; // <rtti_base_class_descriptor>
643 RVector base_td; // <RecoveryBaseDescriptor>
644 } RecoveryCompleteObjectLocator;
645
recovery_complete_object_locator_new()646 RecoveryCompleteObjectLocator *recovery_complete_object_locator_new() {
647 RecoveryCompleteObjectLocator *col = R_NEW0 (RecoveryCompleteObjectLocator);
648 if (!col) {
649 return NULL;
650 }
651 r_vector_init (&col->base_td, sizeof(RecoveryBaseDescriptor), NULL, NULL);
652 return col;
653 }
654
recovery_complete_object_locator_free(RecoveryCompleteObjectLocator * col)655 void recovery_complete_object_locator_free(RecoveryCompleteObjectLocator *col) {
656 if (!col) {
657 return;
658 }
659 r_list_free (col->bcd);
660 r_vector_clear (&col->base_td);
661 free (col);
662 }
663
664
665 struct recovery_type_descriptor_t {
666 ut64 addr;
667 bool valid;
668 rtti_type_descriptor td;
669 RecoveryCompleteObjectLocator *col;
670 };
671
recovery_type_descriptor_new()672 RecoveryTypeDescriptor *recovery_type_descriptor_new() {
673 RecoveryTypeDescriptor *td = R_NEW (RecoveryTypeDescriptor);
674 if (!td) {
675 return NULL;
676 }
677
678 td->addr = 0;
679 td->valid = false;
680 memset (&td->td, 0, sizeof (td->td));
681 td->col = NULL;
682 //td->vtable = NULL;
683 return td;
684 }
685
recovery_type_descriptor_free(RecoveryTypeDescriptor * td)686 void recovery_type_descriptor_free(RecoveryTypeDescriptor *td) {
687 if (!td) {
688 return;
689 }
690 rtti_type_descriptor_fini (&td->td);
691 free (td);
692 }
693
694
695 typedef struct rtti_msvc_anal_context_t {
696 RVTableContext *vt_context;
697 RPVector vtables; // <RVTableInfo>
698 RPVector complete_object_locators; // <RecoveryCompleteObjectLocator>
699 HtUP *addr_col; // <ut64, RecoveryCompleteObjectLocator *>
700 RPVector type_descriptors; // <RecoveryTypeDescriptor>
701 HtUP *addr_td; // <ut64, RecoveryTypeDescriptor *>
702 HtUP *col_td_classes; // <ut64, char *> contains already recovered classes for col (or td) addresses
703 } RRTTIMSVCAnalContext;
704
705
706 RecoveryTypeDescriptor *recovery_anal_type_descriptor(RRTTIMSVCAnalContext *context, ut64 addr, RecoveryCompleteObjectLocator *col);
707
recovery_anal_complete_object_locator(RRTTIMSVCAnalContext * context,ut64 addr,RVTableInfo * vtable)708 RecoveryCompleteObjectLocator *recovery_anal_complete_object_locator(RRTTIMSVCAnalContext *context, ut64 addr, RVTableInfo *vtable) {
709 RecoveryCompleteObjectLocator *col = ht_up_find (context->addr_col, addr, NULL);
710 if (col) {
711 return col;
712 }
713
714 col = recovery_complete_object_locator_new ();
715 if (!col) {
716 return NULL;
717 }
718 r_pvector_push (&context->complete_object_locators, col);
719 ht_up_insert (context->addr_col, addr, col);
720 col->addr = addr;
721 col->valid = rtti_msvc_read_complete_object_locator (context->vt_context, addr, &col->col);
722 if (!col->valid) {
723 return col;
724 }
725 col->vtable = vtable;
726
727
728 ut64 td_addr = rtti_msvc_addr (context->vt_context, col->addr, col->col.object_base, col->col.type_descriptor_addr);
729 col->td = recovery_anal_type_descriptor (context, td_addr, col);
730 if (!col->td->valid) {
731 col->valid = false;
732 return col;
733 }
734 col->td->col = col;
735
736
737 ut64 chd_addr = rtti_msvc_addr (context->vt_context, col->addr, col->col.object_base, col->col.class_descriptor_addr);
738 col->valid &= rtti_msvc_read_class_hierarchy_descriptor (context->vt_context, chd_addr, &col->chd);
739 if (!col->valid) {
740 return col;
741 }
742
743
744 ut64 base = col->chd.base_class_array_addr;
745 ut32 baseClassArrayOffset = 0;
746 if (context->vt_context->word_size == 8) {
747 base = col->addr - col->col.object_base;
748 baseClassArrayOffset = col->chd.base_class_array_addr;
749 }
750
751 col->bcd = rtti_msvc_read_base_class_array (context->vt_context, col->chd.num_base_classes, base, baseClassArrayOffset);
752 if (!col->bcd) {
753 col->valid = false;
754 return col;
755 }
756
757
758 r_vector_reserve (&col->base_td, (size_t)col->bcd->length);
759 RListIter *bcdIter;
760 rtti_base_class_descriptor *bcd;
761 r_list_foreach (col->bcd, bcdIter, bcd) {
762 ut64 base_td_addr = rtti_msvc_addr (context->vt_context, col->addr, col->col.object_base, bcd->type_descriptor_addr);
763 RecoveryTypeDescriptor *td = recovery_anal_type_descriptor (context, base_td_addr, NULL);
764 if (td == col->td) {
765 continue;
766 }
767 if (!td->valid) {
768 if (context->vt_context->anal->verbose) {
769 eprintf ("Warning: type descriptor of base is invalid.\n");
770 }
771 continue;
772 }
773 RecoveryBaseDescriptor *base_desc = r_vector_push (&col->base_td, NULL);
774 base_desc->bcd = bcd;
775 base_desc->td = td;
776 }
777
778 return col;
779 }
780
recovery_anal_type_descriptor(RRTTIMSVCAnalContext * context,ut64 addr,RecoveryCompleteObjectLocator * col)781 RecoveryTypeDescriptor *recovery_anal_type_descriptor(RRTTIMSVCAnalContext *context, ut64 addr, RecoveryCompleteObjectLocator *col) {
782 RecoveryTypeDescriptor *td = ht_up_find (context->addr_td, addr, NULL);
783 if (td) {
784 if (col != NULL) {
785 td->col = col;
786 }
787 return td;
788 }
789
790 td = recovery_type_descriptor_new ();
791 if (!td) {
792 return NULL;
793 }
794 r_pvector_push (&context->type_descriptors, td);
795 ht_up_insert (context->addr_td, addr, td);
796 td->addr = addr;
797 td->valid = rtti_msvc_read_type_descriptor (context->vt_context, addr, &td->td);
798 if (!td->valid) {
799 return td;
800 }
801
802 td->col = col;
803
804 return td;
805 }
806
unique_class_name(RAnal * anal,const char * original_name)807 static char *unique_class_name(RAnal *anal, const char *original_name) {
808 if (!r_anal_class_exists (anal, original_name)) {
809 return strdup (original_name);
810 }
811
812 char *name = NULL;
813 if (anal->verbose) {
814 eprintf ("Warning: class name %s already taken!\n", original_name);
815 }
816 int i = 1;
817
818 do {
819 free (name);
820 name = r_str_newf ("%s.%d", original_name, i++);
821 if (!name) {
822 return NULL;
823 }
824 } while (r_anal_class_exists (anal, name));
825
826 return name;
827 }
828
recovery_apply_vtable(RAnal * anal,const char * class_name,RVTableInfo * vtable_info)829 static void recovery_apply_vtable(RAnal *anal, const char *class_name, RVTableInfo *vtable_info) {
830 if (!vtable_info) {
831 return;
832 }
833
834 RAnalVTable vtable = {0};
835 vtable.addr = vtable_info->saddr;
836 r_anal_class_vtable_set (anal, class_name, &vtable);
837 r_anal_class_vtable_fini (&vtable);
838
839 RVTableMethodInfo *vmeth;
840 r_vector_foreach (&vtable_info->methods, vmeth) {
841 RAnalMethod meth;
842 meth.addr = vmeth->addr;
843 meth.vtable_offset = vmeth->vtable_offset;
844 meth.name = r_str_newf ("virtual_%" PFMT64d, meth.vtable_offset);
845 r_anal_class_method_set (anal, class_name, &meth);
846 r_anal_class_method_fini (&meth);
847 }
848 }
849
850 static const char *recovery_apply_complete_object_locator(RRTTIMSVCAnalContext *context, RecoveryCompleteObjectLocator *col);
851 static const char *recovery_apply_type_descriptor(RRTTIMSVCAnalContext *context, RecoveryTypeDescriptor *td);
852
recovery_apply_bases(RRTTIMSVCAnalContext * context,const char * class_name,RVector * base_descs)853 static void recovery_apply_bases(RRTTIMSVCAnalContext *context, const char *class_name, RVector *base_descs) {
854 RecoveryBaseDescriptor *base_desc;
855 r_vector_foreach (base_descs, base_desc) {
856 RecoveryTypeDescriptor *base_td = base_desc->td;
857 if (!base_td->valid) {
858 eprintf ("Warning Base td is invalid!\n");
859 continue;
860 }
861
862 const char *base_class_name;
863 if (!base_td->col) {
864 if (context->vt_context->anal->verbose) {
865 eprintf ("Warning: Base td %s has no col. Falling back to recovery from td only.\n", base_td->td.name);
866 }
867 base_class_name = recovery_apply_type_descriptor (context, base_td);
868 } else {
869 base_class_name = recovery_apply_complete_object_locator (context, base_td->col);
870 }
871
872 if (!base_class_name) {
873 if (context->vt_context->anal->verbose) {
874 eprintf ("Failed to convert !base td->col or td to a class\n");
875 }
876 continue;
877 }
878
879 RAnalBaseClass base;
880 base.id = NULL;
881 base.offset = (ut64)base_desc->bcd->where.mdisp;
882 base.class_name = strdup (base_class_name);
883 r_anal_class_base_set (context->vt_context->anal, class_name, &base);
884 r_anal_class_base_fini (&base);
885 }
886 }
887
888
recovery_apply_complete_object_locator(RRTTIMSVCAnalContext * context,RecoveryCompleteObjectLocator * col)889 static const char *recovery_apply_complete_object_locator(RRTTIMSVCAnalContext *context, RecoveryCompleteObjectLocator *col) {
890 if (!col->valid) {
891 return NULL;
892 }
893
894 if (!col->td) {
895 if (context->vt_context->anal->verbose) {
896 eprintf ("Warning: no td for col at 0x%"PFMT64x"\n", col->addr);
897 }
898 return NULL;
899 }
900
901 RAnal *anal = context->vt_context->anal;
902
903 const char *existing = ht_up_find (context->col_td_classes, col->addr, NULL);
904 if (existing != NULL) {
905 return existing;
906 }
907
908 char *name = r_anal_rtti_msvc_demangle_class_name (context->vt_context, col->td->td.name);
909 if (!name) {
910 if (context->vt_context->anal->verbose) {
911 eprintf ("Failed to demangle a class name: \"%s\"\n", col->td->td.name);
912 }
913 name = strdup (col->td->td.name);
914 if (!name) {
915 return NULL;
916 }
917 }
918
919 char *tmp = name;
920 name = unique_class_name (anal, name);
921 free (tmp);
922 if (!name) {
923 return NULL;
924 }
925
926 r_anal_class_create (anal, name);
927 ht_up_insert (context->col_td_classes, col->addr, name);
928
929 recovery_apply_vtable (anal, name, col->vtable);
930 recovery_apply_bases (context, name, &col->base_td);
931
932 return name;
933 }
934
935
936
recovery_apply_type_descriptor(RRTTIMSVCAnalContext * context,RecoveryTypeDescriptor * td)937 static const char *recovery_apply_type_descriptor(RRTTIMSVCAnalContext *context, RecoveryTypeDescriptor *td) {
938 if (!td->valid) {
939 return NULL;
940 }
941
942 RAnal *anal = context->vt_context->anal;
943
944 const char *existing = ht_up_find (context->col_td_classes, td->addr, NULL);
945 if (existing != NULL) {
946 return existing;
947 }
948
949 char *name = r_anal_rtti_msvc_demangle_class_name (context->vt_context, td->td.name);
950 if (!name) {
951 if (context->vt_context->anal->verbose) {
952 eprintf("Failed to demangle a class name: \"%s\"\n", td->td.name);
953 }
954 name = strdup (td->td.name);
955 if (!name) {
956 return NULL;
957 }
958 }
959
960 r_anal_class_create (anal, name);
961 ht_up_insert (context->col_td_classes, td->addr, name);
962
963 if (!td->col || !td->col->valid) {
964 return name;
965 }
966
967 recovery_apply_vtable (anal, name, td->col->vtable);
968 recovery_apply_bases (context, name, &td->col->base_td);
969
970 return name;
971 }
972
str_value_free(HtUPKv * kv)973 void str_value_free(HtUPKv *kv) {
974 free (kv->value);
975 }
976
r_anal_rtti_msvc_recover_all(RVTableContext * vt_context,RList * vtables)977 R_API void r_anal_rtti_msvc_recover_all(RVTableContext *vt_context, RList *vtables) {
978 RRTTIMSVCAnalContext context;
979 context.vt_context = vt_context;
980 r_pvector_init (&context.vtables, (RPVectorFree)r_anal_vtable_info_free);
981
982 r_pvector_init (&context.complete_object_locators, (RPVectorFree) recovery_complete_object_locator_free);
983 context.addr_col = ht_up_new0 ();
984 r_pvector_init (&context.type_descriptors, (RPVectorFree) recovery_type_descriptor_free);
985 context.addr_td = ht_up_new0 ();
986
987 context.col_td_classes = ht_up_new (NULL, (HtUPKvFreeFunc)str_value_free, (HtUPCalcSizeV)strlen);
988
989 RListIter *vtableIter;
990 RVTableInfo *table;
991 r_list_foreach (vtables, vtableIter, table) {
992 ut64 colRefAddr = table->saddr - vt_context->word_size;
993 ut64 colAddr;
994 if (!vt_context->read_addr (vt_context->anal, colRefAddr, &colAddr)) {
995 continue;
996 }
997 recovery_anal_complete_object_locator (&context, colAddr, table);
998 }
999
1000 void **it;
1001 #if USE_TD_RECOVERY
1002 r_pvector_foreach (&context.type_descriptors, it) {
1003 RecoveryTypeDescriptor *td = *it;
1004 if (!td->valid) {
1005 continue;
1006 }
1007 recovery_apply_type_descriptor (&context, td);
1008 }
1009 #else
1010 r_pvector_foreach (&context.complete_object_locators, it) {
1011 RecoveryCompleteObjectLocator *col = *it;
1012 if (!col->valid) {
1013 continue;
1014 }
1015 recovery_apply_complete_object_locator (&context, col);
1016 }
1017 #endif
1018
1019 r_pvector_clear (&context.vtables);
1020 r_pvector_clear (&context.complete_object_locators);
1021 ht_up_free (context.addr_col);
1022 r_pvector_clear (&context.type_descriptors);
1023 ht_up_free (context.addr_td);
1024 ht_up_free (context.col_td_classes);
1025 }
1026
1027