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