1/* radare - LGPL - Copyright 2009-2019 - nibble, pancake, alvarofe */
2
3#include <r_types.h>
4#include <r_util.h>
5#include <r_lib.h>
6#include <r_bin.h>
7#include "../i/private.h"
8#include "pe/pe.h"
9
10static Sdb* get_sdb (RBinFile *bf) {
11	RBinObject *o = bf->o;
12	struct PE_(r_bin_pe_obj_t) *bin;
13	if (!o || !o->bin_obj) {
14		return NULL;
15	}
16	bin = (struct PE_(r_bin_pe_obj_t) *) o->bin_obj;
17	return bin? bin->kv: NULL;
18}
19
20static bool load_buffer(RBinFile *bf, void **bin_obj, RBuffer *buf, ut64 loadaddr, Sdb *sdb) {
21	r_return_val_if_fail (bf && bin_obj && buf, false);
22	struct PE_(r_bin_pe_obj_t) *res = PE_(r_bin_pe_new_buf) (buf, bf->rbin->verbose);
23	if (res) {
24		sdb_ns_set (sdb, "info", res->kv);
25		*bin_obj = res;
26		return true;
27	}
28	return false;
29}
30
31static void destroy(RBinFile *bf) {
32	PE_(r_bin_pe_free) ((struct PE_(r_bin_pe_obj_t)*)bf->o->bin_obj);
33}
34
35static ut64 baddr(RBinFile *bf) {
36	return PE_(r_bin_pe_get_image_base) (bf->o->bin_obj);
37}
38
39static RBinAddr* binsym(RBinFile *bf, int type) {
40	struct r_bin_pe_addr_t *peaddr = NULL;
41	RBinAddr *ret = NULL;
42	if (bf && bf->o && bf->o->bin_obj) {
43		switch (type) {
44		case R_BIN_SYM_MAIN:
45				peaddr = PE_(r_bin_pe_get_main_vaddr) (bf->o->bin_obj);
46				break;
47		}
48	}
49	if (peaddr && (ret = R_NEW0 (RBinAddr))) {
50		ret->paddr = peaddr->paddr;
51		ret->vaddr = peaddr->vaddr;
52	}
53	free (peaddr);
54	return ret;
55}
56
57static void add_tls_callbacks(RBinFile *bf, RList* list) {
58	PE_DWord paddr, vaddr, haddr;
59	int count = 0;
60	RBinAddr *ptr = NULL;
61	struct PE_(r_bin_pe_obj_t) *bin = (struct PE_(r_bin_pe_obj_t) *) (bf->o->bin_obj);
62	char *key;
63
64	do {
65		key =  sdb_fmt ("pe.tls_callback%d_paddr", count);
66		paddr = sdb_num_get (bin->kv, key, 0);
67		if (!paddr) {
68			break;
69		}
70
71		key =  sdb_fmt ("pe.tls_callback%d_vaddr", count);
72		vaddr = sdb_num_get (bin->kv, key, 0);
73		if (!vaddr) {
74			break;
75		}
76
77		key =  sdb_fmt ("pe.tls_callback%d_haddr", count);
78		haddr = sdb_num_get (bin->kv, key, 0);
79		if (!haddr) {
80			break;
81		}
82		if ((ptr = R_NEW0 (RBinAddr))) {
83			ptr->paddr  = paddr;
84			ptr->vaddr  = vaddr;
85			ptr->hpaddr = haddr;
86			ptr->type   = R_BIN_ENTRY_TYPE_TLS;
87			r_list_append (list, ptr);
88		}
89		count++;
90	} while (vaddr);
91}
92
93static RList* entries(RBinFile *bf) {
94	struct r_bin_pe_addr_t *entry = NULL;
95	RBinAddr *ptr = NULL;
96	RList* ret;
97
98	if (!(ret = r_list_newf (free))) {
99		return NULL;
100	}
101	if (!(entry = PE_(r_bin_pe_get_entrypoint) (bf->o->bin_obj))) {
102		return ret;
103	}
104	if ((ptr = R_NEW0 (RBinAddr))) {
105		ptr->paddr  = entry->paddr;
106		ptr->vaddr  = entry->vaddr;
107		ptr->hpaddr = entry->haddr;
108		ptr->type   = R_BIN_ENTRY_TYPE_PROGRAM;
109		r_list_append (ret, ptr);
110	}
111	free (entry);
112	// get TLS callback addresses
113	add_tls_callbacks (bf, ret);
114
115	return ret;
116}
117
118static RList* sections(RBinFile *bf) {
119	RList *ret = NULL;
120	RBinSection *ptr = NULL;
121	struct r_bin_pe_section_t *sections = NULL;
122	struct PE_(r_bin_pe_obj_t) *bin = (struct PE_(r_bin_pe_obj_t)*)bf->o->bin_obj;
123	ut64 ba = baddr (bf);
124	int i;
125	if (!(ret = r_list_newf ((RListFree)r_bin_section_free))) {
126		return NULL;
127	}
128	if (!bin || !(sections = bin->sections)){
129		r_list_free (ret);
130		return NULL;
131	}
132	PE_(r_bin_pe_check_sections) (bin, &sections);
133	for (i = 0; !sections[i].last; i++) {
134		if (!(ptr = R_NEW0 (RBinSection))) {
135			break;
136		}
137		if (sections[i].name[0]) {
138			ptr->name = strdup ((char*)sections[i].name);
139		} else {
140			ptr->name = strdup ("");
141		}
142		ptr->size = sections[i].size;
143		if (ptr->size > bin->size) {
144			if (sections[i].vsize < bin->size) {
145				ptr->size = sections[i].vsize;
146			} else {
147				//hack give it page size
148				ptr->size = 4096;
149			}
150		}
151		ptr->vsize = sections[i].vsize;
152		if (!ptr->vsize && ptr->size) {
153			ptr->vsize = ptr->size;
154		}
155		ptr->paddr = sections[i].paddr;
156		ptr->vaddr = sections[i].vaddr + ba;
157		ptr->add = true;
158		ptr->perm = 0;
159		if (R_BIN_PE_SCN_IS_EXECUTABLE (sections[i].perm)) {
160			ptr->perm |= R_PERM_X;
161			ptr->perm |= R_PERM_R; // implicit
162		}
163		if (R_BIN_PE_SCN_IS_WRITABLE (sections[i].perm)) {
164			ptr->perm |= R_PERM_W;
165		}
166		if (R_BIN_PE_SCN_IS_READABLE (sections[i].perm)) {
167			ptr->perm |= R_PERM_R;
168		}
169		// this is causing may tests to fail because rx != srx
170		if (R_BIN_PE_SCN_IS_SHAREABLE (sections[i].perm)) {
171			ptr->perm |= R_PERM_SHAR;
172		}
173		if ((ptr->perm & R_PERM_RW) && !(ptr->perm & R_PERM_X) && ptr->size > 0) {
174			if (!strcmp (ptr->name, ".rsrc") ||
175			  	!strcmp (ptr->name, ".data") ||
176				!strcmp (ptr->name, ".rdata")) {
177					ptr->is_data = true;
178				}
179		}
180		r_list_append (ret, ptr);
181	}
182	return ret;
183}
184
185static void find_pe_overlay(RBinFile *bf) {
186	ut64 pe_overlay_size;
187	ut64 pe_overlay_offset = PE_(bin_pe_get_overlay) (bf->o->bin_obj, &pe_overlay_size);
188	if (pe_overlay_offset) {
189		sdb_num_set (bf->sdb, "pe_overlay.offset", pe_overlay_offset, 0);
190		sdb_num_set (bf->sdb, "pe_overlay.size", pe_overlay_size, 0);
191	}
192}
193
194static RList* symbols(RBinFile *bf) {
195	RList *ret = NULL;
196	RBinSymbol *ptr = NULL;
197	struct r_bin_pe_export_t *symbols = NULL;
198	struct r_bin_pe_import_t *imports = NULL;
199	int i;
200
201	if (!(ret = r_list_newf (free))) {
202		return NULL;
203	}
204	if ((symbols = PE_(r_bin_pe_get_exports)(bf->o->bin_obj))) {
205		for (i = 0; !symbols[i].last; i++) {
206			if (!(ptr = R_NEW0 (RBinSymbol))) {
207				break;
208			}
209			ptr->name = strdup ((char *)symbols[i].name);
210			ptr->libname = *symbols[i].libname ? strdup ((char *)symbols[i].libname) : NULL;
211			ptr->forwarder = r_str_constpool_get (&bf->rbin->constpool, (char *)symbols[i].forwarder);
212			//strncpy (ptr->bind, "NONE", R_BIN_SIZEOF_STRINGS);
213			ptr->bind = R_BIN_BIND_GLOBAL_STR;
214			ptr->type = R_BIN_TYPE_FUNC_STR;
215			ptr->size = 0;
216			ptr->vaddr = symbols[i].vaddr;
217			ptr->paddr = symbols[i].paddr;
218			ptr->ordinal = symbols[i].ordinal;
219			r_list_append (ret, ptr);
220		}
221		free (symbols);
222	}
223
224
225	if ((imports = PE_(r_bin_pe_get_imports)(bf->o->bin_obj))) {
226		for (i = 0; !imports[i].last; i++) {
227			if (!(ptr = R_NEW0 (RBinSymbol))) {
228				break;
229			}
230			//strncpy (ptr->name, (char*)symbols[i].name, R_BIN_SIZEOF_STRINGS);
231			ptr->name = strdup ((const char *)imports[i].name);
232			ptr->libname = strdup ((const char *)imports[i].libname);
233			ptr->is_imported = true;
234			//strncpy (ptr->forwarder, (char*)imports[i].forwarder, R_BIN_SIZEOF_STRINGS);
235			ptr->bind = "NONE";
236			ptr->type = R_BIN_TYPE_FUNC_STR;
237			ptr->size = 0;
238			ptr->vaddr = imports[i].vaddr;
239			ptr->paddr = imports[i].paddr;
240			ptr->ordinal = imports[i].ordinal;
241			r_list_append (ret, ptr);
242		}
243		free (imports);
244	}
245	find_pe_overlay(bf);
246	return ret;
247}
248
249static void filter_import(ut8 *n) {
250	int I;
251	for (I = 0; n[I]; I++) {
252		if (n[I] < 30 || n[I] >= 0x7f) {
253			n[I] = 0;
254			break;
255		}
256	}
257}
258
259static RList* imports(RBinFile *bf) {
260	RList *ret = NULL, *relocs = NULL;
261	RBinImport *ptr = NULL;
262	RBinReloc *rel = NULL;
263	struct r_bin_pe_import_t *imports = NULL;
264	int i;
265
266	if (!bf || !bf->o || !bf->o->bin_obj) {
267		return NULL;
268	}
269	if (!(ret = r_list_newf (r_bin_import_free))) {
270		return NULL;
271	}
272
273	// XXX: has_canary is causing problems! thus we need to check and clean here until it is fixed!
274	if (((struct PE_(r_bin_pe_obj_t)*)bf->o->bin_obj)->relocs) {
275		r_list_free (((struct PE_(r_bin_pe_obj_t)*)bf->o->bin_obj)->relocs);
276	}
277
278	if (!(relocs = r_list_newf (free))) {
279		free (ret);
280		return NULL;
281	}
282	((struct PE_(r_bin_pe_obj_t)*)bf->o->bin_obj)->relocs = relocs;
283
284	if (!(imports = PE_(r_bin_pe_get_imports)(bf->o->bin_obj))) {
285		return ret;
286	}
287	for (i = 0; !imports[i].last; i++) {
288		if (!(ptr = R_NEW0 (RBinImport))) {
289			break;
290		}
291		filter_import (imports[i].name);
292		ptr->name = strdup ((char*)imports[i].name);
293		ptr->libname = strdup ((char*)imports[i].libname);
294		ptr->bind = "NONE";
295		ptr->type = "FUNC";
296		ptr->ordinal = imports[i].ordinal;
297		// NOTE(eddyb) a PE hint is just an optional possible DLL export table
298		// index for the import. There is no point in exposing it.
299		//ptr->hint = imports[i].hint;
300		r_list_append (ret, ptr);
301
302		if (!(rel = R_NEW0 (RBinReloc))) {
303			break;
304		}
305#ifdef R_BIN_PE64
306		rel->type = R_BIN_RELOC_64;
307#else
308		rel->type = R_BIN_RELOC_32;
309#endif
310		rel->additive = 0;
311		rel->import = ptr;
312		rel->addend = 0;
313		{
314			ut8 addr[4];
315			r_buf_read_at (bf->buf, imports[i].paddr, addr, 4);
316			ut64 newaddr = (ut64) r_read_le32 (&addr);
317			rel->vaddr = newaddr;
318		}
319		rel->paddr = imports[i].paddr;
320		r_list_append (relocs, rel);
321	}
322	free (imports);
323	return ret;
324}
325
326static RList* relocs(RBinFile *bf) {
327	struct PE_(r_bin_pe_obj_t)* obj= bf->o->bin_obj;
328	if (obj) {
329		return obj->relocs;
330	}
331	return NULL;
332}
333
334static RList* libs(RBinFile *bf) {
335	struct r_bin_pe_lib_t *libs = NULL;
336	RList *ret = NULL;
337	char *ptr = NULL;
338	int i;
339
340	if (!(ret = r_list_new ())) {
341		return NULL;
342	}
343	ret->free = free;
344	if (!(libs = PE_(r_bin_pe_get_libs)(bf->o->bin_obj))) {
345		return ret;
346	}
347	for (i = 0; !libs[i].last; i++) {
348		ptr = strdup (libs[i].name);
349		r_list_append (ret, ptr);
350	}
351	free (libs);
352	return ret;
353}
354
355static int is_dot_net(RBinFile *bf) {
356	struct r_bin_pe_lib_t *libs = NULL;
357	int i;
358	if (!(libs = PE_(r_bin_pe_get_libs)(bf->o->bin_obj))) {
359		return false;
360	}
361	for (i = 0; !libs[i].last; i++) {
362		if (!strcmp (libs[i].name, "mscoree.dll")) {
363			free (libs);
364			return true;
365		}
366	}
367	free (libs);
368	return false;
369}
370
371static int is_vb6(RBinFile *bf) {
372	struct r_bin_pe_lib_t *libs = NULL;
373	int i;
374	if (!(libs = PE_(r_bin_pe_get_libs)(bf->o->bin_obj))) {
375		return false;
376	}
377	for (i = 0; !libs[i].last; i++) {
378		if (!strcmp (libs[i].name, "msvbvm60.dll")) {
379			free (libs);
380			return true;
381		}
382	}
383	free (libs);
384	return false;
385}
386
387static int has_canary(RBinFile *bf) {
388	// XXX: We only need imports here but this causes leaks, we need to wait for the below. This is a horrible solution!
389	// TODO: use O(1) when imports sdbized
390	RListIter *iter;
391	struct PE_ (r_bin_pe_obj_t) *bin = bf->o->bin_obj;
392	if (bin) {
393		const RList* relocs_list = bin->relocs;
394		RBinReloc *rel;
395		if (relocs_list) {
396			r_list_foreach (relocs_list, iter, rel) {
397				if (!strcmp (rel->import->name, "__security_init_cookie")) {
398					return true;
399				}
400			}
401		}
402	} else {  // rabin2 needs this as it will not initialise bin
403		const RList* imports_list = imports (bf);
404		RBinImport *imp;
405		if (imports_list) {
406			r_list_foreach (imports_list, iter, imp) {
407				if (!strcmp (imp->name, "__security_init_cookie")) {
408					return true;
409				}
410			}
411		}
412	}
413	return false;
414}
415
416static int haschr(const RBinFile* bf, ut16 dllCharacteristic) {
417	const ut8 *buf;
418	unsigned int idx;
419	ut64 sz;
420	if (!bf) {
421		return false;
422	}
423	buf = r_buf_data (bf->buf, &sz);
424	if (!buf) {
425		return false;
426	}
427	idx = (buf[0x3c] | (buf[0x3d]<<8));
428	if (idx + 0x5E + 1 >= sz ) {
429		return false;
430	}
431	//it's funny here idx+0x5E can be 158 and sz 159 but with
432	//the cast it reads two bytes until 160
433	return ((*(ut16*)(buf + idx + 0x5E)) & dllCharacteristic);
434}
435
436static RBinInfo* info(RBinFile *bf) {
437	struct PE_ (r_bin_pe_obj_t) *bin;
438	SDebugInfo di = {{0}};
439	RBinInfo *ret = R_NEW0 (RBinInfo);
440	ut32 claimed_checksum, actual_checksum, pe_overlay;
441
442	if (!ret) {
443		return NULL;
444	}
445	bin = bf->o->bin_obj;
446	ret->file = strdup (bf->file);
447	ret->bclass = PE_(r_bin_pe_get_class) (bf->o->bin_obj);
448	ret->rclass = strdup ("pe");
449	ret->os = PE_(r_bin_pe_get_os) (bf->o->bin_obj);
450	ret->arch = PE_(r_bin_pe_get_arch) (bf->o->bin_obj);
451	ret->machine = PE_(r_bin_pe_get_machine) (bf->o->bin_obj);
452	ret->subsystem = PE_(r_bin_pe_get_subsystem) (bf->o->bin_obj);
453	ret->default_cc = PE_(r_bin_pe_get_cc) (bf->o->bin_obj);
454	if (is_dot_net (bf)) {
455		ret->lang = "cil";
456	}
457	if (is_vb6 (bf)) {
458		ret->lang = "vb";
459	}
460	if (PE_(r_bin_pe_is_dll) (bf->o->bin_obj)) {
461		ret->type = strdup ("DLL (Dynamic Link Library)");
462	} else {
463		ret->type = strdup ("EXEC (Executable file)");
464	}
465	claimed_checksum = PE_(bin_pe_get_claimed_checksum) (bf->o->bin_obj);
466	actual_checksum  = PE_(bin_pe_get_actual_checksum) (bf->o->bin_obj);
467	pe_overlay = sdb_num_get (bf->sdb, "pe_overlay.size", 0);
468	ret->bits = PE_(r_bin_pe_get_bits) (bf->o->bin_obj);
469	ret->big_endian = PE_(r_bin_pe_is_big_endian) (bf->o->bin_obj);
470	ret->dbg_info = 0;
471	ret->has_lit = true;
472	ret->has_canary = has_canary (bf);
473	ret->has_nx = haschr (bf, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT);
474	ret->has_pi = haschr (bf, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE);
475	ret->claimed_checksum = strdup (sdb_fmt ("0x%08x", claimed_checksum));
476	ret->actual_checksum  = strdup (sdb_fmt ("0x%08x", actual_checksum));
477	ret->pe_overlay = pe_overlay > 0;
478	ret->signature = bin ? bin->is_signed : false;
479	ret->file_hashes = r_list_newf ((RListFree) r_bin_file_hash_free);
480	Sdb *db = sdb_ns (bf->sdb, "pe", true);
481	sdb_bool_set (db, "canary", has_canary (bf), 0);
482	sdb_bool_set (db, "highva", haschr (bf, IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA), 0);
483	sdb_bool_set (db, "aslr", haschr (bf, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE), 0);
484	sdb_bool_set (db, "forceintegrity", haschr (bf, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY), 0);
485	sdb_bool_set (db, "nx", haschr (bf, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT), 0);
486	sdb_bool_set (db, "isolation", !haschr (bf, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY), 0);
487	sdb_bool_set (db, "seh", !haschr (bf, IMAGE_DLLCHARACTERISTICS_NO_SEH), 0);
488	sdb_bool_set (db, "bind", !haschr (bf, IMAGE_DLLCHARACTERISTICS_NO_BIND), 0);
489	sdb_bool_set (db, "appcontainer", haschr (bf, IMAGE_DLLCHARACTERISTICS_APPCONTAINER), 0);
490	sdb_bool_set (db, "wdmdriver", haschr (bf, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER), 0);
491	sdb_bool_set (db, "guardcf", haschr (bf, IMAGE_DLLCHARACTERISTICS_GUARD_CF), 0);
492	sdb_bool_set (db, "terminalserveraware", haschr (bf, IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE), 0);
493	sdb_num_set (db, "bits", ret->bits, 0);
494	sdb_set (db, "claimed_checksum", ret->claimed_checksum, 0);
495	sdb_set (db, "actual_checksum", ret->actual_checksum, 0);
496	sdb_bool_set (db, "is_authhash_valid", PE_(bin_pe_is_authhash_valid) (bf->o->bin_obj), 0);
497
498	ret->has_va = true;
499
500	if (PE_(r_bin_pe_is_stripped_debug) (bf->o->bin_obj)) {
501		ret->dbg_info |= R_BIN_DBG_STRIPPED;
502	}
503	if (PE_(r_bin_pe_is_stripped_line_nums) (bf->o->bin_obj)) {
504		ret->dbg_info |= R_BIN_DBG_LINENUMS;
505	}
506	if (PE_(r_bin_pe_is_stripped_local_syms) (bf->o->bin_obj)) {
507		ret->dbg_info |= R_BIN_DBG_SYMS;
508	}
509	if (PE_(r_bin_pe_is_stripped_relocs) (bf->o->bin_obj)) {
510		ret->dbg_info |= R_BIN_DBG_RELOCS;
511	}
512	if (PE_(r_bin_pe_get_debug_data)(bf->o->bin_obj, &di)) {
513		ret->guid = r_str_ndup (di.guidstr, GUIDSTR_LEN);
514		if (ret->guid) {
515			ret->debug_file_name = r_str_ndup (di.file_name, DBG_FILE_NAME_LEN);
516			if (!ret->debug_file_name) {
517				R_FREE (ret->guid);
518			}
519		}
520	}
521
522	return ret;
523}
524
525static ut64 get_vaddr (RBinFile *bf, ut64 baddr, ut64 paddr, ut64 vaddr) {
526	return baddr + vaddr;
527}
528
529static RList *compute_hashes(RBinFile *bf) {
530	RList *file_hashes = r_list_newf ((RListFree) r_bin_file_hash_free);
531	const char *authentihash = PE_(bin_pe_compute_authentihash) (bf->o->bin_obj);
532	if (authentihash) {
533		RBinFileHash *authhash = R_NEW0 (RBinFileHash);
534		if (authhash) {
535			authhash->type = strdup ("authentihash");
536			authhash->hex = authentihash;
537			r_list_push (file_hashes, authhash);
538		}
539	}
540
541	return file_hashes;
542}
543