1 /* radare2 - LGPL - Copyright 2017-2021 - pancake, cgvwzq */
2
3 // http://webassembly.org/docs/binary-encoding/#module-structure
4
5 #include <r_types.h>
6 #include <r_util.h>
7 #include <r_lib.h>
8 #include <r_bin.h>
9
10 #include "wasm/wasm.h"
11 #include "../format/wasm/wasm.h"
12
check_buffer(RBuffer * rbuf)13 static bool check_buffer(RBuffer *rbuf) {
14 ut8 buf[4] = { 0 };
15 return rbuf && r_buf_read_at (rbuf, 0, buf, 4) == 4 && !memcmp (buf, R_BIN_WASM_MAGIC_BYTES, 4);
16 }
17
find_export(const ut32 * p,const RBinWasmExportEntry * q)18 static bool find_export(const ut32 *p, const RBinWasmExportEntry *q) {
19 if (q->kind != R_BIN_WASM_EXTERNALKIND_Function) {
20 return true;
21 }
22 return q->index != *p;
23 }
24
load_buffer(RBinFile * bf,void ** bin_obj,RBuffer * buf,ut64 loadaddr,Sdb * sdb)25 static bool load_buffer(RBinFile *bf, void **bin_obj, RBuffer *buf, ut64 loadaddr, Sdb *sdb) {
26 r_return_val_if_fail (bf && buf && r_buf_size (buf) != UT64_MAX, false);
27
28 if (check_buffer (buf)) {
29 *bin_obj = r_bin_wasm_init (bf, buf);
30 return true;
31 }
32 return false;
33 }
34
destroy(RBinFile * bf)35 static void destroy(RBinFile *bf) {
36 r_bin_wasm_destroy (bf);
37 }
38
baddr(RBinFile * bf)39 static ut64 baddr(RBinFile *bf) {
40 return 0;
41 }
42
binsym(RBinFile * bf,int type)43 static RBinAddr *binsym(RBinFile *bf, int type) {
44 return NULL; // TODO
45 }
46
47 static RList *sections(RBinFile *bf);
48
entries(RBinFile * bf)49 static RList *entries(RBinFile *bf) {
50 RBinWasmObj *bin = bf && bf->o ? bf->o->bin_obj : NULL;
51 // TODO
52 RList *ret = NULL;
53 RBinAddr *ptr = NULL;
54
55 if (!(ret = r_list_newf ((RListFree)free))) {
56 return NULL;
57 }
58
59 ut64 addr = (ut64)r_bin_wasm_get_entrypoint (bin);
60 if (!addr) {
61 RList *codes = r_bin_wasm_get_codes (bin);
62 if (codes) {
63 RListIter *iter;
64 RBinWasmCodeEntry *func;
65 r_list_foreach (codes, iter, func) {
66 addr = func->code;
67 break;
68 }
69 }
70 if (!addr) {
71 r_list_free (ret);
72 return NULL;
73 }
74 }
75 if ((ptr = R_NEW0 (RBinAddr))) {
76 ptr->paddr = addr;
77 ptr->vaddr = addr;
78 r_list_append (ret, ptr);
79 }
80 return ret;
81 }
82
sections(RBinFile * bf)83 static RList *sections (RBinFile *bf) {
84 RBinWasmObj *bin = bf && bf->o ? bf->o->bin_obj : NULL;
85 RList *ret = NULL;
86 RList *secs = NULL;
87 RBinSection *ptr = NULL;
88 RBinWasmSection *sec;
89
90 if (!(ret = r_list_newf ((RListFree)free))) {
91 return NULL;
92 }
93 if (!(secs = r_bin_wasm_get_sections (bin))) {
94 r_list_free (ret);
95 return NULL;
96 }
97 RListIter *iter;
98 r_list_foreach (secs, iter, sec) {
99 if (!(ptr = R_NEW0 (RBinSection))) {
100 r_list_free (secs);
101 r_list_free (ret);
102 return NULL;
103 }
104 ptr->name = strdup ((char *)sec->name);
105 if (sec->id == R_BIN_WASM_SECTION_DATA || sec->id == R_BIN_WASM_SECTION_MEMORY) {
106 ptr->is_data = true;
107 }
108 ptr->size = sec->payload_len;
109 ptr->vsize = sec->payload_len;
110 ptr->vaddr = sec->offset;
111 ptr->paddr = sec->offset;
112 ptr->add = true;
113 // TODO permissions
114 ptr->perm = 0;
115 r_list_append (ret, ptr);
116 }
117 return ret;
118 }
119
symbols(RBinFile * bf)120 static RList *symbols(RBinFile *bf) {
121 RList *ret = NULL, *codes = NULL, *imports = NULL, *exports = NULL;
122 RBinSymbol *ptr = NULL;
123
124 if (!bf || !bf->o || !bf->o->bin_obj) {
125 return NULL;
126 }
127 RBinWasmObj *bin = bf->o->bin_obj;
128 if (!(ret = r_list_newf ((RListFree)free))) {
129 return NULL;
130 }
131 if (!(codes = r_bin_wasm_get_codes (bin))) {
132 goto bad_alloc;
133 }
134 if (!(imports = r_bin_wasm_get_imports (bin))) {
135 goto bad_alloc;
136 }
137 if (!(exports = r_bin_wasm_get_exports (bin))) {
138 goto bad_alloc;
139 }
140
141 ut32 fcn_idx = 0,
142 table_idx = 0,
143 mem_idx = 0,
144 global_idx = 0;
145
146 ut32 i = 0;
147 RBinWasmImportEntry *imp;
148 RListIter *iter;
149 r_list_foreach (imports, iter, imp) {
150 if (!(ptr = R_NEW0 (RBinSymbol))) {
151 goto bad_alloc;
152 }
153 ptr->name = strdup (imp->field_str);
154 ptr->libname = strdup (imp->module_str);
155 ptr->is_imported = true;
156 ptr->forwarder = "NONE";
157 ptr->bind = "NONE";
158 switch (imp->kind) {
159 case R_BIN_WASM_EXTERNALKIND_Function:
160 ptr->type = R_BIN_TYPE_FUNC_STR;
161 fcn_idx++;
162 break;
163 case R_BIN_WASM_EXTERNALKIND_Table:
164 ptr->type = "TABLE";
165 table_idx++;
166 break;
167 case R_BIN_WASM_EXTERNALKIND_Memory:
168 ptr->type = "MEMORY";
169 mem_idx++;
170 break;
171 case R_BIN_WASM_EXTERNALKIND_Global:
172 ptr->type = R_BIN_BIND_GLOBAL_STR;
173 global_idx++;
174 break;
175 }
176 ptr->size = 0;
177 ptr->vaddr = -1;
178 ptr->paddr = -1;
179 ptr->ordinal = i;
180 i += 1;
181 r_list_append (ret, ptr);
182 }
183
184 RListIter *is_exp = NULL;
185 RBinWasmCodeEntry *func;
186 r_list_foreach (codes, iter, func) {
187 if (!(ptr = R_NEW0 (RBinSymbol))) {
188 goto bad_alloc;
189 }
190 const char *fcn_name = r_bin_wasm_get_function_name (bin, fcn_idx);
191 if (fcn_name) {
192 ptr->name = strdup (fcn_name);
193
194 is_exp = r_list_find (exports, &fcn_idx, (RListComparator)find_export);
195 if (is_exp) {
196 ptr->bind = R_BIN_BIND_GLOBAL_STR;
197 }
198 } else {
199 // fallback if symbol is not found.
200 ptr->name = r_str_newf ("fcn.%d", fcn_idx);
201 }
202
203 ptr->forwarder = "NONE";
204 if (!ptr->bind) {
205 ptr->bind = "NONE";
206 }
207 ptr->type = R_BIN_TYPE_FUNC_STR;
208 ptr->size = func->len;
209 ptr->vaddr = (ut64)func->code;
210 ptr->paddr = (ut64)func->code;
211 ptr->ordinal = i;
212 i++;
213 fcn_idx++;
214 r_list_append (ret, ptr);
215 }
216
217 // TODO: globals, tables and memories
218 return ret;
219 bad_alloc:
220 // not so sure if imports should be freed.
221 r_list_free (exports);
222 r_list_free (codes);
223 r_list_free (ret);
224 return NULL;
225 }
226
imports(RBinFile * bf)227 static RList *imports (RBinFile *bf) {
228 RBinWasmObj *bin = NULL;
229 RList *imports = NULL;
230 RBinImport *ptr = NULL;
231 RList *ret = NULL;
232
233 if (!bf || !bf->o || !bf->o->bin_obj) {
234 return NULL;
235 }
236 bin = bf->o->bin_obj;
237 if (!(ret = r_list_newf (r_bin_import_free))) {
238 return NULL;
239 }
240 if (!(imports = r_bin_wasm_get_imports (bin))) {
241 goto bad_alloc;
242 }
243
244 RBinWasmImportEntry *import = NULL;
245 ut32 i = 0;
246 RListIter *iter;
247 r_list_foreach (imports, iter, import) {
248 if (!(ptr = R_NEW0 (RBinImport))) {
249 goto bad_alloc;
250 }
251 ptr->name = strdup (import->field_str);
252 ptr->classname = strdup (import->module_str);
253 ptr->ordinal = i;
254 ptr->bind = "NONE";
255 switch (import->kind) {
256 case R_BIN_WASM_EXTERNALKIND_Function:
257 ptr->type = "FUNC";
258 break;
259 case R_BIN_WASM_EXTERNALKIND_Table:
260 ptr->type = "TABLE";
261 break;
262 case R_BIN_WASM_EXTERNALKIND_Memory:
263 ptr->type = "MEM";
264 break;
265 case R_BIN_WASM_EXTERNALKIND_Global:
266 ptr->type = "GLOBAL";
267 break;
268 }
269 r_list_append (ret, ptr);
270 }
271 return ret;
272 bad_alloc:
273 r_list_free (imports);
274 r_list_free (ret);
275 return NULL;
276 }
277
libs(RBinFile * bf)278 static RList *libs (RBinFile *bf) {
279 return NULL;
280 }
281
info(RBinFile * bf)282 static RBinInfo *info (RBinFile *bf) {
283 RBinInfo *ret = NULL;
284
285 if (!(ret = R_NEW0 (RBinInfo))) {
286 return NULL;
287 }
288 ret->file = strdup (bf->file);
289 ret->bclass = strdup ("module");
290 ret->rclass = strdup ("wasm");
291 ret->os = strdup ("WebAssembly");
292 ret->arch = strdup ("wasm");
293 ret->machine = strdup (ret->arch);
294 ret->subsystem = strdup ("wasm");
295 ret->type = strdup ("EXEC");
296 ret->bits = 32;
297 ret->has_va = 0;
298 ret->big_endian = false;
299 ret->dbg_info = 0;
300 return ret;
301 }
302
size(RBinFile * bf)303 static ut64 size (RBinFile *bf) {
304 if (!bf || !bf->buf) {
305 return 0;
306 }
307 return r_buf_size (bf->buf);
308 }
309
310 /* inspired in http://www.phreedom.org/solar/code/tinype/tiny.97/tiny.asm */
create(RBin * bin,const ut8 * code,int codelen,const ut8 * data,int datalen,RBinArchOptions * opt)311 static RBuffer *create (RBin *bin, const ut8 *code, int codelen, const ut8 *data, int datalen, RBinArchOptions *opt) {
312 RBuffer *buf = r_buf_new ();
313 #define B(x, y) r_buf_append_bytes (buf, (const ut8 *)(x), y)
314 #define D(x) r_buf_append_ut32 (buf, x)
315 B ("\x00" "asm", 4);
316 B ("\x01\x00\x00\x00", 4);
317 return buf;
318 }
319
get_fcn_offset_from_id(RBinFile * bf,int fcn_idx)320 static int get_fcn_offset_from_id(RBinFile *bf, int fcn_idx) {
321 RBinWasmObj *bin = bf->o->bin_obj;
322 RList *codes = r_bin_wasm_get_codes (bin);
323 if (codes) {
324 RBinWasmCodeEntry *func = r_list_get_n (codes, fcn_idx);
325 if (func) {
326 return func->code;
327 }
328 }
329 return -1;
330 }
331
getoffset(RBinFile * bf,int type,int idx)332 static int getoffset(RBinFile *bf, int type, int idx) {
333 switch (type) {
334 case 'f': // fcnid -> fcnaddr
335 return get_fcn_offset_from_id (bf, idx);
336 }
337 return -1;
338 }
339
getname(RBinFile * bf,int type,int idx,bool sd)340 static const char *getname(RBinFile *bf, int type, int idx, bool sd) {
341 RBinWasmObj *bin = bf->o->bin_obj;
342 switch (type) {
343 case 'f': // fcnidx
344 {
345 const char *r = r_bin_wasm_get_function_name (bin, idx);
346 return r? strdup (r): NULL;
347 }
348 }
349 return NULL;
350 }
351
352 RBinPlugin r_bin_plugin_wasm = {
353 .name = "wasm",
354 .desc = "WebAssembly bin plugin",
355 .license = "MIT",
356 .load_buffer = &load_buffer,
357 .size = &size,
358 .destroy = &destroy,
359 .check_buffer = &check_buffer,
360 .baddr = &baddr,
361 .binsym = &binsym,
362 .entries = &entries,
363 .sections = §ions,
364 .symbols = &symbols,
365 .imports = &imports,
366 .info = &info,
367 .libs = &libs,
368 .get_offset = &getoffset,
369 .get_name = &getname,
370 .create = &create,
371 };
372
373 #ifndef R2_PLUGIN_INCORE
374 R_API RLibStruct radare_plugin = {
375 .type = R_LIB_TYPE_BIN,
376 .data = &r_bin_plugin_wasm,
377 .version = R2_VERSION
378 };
379 #endif
380