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