1 /* radare2 - LGPL - Copyright 2017-2021 - xvilka, deroad */
2 
3 #include <string.h>
4 #include <r_types.h>
5 #include <r_lib.h>
6 #include <r_asm.h>
7 #include <r_anal.h>
8 #undef R_IPI
9 #define R_IPI static
10 #define WASM_NO_ASM // to get rid of a warning
11 #include "../../bin/format/wasm/wasm.h"
12 #include "../../asm/arch/wasm/wasm.c"
13 
14 #define WASM_STACK_SIZE 256
15 
16 static ut64 scope_hint = UT64_MAX;
17 static ut64 addr_old = UT64_MAX;
18 
19 // finds the address of the call function (essentially where to jump to).
get_cf_offset(RAnal * anal,const ut8 * data,int len)20 static ut64 get_cf_offset(RAnal *anal, const ut8 *data, int len) {
21 	ut32 fcn_id;
22 	if (len < 2 || !read_u32_leb128 (&data[1], &data[len - 1], &fcn_id)) {
23 		return UT64_MAX;
24 	}
25 	return anal->binb.get_offset (anal->binb.bin, 'f', fcn_id);
26 }
27 
advance_till_scope_end(RAnal * anal,RAnalOp * op,ut64 address,ut32 expected_type,ut32 depth,bool use_else)28 static bool advance_till_scope_end(RAnal* anal, RAnalOp *op, ut64 address, ut32 expected_type, ut32 depth, bool use_else) {
29 	ut8 buffer[16];
30 	ut8 *ptr = buffer;
31 	ut8 *end = ptr + sizeof (buffer);
32 	WasmOp wop = {{0}};
33 	int size = 0;
34 	while (anal->iob.read_at (anal->iob.io, address, buffer, sizeof (buffer))) {
35 		size = wasm_dis (&wop, ptr, end - ptr);
36 		if (!wop.txt || (wop.type == WASM_TYPE_OP_CORE && wop.op.core == WASM_OP_TRAP)) {
37 			// if invalid stop here.
38 			break;
39 		}
40 		if (wop.type == WASM_TYPE_OP_CORE) {
41 			WasmOpCodes wopop = wop.op.core;
42 			if (wopop == WASM_OP_LOOP || wopop == WASM_OP_BLOCK || wopop == WASM_OP_IF) {
43 				depth++;
44 			}
45 			if (use_else && wopop == WASM_OP_ELSE && !depth) {
46 				op->type = expected_type;
47 				op->jump = address + 1; // else size == 1
48 				return true;
49 			} else if (wopop == WASM_OP_END && depth > 0) {
50 				// let's wait till i get the final depth
51 				depth--;
52 			} else if (wopop == WASM_OP_END && !depth) {
53 				op->type = expected_type;
54 				op->jump = address;
55 				return true;
56 			}
57 		}
58 		address += size;
59 	}
60 	return false;
61 }
62 
63 // analyzes the wasm opcode.
wasm_op(RAnal * anal,RAnalOp * op,ut64 addr,const ut8 * data,int len,RAnalOpMask mask)64 static int wasm_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len, RAnalOpMask mask) {
65 	WasmOp wop = {{0}};
66 	RAnalHint *hint = NULL;
67 	int ret = wasm_dis (&wop, data, len);
68 	op->size = ret;
69 	op->addr = addr;
70 	op->sign = true;
71 	op->type = R_ANAL_OP_TYPE_UNK;
72 	switch (wop.type) {
73 	case WASM_TYPE_OP_CORE:
74 		op->id = wop.op.core;
75 		break;
76 	case WASM_TYPE_OP_ATOMIC:
77 		op->id = (0xfe << 8) | wop.op.atomic;
78 		break;
79 	case WASM_TYPE_OP_SIMD:
80 		op->id = 0xfd;
81 		break;
82 	}
83 
84 	if (!wop.txt || !strncmp (wop.txt, "invalid", 7)) {
85 		op->type = R_ANAL_OP_TYPE_ILL;
86 		free (wop.txt);
87 		return -1;
88 	}
89 
90 	if (addr_old == addr && (wop.type != WASM_TYPE_OP_CORE || wop.op.core != WASM_OP_END)) {
91 		goto anal_end;
92 	}
93 
94 	switch (wop.type) {
95 	case WASM_TYPE_OP_CORE:
96 		switch (wop.op.core) {
97 		/* Calls here are using index instead of address */
98 		case WASM_OP_LOOP:
99 			op->type = R_ANAL_OP_TYPE_NOP;
100 			if (!(hint = r_anal_hint_get (anal, addr))) {
101 				scope_hint--;
102 				r_anal_hint_set_opcode (anal, scope_hint, "loop");
103 				r_anal_hint_set_jump (anal, scope_hint, addr);
104 			}
105 			break;
106 		case WASM_OP_BLOCK:
107 			op->type = R_ANAL_OP_TYPE_NOP;
108 			if (!(hint = r_anal_hint_get (anal, addr))) {
109 				scope_hint--;
110 				r_anal_hint_set_opcode (anal, scope_hint, "block");
111 				r_anal_hint_set_jump (anal, scope_hint, addr);
112 			}
113 			break;
114 		case WASM_OP_IF:
115 			if (!(hint = r_anal_hint_get (anal, addr))) {
116 				scope_hint--;
117 				r_anal_hint_set_opcode (anal, scope_hint, "if");
118 				r_anal_hint_set_jump (anal, scope_hint, addr);
119 				if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_CJMP, 0, true)) {
120 					op->fail = addr + op->size;
121 				}
122 			} else {
123 				op->type = R_ANAL_OP_TYPE_CJMP;
124 				op->jump = hint->jump;
125 				op->fail = addr + op->size;
126 			}
127 			break;
128 		case WASM_OP_ELSE:
129 			// get if and set hint.
130 			if (!(hint = r_anal_hint_get (anal, addr))) {
131 				advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_JMP, 0, true);
132 			} else {
133 				op->type = R_ANAL_OP_TYPE_JMP;
134 				op->jump = hint->jump;
135 			}
136 			break;
137 		case WASM_OP_BR:
138 			{
139 				RAnalHint *hint2 = NULL;
140 				ut32 val;
141 				read_u32_leb128 (data + 1, data + len, &val);
142 				if ((hint2 = r_anal_hint_get (anal, addr)) && hint2->jump != UT64_MAX) {
143 					op->type = R_ANAL_OP_TYPE_JMP;
144 					op->jump = hint2->jump;
145 				} else if ((hint = r_anal_hint_get (anal, scope_hint))) {
146 					if (hint->opcode && !strncmp ("loop", hint->opcode, 4)) {
147 						op->type = R_ANAL_OP_TYPE_JMP;
148 						op->jump = hint->jump;
149 						r_anal_hint_set_jump (anal, addr, op->jump);
150 					} else {
151 						if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_JMP, val, false)) {
152 							r_anal_hint_set_jump (anal, addr, op->jump);
153 						}
154 					}
155 				} else {
156 					if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_JMP, val, false)) {
157 						eprintf ("[wasm] cannot find jump type for br (using block type)\n");
158 						r_anal_hint_set_jump (anal, addr, op->jump);
159 					} else {
160 						eprintf ("[wasm] cannot find jump for br\n");
161 					}
162 				}
163 				r_anal_hint_free (hint2);
164 			}
165 			break;
166 		case WASM_OP_BRIF:
167 			{
168 				RAnalHint *hint2 = NULL;
169 				ut32 val;
170 				read_u32_leb128 (data + 1, data + len, &val);
171 				if ((hint2 = r_anal_hint_get (anal, addr)) && hint2->jump != UT64_MAX) {
172 					op->type = R_ANAL_OP_TYPE_CJMP;
173 					op->jump = hint2->jump;
174 					op->fail = addr + op->size;
175 				} else if ((hint = r_anal_hint_get (anal, scope_hint))) {
176 					if (hint->opcode && !strncmp ("loop", hint->opcode, 4)) {
177 						op->fail = addr + op->size;
178 						op->jump = hint->jump;
179 						r_anal_hint_set_jump (anal, addr, op->jump);
180 					} else {
181 						if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_CJMP, val, false)) {
182 							op->fail = addr + op->size;
183 							r_anal_hint_set_jump (anal, addr, op->jump);
184 						}
185 					}
186 				} else {
187 					if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_CJMP, val, false)) {
188 						eprintf ("[wasm] cannot find jump type for br_if (using block type)\n");
189 						op->fail = addr + op->size;
190 						r_anal_hint_set_jump (anal, addr, op->jump);
191 					} else {
192 						eprintf ("[wasm] cannot find jump for br_if\n");
193 					}
194 				}
195 				r_anal_hint_free (hint2);
196 			}
197 			break;
198 		case WASM_OP_END:
199 			{
200 				op->type = R_ANAL_OP_TYPE_NOP;
201 				if (scope_hint < UT64_MAX) {
202 					hint = r_anal_hint_get (anal, scope_hint);
203 					if (hint && !strncmp ("loop", hint->opcode, 4)) {
204 						r_anal_hint_set_jump (anal, addr, op->jump);
205 						r_anal_hint_set_jump (anal, op->jump, addr);
206 					} else if (hint && !strncmp ("block", hint->opcode, 5)) {
207 						// if/else/block
208 						r_anal_hint_set_jump (anal, hint->jump, addr);
209 						r_anal_hint_set_jump (anal, addr, UT64_MAX);
210 					}
211 					if (hint) {
212 						r_anal_hint_set_opcode (anal, scope_hint, "invalid");
213 						r_anal_hint_set_jump (anal, scope_hint, UT64_MAX);
214 						r_anal_hint_del (anal, scope_hint, 1);
215 						scope_hint++;
216 					} else {
217 						// all wasm routines ends with an end.
218 						op->eob = true;
219 						op->type = R_ANAL_OP_TYPE_RET;
220 						scope_hint = UT64_MAX;
221 					}
222 				} else {
223 					if (!(hint = r_anal_hint_get (anal, addr))) {
224 						// all wasm routines ends with an end.
225 						op->eob = true;
226 						op->type = R_ANAL_OP_TYPE_RET;
227 					}
228 				}
229 			}
230 			break;
231 		case WASM_OP_I32REMS:
232 		case WASM_OP_I32REMU:
233 			op->type = R_ANAL_OP_TYPE_MOD;
234 			break;
235 		case WASM_OP_GETLOCAL:
236 		case WASM_OP_I32LOAD:
237 		case WASM_OP_I64LOAD:
238 		case WASM_OP_F32LOAD:
239 		case WASM_OP_F64LOAD:
240 		case WASM_OP_I32LOAD8S:
241 		case WASM_OP_I32LOAD8U:
242 		case WASM_OP_I32LOAD16S:
243 		case WASM_OP_I32LOAD16U:
244 		case WASM_OP_I64LOAD8S:
245 		case WASM_OP_I64LOAD8U:
246 		case WASM_OP_I64LOAD16S:
247 		case WASM_OP_I64LOAD16U:
248 		case WASM_OP_I64LOAD32S:
249 		case WASM_OP_I64LOAD32U:
250 			op->type = R_ANAL_OP_TYPE_LOAD;
251 			break;
252 		case WASM_OP_SETLOCAL:
253 		case WASM_OP_TEELOCAL:
254 			op->type = R_ANAL_OP_TYPE_STORE;
255 			break;
256 		case WASM_OP_I32EQZ:
257 		case WASM_OP_I32EQ:
258 		case WASM_OP_I32NE:
259 		case WASM_OP_I32LTS:
260 		case WASM_OP_I32LTU:
261 		case WASM_OP_I32GTS:
262 		case WASM_OP_I32GTU:
263 		case WASM_OP_I32LES:
264 		case WASM_OP_I32LEU:
265 		case WASM_OP_I32GES:
266 		case WASM_OP_I32GEU:
267 		case WASM_OP_I64EQZ:
268 		case WASM_OP_I64EQ:
269 		case WASM_OP_I64NE:
270 		case WASM_OP_I64LTS:
271 		case WASM_OP_I64LTU:
272 		case WASM_OP_I64GTS:
273 		case WASM_OP_I64GTU:
274 		case WASM_OP_I64LES:
275 		case WASM_OP_I64LEU:
276 		case WASM_OP_I64GES:
277 		case WASM_OP_I64GEU:
278 		case WASM_OP_F32EQ:
279 		case WASM_OP_F32NE:
280 		case WASM_OP_F32LT:
281 		case WASM_OP_F32GT:
282 		case WASM_OP_F32LE:
283 		case WASM_OP_F32GE:
284 		case WASM_OP_F64EQ:
285 		case WASM_OP_F64NE:
286 		case WASM_OP_F64LT:
287 		case WASM_OP_F64GT:
288 		case WASM_OP_F64LE:
289 		case WASM_OP_F64GE:
290 			op->type = R_ANAL_OP_TYPE_CMP;
291 			break;
292 		case WASM_OP_I64OR:
293 		case WASM_OP_I32OR:
294 			op->type = R_ANAL_OP_TYPE_OR;
295 			break;
296 		case WASM_OP_I64XOR:
297 		case WASM_OP_I32XOR:
298 			op->type = R_ANAL_OP_TYPE_XOR;
299 			break;
300 		case WASM_OP_I32CONST:
301 		case WASM_OP_I64CONST:
302 		case WASM_OP_F32CONST:
303 		case WASM_OP_F64CONST:
304 			op->type = R_ANAL_OP_TYPE_MOV;
305 			{
306 				ut8 arg = data[1];
307 				r_strbuf_setf (&op->esil, "4,sp,-=,%d,sp,=[4]", arg);
308 			}
309 			break;
310 		case WASM_OP_I64ADD:
311 		case WASM_OP_I32ADD:
312 		case WASM_OP_F32ADD:
313 		case WASM_OP_F64ADD:
314 			op->type = R_ANAL_OP_TYPE_ADD;
315 			break;
316 		case WASM_OP_I64SUB:
317 		case WASM_OP_I32SUB:
318 		case WASM_OP_F32SUB:
319 		case WASM_OP_F64SUB:
320 			op->type = R_ANAL_OP_TYPE_SUB;
321 			break;
322 		case WASM_OP_NOP:
323 			op->type = R_ANAL_OP_TYPE_NOP;
324 			r_strbuf_setf (&op->esil, "%s", "");
325 			break;
326 		case WASM_OP_CALL:
327 		case WASM_OP_CALLINDIRECT:
328 			op->type = R_ANAL_OP_TYPE_CALL;
329 			op->jump = get_cf_offset (anal, data, len);
330 			op->fail = addr + op->size;
331 			if (op->jump != UT64_MAX) {
332 				op->ptr = op->jump;
333 			}
334 			r_strbuf_setf (&op->esil, "4,sp,-=,0x%"PFMT64x",sp,=[4],0x%"PFMT64x",pc,=", op->fail, op->jump);
335 			break;
336 		case WASM_OP_RETURN:
337 			// should be ret, but if there the analisys is stopped.
338 			op->type = R_ANAL_OP_TYPE_CRET;
339 		default:
340 			break;
341 		}
342 		break;
343 	case WASM_TYPE_OP_ATOMIC:
344 		switch (wop.op.atomic) {
345 		case WASM_OP_I32ATOMICLOAD:
346 		case WASM_OP_I64ATOMICLOAD:
347 		case WASM_OP_I32ATOMICLOAD8U:
348 		case WASM_OP_I32ATOMICLOAD16U:
349 		case WASM_OP_I64ATOMICLOAD8U:
350 		case WASM_OP_I64ATOMICLOAD16U:
351 		case WASM_OP_I64ATOMICLOAD32U:
352 			op->type = R_ANAL_OP_TYPE_LOAD;
353 			break;
354 		case WASM_OP_I32ATOMICSTORE:
355 		case WASM_OP_I64ATOMICSTORE:
356 		case WASM_OP_I32ATOMICSTORE8:
357 		case WASM_OP_I32ATOMICSTORE16:
358 		case WASM_OP_I64ATOMICSTORE8:
359 		case WASM_OP_I64ATOMICSTORE16:
360 		case WASM_OP_I64ATOMICSTORE32:
361 			op->type = R_ANAL_OP_TYPE_STORE;
362 			break;
363 		case WASM_OP_I32ATOMICRMWADD:
364 		case WASM_OP_I64ATOMICRMWADD:
365 		case WASM_OP_I32ATOMICRMW8UADD:
366 		case WASM_OP_I32ATOMICRMW16UADD:
367 		case WASM_OP_I64ATOMICRMW8UADD:
368 		case WASM_OP_I64ATOMICRMW16UADD:
369 		case WASM_OP_I64ATOMICRMW32UADD:
370 			op->type = R_ANAL_OP_TYPE_ADD;
371 			break;
372 		case WASM_OP_I32ATOMICRMW8USUB:
373 		case WASM_OP_I32ATOMICRMW16USUB:
374 		case WASM_OP_I32ATOMICRMWSUB:
375 		case WASM_OP_I64ATOMICRMW8USUB:
376 		case WASM_OP_I64ATOMICRMW16USUB:
377 		case WASM_OP_I64ATOMICRMW32USUB:
378 		case WASM_OP_I64ATOMICRMWSUB:
379 			op->type = R_ANAL_OP_TYPE_SUB;
380 			break;
381 		case WASM_OP_I32ATOMICRMWAND:
382 		case WASM_OP_I64ATOMICRMWAND:
383 		case WASM_OP_I32ATOMICRMW8UAND:
384 		case WASM_OP_I32ATOMICRMW16UAND:
385 		case WASM_OP_I64ATOMICRMW8UAND:
386 		case WASM_OP_I64ATOMICRMW16UAND:
387 		case WASM_OP_I64ATOMICRMW32UAND:
388 			op->type = R_ANAL_OP_TYPE_AND;
389 			break;
390 		case WASM_OP_I32ATOMICRMWOR:
391 		case WASM_OP_I64ATOMICRMWOR:
392 		case WASM_OP_I32ATOMICRMW8UOR:
393 		case WASM_OP_I32ATOMICRMW16UOR:
394 		case WASM_OP_I64ATOMICRMW8UOR:
395 		case WASM_OP_I64ATOMICRMW16UOR:
396 		case WASM_OP_I64ATOMICRMW32UOR:
397 			op->type = R_ANAL_OP_TYPE_OR;
398 			break;
399 		case WASM_OP_I32ATOMICRMWXOR:
400 		case WASM_OP_I64ATOMICRMWXOR:
401 		case WASM_OP_I32ATOMICRMW8UXOR:
402 		case WASM_OP_I32ATOMICRMW16UXOR:
403 		case WASM_OP_I64ATOMICRMW8UXOR:
404 		case WASM_OP_I64ATOMICRMW16UXOR:
405 		case WASM_OP_I64ATOMICRMW32UXOR:
406 			op->type = R_ANAL_OP_TYPE_XOR;
407 			break;
408 		case WASM_OP_I32ATOMICRMWXCHG:
409 		case WASM_OP_I64ATOMICRMWXCHG:
410 		case WASM_OP_I32ATOMICRMW8UXCHG:
411 		case WASM_OP_I32ATOMICRMW16UXCHG:
412 		case WASM_OP_I64ATOMICRMW8UXCHG:
413 		case WASM_OP_I64ATOMICRMW16UXCHG:
414 		case WASM_OP_I64ATOMICRMW32UXCHG:
415 			op->type = R_ANAL_OP_TYPE_XCHG;
416 			break;
417 		default:
418 			break;
419 		}
420 	default:
421 		break;
422 	}
423 
424 anal_end:
425 	addr_old = addr;
426 	free (wop.txt);
427 	r_anal_hint_free (hint);
428 	return op->size;
429 }
430 
archinfo(RAnal * a,int q)431 static int archinfo(RAnal *a, int q) {
432 	return 1;
433 }
434 
get_reg_profile(RAnal * anal)435 static char *get_reg_profile(RAnal *anal) {
436 	return strdup (
437 		"=PC	pc\n"
438 		"=BP	bp\n"
439 		"=SP	sp\n"
440 		"=A0	r0\n"
441 		"=A1	r1\n"
442 		"=A2	r2\n"
443 		"gpr	sp	.32	0	0\n" // stack pointer
444 		"gpr	pc	.32	4	0\n" // program counter
445 		"gpr	bp	.32	8	0\n" // base pointer // unused
446 	);
447 }
448 
449 RAnalPlugin r_anal_plugin_wasm = {
450 	.name = "wasm",
451 	.desc = "WebAssembly analysis plugin",
452 	.license = "LGPL3",
453 	.arch = "wasm",
454 	.bits = 64,
455 	.archinfo = archinfo,
456 	.get_reg_profile = get_reg_profile,
457 	.op = &wasm_op,
458 	.esil = true
459 };
460 
461 #ifndef R2_PLUGIN_INCORE
462 R_API RLibStruct radare_plugin = {
463 	.type = R_LIB_TYPE_ANAL,
464 	.data = &r_anal_plugin_wasm,
465 	.version = R2_VERSION
466 };
467 #endif
468