1 /* radare - LGPL - Copyright 2010-2019 - nibble, alvaro, pancake */
2
3 #include <r_anal.h>
4 #include <r_parse.h>
5 #include <r_util.h>
6 #include <r_list.h>
7
8 #define aprintf(format, ...) if (anal->verbose) eprintf (format, __VA_ARGS__)
9
10 #define JMPTBL_MAXSZ 512
11
apply_case(RAnal * anal,RAnalBlock * block,ut64 switch_addr,ut64 offset_sz,ut64 case_addr,ut64 id,ut64 case_addr_loc)12 static void apply_case(RAnal *anal, RAnalBlock *block, ut64 switch_addr, ut64 offset_sz, ut64 case_addr, ut64 id, ut64 case_addr_loc) {
13 // eprintf ("** apply_case: 0x%"PFMT64x " from 0x%"PFMT64x "\n", case_addr, case_addr_loc);
14 r_meta_set_data_at (anal, case_addr_loc, offset_sz);
15 r_anal_hint_set_immbase (anal, case_addr_loc, 10);
16 r_anal_xrefs_set (anal, switch_addr, case_addr, R_ANAL_REF_TYPE_CODE);
17 if (block) {
18 r_anal_block_add_switch_case (block, switch_addr, id, case_addr);
19 }
20 if (anal->flb.set) {
21 char flagname[0x30];
22 snprintf (flagname, sizeof (flagname), "case.0x%"PFMT64x ".%d", (ut64)switch_addr, (int)id);
23 anal->flb.set (anal->flb.f, flagname, case_addr, 1);
24 }
25 }
26
apply_switch(RAnal * anal,ut64 switch_addr,ut64 jmptbl_addr,ut64 cases_count,ut64 default_case_addr)27 static void apply_switch(RAnal *anal, ut64 switch_addr, ut64 jmptbl_addr, ut64 cases_count, ut64 default_case_addr) {
28 char tmp[0x30];
29 snprintf (tmp, sizeof (tmp), "switch table (%"PFMT64u" cases) at 0x%"PFMT64x, cases_count, jmptbl_addr);
30 r_meta_set_string (anal, R_META_TYPE_COMMENT, switch_addr, tmp);
31 if (anal->flb.set) {
32 snprintf (tmp, sizeof (tmp), "switch.0x%08"PFMT64x, switch_addr);
33 anal->flb.set (anal->flb.f, tmp, switch_addr, 1);
34 if (default_case_addr != UT64_MAX) {
35 r_anal_xrefs_set (anal, switch_addr, default_case_addr, R_ANAL_REF_TYPE_CODE);
36 snprintf (tmp, sizeof (tmp), "case.default.0x%"PFMT64x, switch_addr);
37 anal->flb.set (anal->flb.f, tmp, default_case_addr, 1);
38 }
39 }
40 }
41
42 // analyze a jmptablle inside a function // maybe rename to r_anal_fcn_jmptbl() ?
r_anal_jmptbl(RAnal * anal,RAnalFunction * fcn,RAnalBlock * block,ut64 jmpaddr,ut64 table,ut64 tablesize,ut64 default_addr)43 R_API bool r_anal_jmptbl(RAnal *anal, RAnalFunction *fcn, RAnalBlock *block, ut64 jmpaddr, ut64 table, ut64 tablesize, ut64 default_addr) {
44 const int depth = 50;
45 return try_walkthrough_jmptbl (anal, fcn, block, depth, jmpaddr, table, table, tablesize, tablesize, default_addr, false);
46 }
47
try_walkthrough_casetbl(RAnal * anal,RAnalFunction * fcn,RAnalBlock * block,int depth,ut64 ip,ut64 jmptbl_loc,ut64 casetbl_loc,ut64 jmptbl_off,ut64 sz,ut64 jmptbl_size,ut64 default_case,bool ret0)48 R_API bool try_walkthrough_casetbl(RAnal *anal, RAnalFunction *fcn, RAnalBlock *block, int depth, ut64 ip, ut64 jmptbl_loc, ut64 casetbl_loc, ut64 jmptbl_off, ut64 sz, ut64 jmptbl_size, ut64 default_case, bool ret0) {
49 bool ret = ret0;
50 if (jmptbl_size == 0) {
51 jmptbl_size = JMPTBL_MAXSZ;
52 }
53 if (jmptbl_loc == UT64_MAX) {
54 aprintf ("Warning: Invalid JumpTable location 0x%08" PFMT64x "\n", jmptbl_loc);
55 return false;
56 }
57 if (casetbl_loc == UT64_MAX) {
58 aprintf ("Warning: Invalid CaseTable location 0x%08" PFMT64x "\n", jmptbl_loc);
59 return false;
60 }
61 if (jmptbl_size < 1 || jmptbl_size > ST32_MAX) {
62 aprintf ("Warning: Invalid JumpTable size at 0x%08" PFMT64x "\n", ip);
63 return false;
64 }
65 ut64 jmpptr, case_idx, jmpptr_idx;
66 ut8 *jmptbl = calloc (jmptbl_size, sz);
67 if (!jmptbl || !anal->iob.read_at (anal->iob.io, jmptbl_loc, jmptbl, jmptbl_size * sz)) {
68 free (jmptbl);
69 return false;
70 }
71 ut8 *casetbl = calloc (jmptbl_size, sizeof (ut8));
72 if (!casetbl || !anal->iob.read_at (anal->iob.io, casetbl_loc, casetbl, jmptbl_size)) {
73 free (jmptbl);
74 free (casetbl);
75 return false;
76 }
77 for (case_idx = 0; case_idx < jmptbl_size; case_idx++) {
78 jmpptr_idx = casetbl[case_idx];
79
80 if (jmpptr_idx >= jmptbl_size) {
81 ret = false;
82 break;
83 }
84
85 switch (sz) {
86 case 1:
87 jmpptr = r_read_le8 (jmptbl + jmpptr_idx);
88 break;
89 case 2:
90 jmpptr = r_read_le16 (jmptbl + jmpptr_idx * 2);
91 break;
92 case 4:
93 jmpptr = r_read_le32 (jmptbl + jmpptr_idx * 4);
94 break;
95 default:
96 jmpptr = r_read_le64 (jmptbl + jmpptr_idx * 8);
97 break;
98 }
99 if (jmpptr == 0 || jmpptr == UT32_MAX || jmpptr == UT64_MAX) {
100 break;
101 }
102 if (!anal->iob.is_valid_offset (anal->iob.io, jmpptr, 0)) {
103 st32 jmpdelta = (st32)jmpptr;
104 // jump tables where sign extended movs are used
105 jmpptr = jmptbl_off + jmpdelta;
106 if (!anal->iob.is_valid_offset (anal->iob.io, jmpptr, 0)) {
107 break;
108 }
109 }
110 if (anal->limit) {
111 if (jmpptr < anal->limit->from || jmpptr > anal->limit->to) {
112 break;
113 }
114 }
115
116 const ut64 jmpptr_idx_off = casetbl_loc + case_idx;
117 r_meta_set_data_at (anal, jmpptr_idx_off, 1);
118 r_anal_hint_set_immbase (anal, jmpptr_idx_off, 10);
119
120 apply_case (anal, block, ip, sz, jmpptr, case_idx, jmptbl_loc + jmpptr_idx * sz);
121 (void)r_anal_fcn_bb (anal, fcn, jmpptr, depth - 1);
122 }
123
124 if (case_idx > 0) {
125 if (default_case == 0) {
126 default_case = UT64_MAX;
127 }
128 apply_switch (anal, ip, jmptbl_loc, case_idx, default_case);
129 }
130
131 free (jmptbl);
132 free (casetbl);
133 return ret;
134 }
135
try_walkthrough_jmptbl(RAnal * anal,RAnalFunction * fcn,RAnalBlock * block,int depth,ut64 ip,ut64 jmptbl_loc,ut64 jmptbl_off,ut64 sz,ut64 jmptbl_size,ut64 default_case,bool ret0)136 R_API bool try_walkthrough_jmptbl(RAnal *anal, RAnalFunction *fcn, RAnalBlock *block, int depth, ut64 ip, ut64 jmptbl_loc, ut64 jmptbl_off, ut64 sz, ut64 jmptbl_size, ut64 default_case, bool ret0) {
137 bool ret = ret0;
138 // jmptbl_size can not always be determined
139 if (jmptbl_size == 0) {
140 jmptbl_size = JMPTBL_MAXSZ;
141 }
142 if (jmptbl_loc == UT64_MAX) {
143 aprintf ("Warning: Invalid JumpTable location 0x%08"PFMT64x"\n", jmptbl_loc);
144 return false;
145 }
146 if (jmptbl_size < 1 || jmptbl_size > ST32_MAX) {
147 aprintf ("Warning: Invalid JumpTable size at 0x%08"PFMT64x"\n", ip);
148 return false;
149 }
150 ut64 jmpptr, offs;
151 ut8 *jmptbl = calloc (jmptbl_size, sz);
152 if (!jmptbl) {
153 return false;
154 }
155 bool is_arm = anal->cur->arch && !strncmp (anal->cur->arch, "arm", 3);
156 // eprintf ("JMPTBL AT 0x%"PFMT64x"\n", jmptbl_loc);
157 anal->iob.read_at (anal->iob.io, jmptbl_loc, jmptbl, jmptbl_size * sz);
158 for (offs = 0; offs + sz - 1 < jmptbl_size * sz; offs += sz) {
159 switch (sz) {
160 case 1:
161 jmpptr = (ut64)(ut8)r_read_le8 (jmptbl + offs);
162 break;
163 case 2:
164 jmpptr = (ut64)r_read_le16 (jmptbl + offs);
165 break;
166 case 4:
167 jmpptr = r_read_le32 (jmptbl + offs);
168 break;
169 case 8:
170 jmpptr = r_read_le64 (jmptbl + offs);
171 break; // XXX
172 default:
173 jmpptr = r_read_le64 (jmptbl + offs);
174 break;
175 }
176 // eprintf ("WALKING %llx\n", jmpptr);
177 // if we don't check for 0 here, the next check with ptr+jmpptr
178 // will obviously be a good offset since it will be the start
179 // of the table, which is not what we want
180 if (jmpptr == 0 || jmpptr == UT32_MAX || jmpptr == UT64_MAX) {
181 break;
182 }
183 if (sz == 2 && is_arm) {
184 jmpptr = ip + 4 + (jmpptr * 2); // tbh [pc, r2, lsl 1] // assume lsl 1
185 } else if (sz == 1 && is_arm) {
186 jmpptr = ip + 4 + (jmpptr * 2); // lbb [pc, r2] // assume lsl 1
187 } else if (!anal->iob.is_valid_offset (anal->iob.io, jmpptr, 0)) {
188 st32 jmpdelta = (st32)jmpptr;
189 // jump tables where sign extended movs are used
190 jmpptr = jmptbl_off + jmpdelta;
191 if (!anal->iob.is_valid_offset (anal->iob.io, jmpptr, 0)) {
192 break;
193 }
194 }
195 if (anal->limit) {
196 if (jmpptr < anal->limit->from || jmpptr > anal->limit->to) {
197 break;
198 }
199 }
200 apply_case (anal, block, ip, sz, jmpptr, offs / sz, jmptbl_loc + offs);
201 (void)r_anal_fcn_bb (anal, fcn, jmpptr, depth - 1);
202 }
203
204 if (offs > 0) {
205 if (default_case == 0) {
206 default_case = UT64_MAX;
207 }
208 apply_switch (anal, ip, jmptbl_loc, offs / sz, default_case);
209 }
210
211 free (jmptbl);
212 return ret;
213 }
214
215 // TODO: RENAME
try_get_delta_jmptbl_info(RAnal * anal,RAnalFunction * fcn,ut64 jmp_addr,ut64 lea_addr,ut64 * table_size,ut64 * default_case)216 R_API bool try_get_delta_jmptbl_info(RAnal *anal, RAnalFunction *fcn, ut64 jmp_addr, ut64 lea_addr, ut64 *table_size, ut64 *default_case) {
217 bool isValid = false;
218 bool foundCmp = false;
219 int i;
220
221 RAnalOp tmp_aop = {0};
222 if (lea_addr > jmp_addr) {
223 return false;
224 }
225 int search_sz = jmp_addr - lea_addr;
226 ut8 *buf = malloc (search_sz);
227 if (!buf) {
228 return false;
229 }
230 // search for a cmp register with a reasonable size
231 anal->iob.read_at (anal->iob.io, lea_addr, (ut8 *)buf, search_sz);
232
233 for (i = 0; i + 8 < search_sz; i++) {
234 int len = r_anal_op (anal, &tmp_aop, lea_addr + i, buf + i, search_sz - i, R_ANAL_OP_MASK_BASIC);
235 if (len < 1) {
236 len = 1;
237 }
238
239 if (foundCmp) {
240 if (tmp_aop.type != R_ANAL_OP_TYPE_CJMP) {
241 continue;
242 }
243
244 *default_case = tmp_aop.jump == tmp_aop.jump + len ? tmp_aop.fail : tmp_aop.jump;
245 break;
246 }
247
248 ut32 type = tmp_aop.type & R_ANAL_OP_TYPE_MASK;
249 if (type != R_ANAL_OP_TYPE_CMP) {
250 continue;
251 }
252 // get the value of the cmp
253 // for operands in op, check if type is immediate and val is sane
254 // TODO: How? opex?
255
256 // for the time being, this seems to work
257 // might not actually have a value, let the next step figure out the size then
258 if (tmp_aop.val == UT64_MAX && tmp_aop.refptr == 0) {
259 isValid = true;
260 *table_size = 0;
261 } else if (tmp_aop.refptr == 0) {
262 isValid = tmp_aop.val < 0x200;
263 *table_size = tmp_aop.val + 1;
264 } else {
265 isValid = tmp_aop.refptr < 0x200;
266 *table_size = tmp_aop.refptr + 1;
267 }
268 // TODO: check the jmp for whether val is included in valid range or not (ja vs jae)
269 foundCmp = true;
270 }
271 free (buf);
272 return isValid;
273 }
274
275 // TODO: find a better function name
walkthrough_arm_jmptbl_style(RAnal * anal,RAnalFunction * fcn,RAnalBlock * block,int depth,ut64 ip,ut64 jmptbl_loc,ut64 sz,ut64 jmptbl_size,ut64 default_case,int ret0)276 R_API int walkthrough_arm_jmptbl_style(RAnal *anal, RAnalFunction *fcn, RAnalBlock *block, int depth, ut64 ip, ut64 jmptbl_loc, ut64 sz, ut64 jmptbl_size, ut64 default_case, int ret0) {
277 /*
278 * Example about arm jump table
279 *
280 * 0x000105b4 060050e3 cmp r0, 3
281 * 0x000105b8 00f18f90 addls pc, pc, r0, lsl 2
282 * 0x000105bc 0d0000ea b loc.000105f8
283 * 0x000105c0 050000ea b 0x105dc
284 * 0x000105c4 050000ea b 0x105e0
285 * 0x000105c8 060000ea b 0x105e8
286 * ; CODE XREF from loc._a_7 (+0x10)
287 * 0x000105dc b6ffffea b sym.input_1
288 * ; CODE XREF from loc._a_7 (+0x14)
289 * 0x000105e0 b9ffffea b sym.input_2
290 * ; CODE XREF from loc._a_7 (+0x28)
291 * 0x000105e4 ccffffea b sym.input_7
292 * ; CODE XREF from loc._a_7 (+0x18)
293 * 0x000105e8 bbffffea b sym.input_3
294 */
295
296 ut64 offs, jmpptr;
297 int ret = ret0;
298
299 if (jmptbl_size == 0) {
300 jmptbl_size = JMPTBL_MAXSZ;
301 }
302
303 for (offs = 0; offs + sz - 1 < jmptbl_size * sz; offs += sz) {
304 jmpptr = jmptbl_loc + offs;
305 apply_case (anal, block, ip, sz, jmpptr, offs / sz, jmptbl_loc + offs);
306 (void)r_anal_fcn_bb (anal, fcn, jmpptr, depth - 1);
307 }
308
309 if (offs > 0) {
310 if (default_case == 0 || default_case == UT32_MAX) {
311 default_case = UT64_MAX;
312 }
313 apply_switch (anal, ip, jmptbl_loc, offs / sz, default_case);
314 }
315 return ret;
316 }
317
try_get_jmptbl_info(RAnal * anal,RAnalFunction * fcn,ut64 addr,RAnalBlock * my_bb,ut64 * table_size,ut64 * default_case)318 R_API bool try_get_jmptbl_info(RAnal *anal, RAnalFunction *fcn, ut64 addr, RAnalBlock *my_bb, ut64 *table_size, ut64 *default_case) {
319 bool isValid = false;
320 int i;
321 RListIter *iter;
322 RAnalBlock *tmp_bb, *prev_bb;
323 prev_bb = 0;
324 if (!fcn->bbs) {
325 return false;
326 }
327
328 /* if UJMP is in .plt section just skip it */
329 RBinSection *s = anal->binb.get_vsect_at (anal->binb.bin, addr);
330 if (s && s->name[0]) {
331 bool in_plt = strstr (s->name, ".plt") != NULL;
332 if (!in_plt && strstr (s->name, "_stubs") != NULL) {
333 /* for mach0 */
334 in_plt = true;
335 }
336 if (in_plt) {
337 return false;
338 }
339 }
340
341 // search for the predecessor bb
342 r_list_foreach (fcn->bbs, iter, tmp_bb) {
343 if (tmp_bb->jump == my_bb->addr || tmp_bb->fail == my_bb->addr) {
344 prev_bb = tmp_bb;
345 break;
346 }
347 }
348 // predecessor must be a conditional jump
349 if (!prev_bb || !prev_bb->jump || !prev_bb->fail) {
350 aprintf ("Warning: [anal.jmp.tbl] Missing predecesessor cjmp bb at 0x%08"PFMT64x"\n", addr);
351 return false;
352 }
353
354 // default case is the jump target of the unconditional jump
355 *default_case = prev_bb->jump == my_bb->addr ? prev_bb->fail : prev_bb->jump;
356
357 RAnalOp tmp_aop = {0};
358 ut8 *bb_buf = calloc (1, prev_bb->size);
359 if (!bb_buf) {
360 return false;
361 }
362 // search for a cmp register with a reasonable size
363 anal->iob.read_at (anal->iob.io, prev_bb->addr, (ut8 *) bb_buf, prev_bb->size);
364 isValid = false;
365
366 RAnalHint *hint = r_anal_hint_get (anal, addr);
367 if (hint) {
368 ut64 val = hint->val;
369 r_anal_hint_free (hint);
370 if (val != UT64_MAX) {
371 *table_size = val;
372 return true;
373 }
374 }
375
376 for (i = 0; i < prev_bb->op_pos_size; i++) {
377 ut64 prev_pos = prev_bb->op_pos[i];
378 ut64 op_addr = prev_bb->addr + prev_pos;
379 if (prev_pos >= prev_bb->size) {
380 continue;
381 }
382 int buflen = prev_bb->size - prev_pos;
383 int len = r_anal_op (anal, &tmp_aop, op_addr,
384 bb_buf + prev_pos, buflen,
385 R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT);
386 ut32 type = tmp_aop.type & R_ANAL_OP_TYPE_MASK;
387 if (len < 1 || type != R_ANAL_OP_TYPE_CMP) {
388 r_anal_op_fini (&tmp_aop);
389 continue;
390 }
391 // get the value of the cmp
392 // for operands in op, check if type is immediate and val is sane
393 // TODO: How? opex?
394
395 // for the time being, this seems to work
396 // might not actually have a value, let the next step figure out the size then
397 if (tmp_aop.val == UT64_MAX && tmp_aop.refptr == 0) {
398 isValid = true;
399 *table_size = 0;
400 } else if (tmp_aop.refptr == 0 || tmp_aop.val != UT64_MAX) {
401 isValid = tmp_aop.val < 0x200;
402 *table_size = tmp_aop.val + 1;
403 } else {
404 isValid = tmp_aop.refptr < 0x200;
405 *table_size = tmp_aop.refptr + 1;
406 }
407 r_anal_op_fini (&tmp_aop);
408 // TODO: check the jmp for whether val is included in valid range or not (ja vs jae)
409 break;
410 }
411 free (bb_buf);
412 // eprintf ("switch at 0x%" PFMT64x "\n\tdefault case 0x%" PFMT64x "\n\t#cases: %d\n",
413 // addr,
414 // *default_case,
415 // *table_size);
416 return isValid;
417 }
418