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 = &sections,
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