1 /* radare - LGPL - Copyright 2015-2018 nodepad, pancake */
2 
3 #include "mz.h"
4 #include <r_list.h>
5 
r_bin_mz_va_to_la(const ut16 segment,const ut16 offset)6 static ut64 r_bin_mz_va_to_la(const ut16 segment, const ut16 offset) {
7 	return (segment << 4) + offset;
8 }
9 
r_bin_mz_la_to_pa(const struct r_bin_mz_obj_t * bin,ut64 la)10 static ut64 r_bin_mz_la_to_pa(const struct r_bin_mz_obj_t *bin, ut64 la) {
11 	return la + (bin->dos_header->header_paragraphs << 4);
12 }
13 
r_bin_mz_get_entrypoint(const struct r_bin_mz_obj_t * bin)14 RBinAddr *r_bin_mz_get_entrypoint (const struct r_bin_mz_obj_t *bin) {
15 	const MZ_image_dos_header *mz;
16 	ut64 la;
17 	RBinAddr *entrypoint;
18 
19 	if (!bin || !bin->dos_header) {
20 		return NULL;
21 	}
22 
23 	mz = bin->dos_header;
24 	la = r_bin_mz_va_to_la (mz->cs, mz->ip);
25 	la &= 0xfffff;
26 	if (la >= bin->load_module_size) {
27 		eprintf ("Error: entry point outside load module\n");
28 		return NULL;
29 	}
30 	entrypoint = R_NEW0 (RBinAddr);
31 	if (entrypoint) {
32 		entrypoint->vaddr = la;
33 		entrypoint->paddr = r_bin_mz_la_to_pa (bin, la);
34 	}
35 
36 	return entrypoint;
37 }
38 
cmp_sections(const void * a,const void * b)39 static int cmp_sections(const void *a, const void *b) {
40 	const RBinSection *s_a, *s_b;
41 
42 	s_a = a;
43 	s_b = b;
44 
45 	return s_a->vaddr - s_b->vaddr;
46 }
47 
r_bin_mz_init_section(const struct r_bin_mz_obj_t * bin,ut64 laddr)48 static RBinSection *r_bin_mz_init_section(const struct r_bin_mz_obj_t *bin,
49 					  ut64 laddr) {
50 	RBinSection *section;
51 
52 	section = R_NEW0 (RBinSection);
53 	if (section) {
54 		section->vaddr = laddr;
55 	}
56 
57 	return section;
58 }
59 
r_bin_mz_get_segments(const struct r_bin_mz_obj_t * bin)60 RList *r_bin_mz_get_segments (const struct r_bin_mz_obj_t *bin) {
61 	RList *seg_list;
62 	RListIter *iter;
63 	RBinSection *section;
64 	MZ_image_relocation_entry *relocs;
65 	int i, num_relocs, section_number;
66 	ut16 ss;
67 
68 	if (!bin || !bin->dos_header) {
69 		return NULL;
70 	}
71 
72 	seg_list = r_list_newf (free);
73 	if (!seg_list) {
74 		return NULL;
75 	}
76 
77 	/* Add address of first segment to make sure that it is present
78 	 * even if there are no relocations or there isn't first segment in
79 	 * the relocations. */
80 	section = r_bin_mz_init_section (bin, 0);
81 	if (!section) {
82 		goto err_out;
83 	}
84 	r_list_add_sorted (seg_list, section, cmp_sections);
85 
86 	relocs = bin->relocation_entries;
87 	num_relocs = bin->dos_header->num_relocs;
88 	for (i = 0; i < num_relocs; i++) {
89 		RBinSection c;
90 		ut64 laddr, paddr, section_laddr;
91 		ut16 curr_seg;
92 
93 		laddr = r_bin_mz_va_to_la (relocs[i].segment, relocs[i].offset);
94 		if ((laddr + 2) >= bin->load_module_size) {
95 			continue;
96 		}
97 
98 		paddr = r_bin_mz_la_to_pa (bin, laddr);
99 		if (r_buf_size (bin->b) < paddr + 2) {
100 			continue;
101 		}
102 		curr_seg = r_buf_read_le16_at (bin->b, paddr);
103 
104 		section_laddr = r_bin_mz_va_to_la (curr_seg, 0);
105 		if (section_laddr > bin->load_module_size) {
106 			continue;
107 		}
108 
109 		c.vaddr = section_laddr;
110 		if (r_list_find (seg_list, &c, cmp_sections)) {
111 			continue;
112 		}
113 
114 		section = r_bin_mz_init_section (bin, section_laddr);
115 		if (!section) {
116 			goto err_out;
117 		}
118 		r_list_add_sorted (seg_list, section, cmp_sections);
119 	}
120 
121 	/* Add address of stack segment if it's inside the load module. */
122 	ss = bin->dos_header->ss;
123 	if (r_bin_mz_va_to_la (ss, 0) < bin->load_module_size) {
124 		section = r_bin_mz_init_section (bin, r_bin_mz_va_to_la (ss, 0));
125 		if (!section) {
126 			goto err_out;
127 		}
128 		r_list_add_sorted (seg_list, section, cmp_sections);
129 	}
130 
131 	/* Fixup sizes and addresses, set name, permissions and set add flag */
132 	section_number = 0;
133 	r_list_foreach (seg_list, iter, section) {
134 		section->name = r_str_newf ("seg_%03d", section_number);
135 		if (section_number) {
136 			RBinSection *p_section = iter->p->data;
137 			p_section->size = section->vaddr - p_section->vaddr;
138 			p_section->vsize = p_section->size;
139 		}
140 		section->vsize = section->size;
141 		section->paddr = r_bin_mz_la_to_pa (bin, section->vaddr);
142 		section->perm = r_str_rwx ("rwx");
143 		section->add = true;
144 		section_number++;
145 	}
146 	section = r_list_get_top (seg_list);
147 	section->size = bin->load_module_size - section->vaddr;
148 	section->vsize = section->size;
149 
150 	return seg_list;
151 
152 err_out:
153 	eprintf ("Error: alloc (RBinSection)\n");
154 	r_list_free (seg_list);
155 
156 	return NULL;
157 }
158 
r_bin_mz_get_relocs(const struct r_bin_mz_obj_t * bin)159 struct r_bin_mz_reloc_t *r_bin_mz_get_relocs (const struct r_bin_mz_obj_t *bin) {
160 	int i, j;
161 	const int num_relocs = bin->dos_header->num_relocs;
162 	const MZ_image_relocation_entry *const rel_entry = bin->relocation_entries;
163 
164 	struct r_bin_mz_reloc_t *relocs = calloc (num_relocs + 1, sizeof (*relocs));
165 	if (!relocs) {
166 		eprintf ("Error: calloc (struct r_bin_mz_reloc_t)\n");
167 		return NULL;
168 	}
169 	for (i = 0, j = 0; i < num_relocs; i++) {
170 		relocs[j].vaddr = r_bin_mz_va_to_la (rel_entry[i].segment,
171 			rel_entry[i].offset);
172 		relocs[j].paddr = r_bin_mz_la_to_pa (bin, relocs[j].vaddr);
173 
174 		/* Add only relocations which resides inside dos executable */
175 		if (relocs[j].vaddr < bin->load_module_size) {
176 			j++;
177 		}
178 	}
179 	relocs[j].last = 1;
180 
181 	return relocs;
182 }
183 
r_bin_mz_free(struct r_bin_mz_obj_t * bin)184 void *r_bin_mz_free (struct r_bin_mz_obj_t *bin) {
185 	if (!bin) {
186 		return NULL;
187 	}
188 	free ((void *)bin->dos_header);
189 	free ((void *)bin->dos_extended_header);
190 	free ((void *)bin->relocation_entries);
191 	r_buf_free (bin->b);
192 	bin->b = NULL;
193 	free (bin);
194 	return NULL;
195 }
196 
r_bin_mz_init_hdr(struct r_bin_mz_obj_t * bin)197 static int r_bin_mz_init_hdr(struct r_bin_mz_obj_t *bin) {
198 	int relocations_size, dos_file_size;
199 	MZ_image_dos_header *mz;
200 	if (!(mz = R_NEW0 (MZ_image_dos_header))) {
201 		r_sys_perror ("malloc (MZ_image_dos_header)");
202 		return false;
203 	}
204 	bin->dos_header = mz;
205 	// TODO: read field by field to avoid endian and alignment issues
206 	if (r_buf_read_at (bin->b, 0, (ut8 *)mz, sizeof (*mz)) == -1) {
207 		eprintf ("Error: read (MZ_image_dos_header)\n");
208 		return false;
209 	}
210 	// dos_header is not endian safe here in this point
211 	if (mz->blocks_in_file < 1) {
212 		return false;
213 	}
214 	dos_file_size = ((mz->blocks_in_file - 1) << 9) +
215 		mz->bytes_in_last_block;
216 
217 	bin->dos_file_size = dos_file_size;
218 	if (dos_file_size > bin->size) {
219 		return false;
220 	}
221 	bin->load_module_size = dos_file_size - (mz->header_paragraphs << 4);
222 	relocations_size = mz->num_relocs * sizeof (MZ_image_relocation_entry);
223 	if ((mz->reloc_table_offset + relocations_size) > bin->size) {
224 		return false;
225 	}
226 
227 	sdb_num_set (bin->kv, "mz.initial.cs", mz->cs, 0);
228 	sdb_num_set (bin->kv, "mz.initial.ip", mz->ip, 0);
229 	sdb_num_set (bin->kv, "mz.initial.ss", mz->ss, 0);
230 	sdb_num_set (bin->kv, "mz.initial.sp", mz->sp, 0);
231 	sdb_num_set (bin->kv, "mz.overlay_number", mz->overlay_number, 0);
232 	sdb_num_set (bin->kv, "mz.dos_header.offset", 0, 0);
233 	sdb_set (bin->kv, "mz.dos_header.format", "[2]zwwwwwwwwwwwww"
234 						  " signature bytes_in_last_block blocks_in_file num_relocs "
235 						  " header_paragraphs min_extra_paragraphs max_extra_paragraphs "
236 						  " ss sp checksum ip cs reloc_table_offset overlay_number ",
237 		0);
238 
239 	bin->dos_extended_header_size = mz->reloc_table_offset -
240 		sizeof (MZ_image_dos_header);
241 
242 	if (bin->dos_extended_header_size > 0) {
243 		if (!(bin->dos_extended_header =
244 				    malloc (bin->dos_extended_header_size))) {
245 			r_sys_perror ("malloc (dos extended header)");
246 			return false;
247 		}
248 		if (r_buf_read_at (bin->b, sizeof (MZ_image_dos_header),
249 			    (ut8 *)bin->dos_extended_header,
250 			    bin->dos_extended_header_size) == -1) {
251 			eprintf ("Error: read (dos extended header)\n");
252 			return false;
253 		}
254 	}
255 
256 	if (relocations_size > 0) {
257 		if (!(bin->relocation_entries = malloc (relocations_size))) {
258 			r_sys_perror ("malloc (dos relocation entries)");
259 			return false;
260 		}
261 		if (r_buf_read_at (bin->b, bin->dos_header->reloc_table_offset,
262 			    (ut8 *)bin->relocation_entries, relocations_size) == -1) {
263 			eprintf ("Error: read (dos relocation entries)\n");
264 			R_FREE (bin->relocation_entries);
265 			return false;
266 		}
267 	}
268 	return true;
269 }
270 
r_bin_mz_init(struct r_bin_mz_obj_t * bin)271 static bool r_bin_mz_init(struct r_bin_mz_obj_t *bin) {
272 	bin->dos_header = NULL;
273 	bin->dos_extended_header = NULL;
274 	bin->relocation_entries = NULL;
275 	bin->kv = sdb_new0 ();
276 	if (!r_bin_mz_init_hdr (bin)) {
277 		eprintf ("Warning: File is not MZ\n");
278 		return false;
279 	}
280 	return true;
281 }
282 
r_bin_mz_new(const char * file)283 struct r_bin_mz_obj_t *r_bin_mz_new (const char *file) {
284 	struct r_bin_mz_obj_t *bin = R_NEW0 (struct r_bin_mz_obj_t);
285 	if (!bin) {
286 		return NULL;
287 	}
288 	bin->file = file;
289 	size_t binsz;
290 	ut8 *buf = (ut8*)r_file_slurp (file, &binsz);
291 	bin->size = binsz;
292 	if (!buf) {
293 		return r_bin_mz_free (bin);
294 	}
295 	bin->b = r_buf_new ();
296 	if (!r_buf_set_bytes (bin->b, buf, bin->size)) {
297 		free ((void *)buf);
298 		return r_bin_mz_free (bin);
299 	}
300 	free ((void *)buf);
301 	if (!r_bin_mz_init (bin)) {
302 		return r_bin_mz_free (bin);
303 	}
304 	return bin;
305 }
306 
r_bin_mz_new_buf(RBuffer * buf)307 struct r_bin_mz_obj_t *r_bin_mz_new_buf(RBuffer *buf) {
308 	struct r_bin_mz_obj_t *bin = R_NEW0 (struct r_bin_mz_obj_t);
309 	if (!bin) {
310 		return NULL;
311 	}
312 	bin->b = r_buf_new_with_buf (buf);
313 	if (!bin->b) {
314 		return r_bin_mz_free (bin);
315 	}
316 	bin->size = r_buf_size (buf);
317 	return r_bin_mz_init (bin)? bin: r_bin_mz_free (bin);
318 }
319 
r_bin_mz_get_main_vaddr(struct r_bin_mz_obj_t * bin)320 RBinAddr *r_bin_mz_get_main_vaddr (struct r_bin_mz_obj_t *bin) {
321 	int n;
322 	ut8 b[512];
323 	if (!bin || !bin->b) {
324 		return NULL;
325 	}
326 	RBinAddr *entry = r_bin_mz_get_entrypoint (bin);
327 	if (!entry) {
328 		return NULL;
329 	}
330 	ZERO_FILL (b);
331 	if (r_buf_read_at (bin->b, entry->paddr, b, sizeof (b)) < 0) {
332 		eprintf ("Warning: Cannot read entry at 0x%16" PFMT64x "\n", (ut64)entry->paddr);
333 		free (entry);
334 		return NULL;
335 	}
336 	// MSVC
337 	if (b[0] == 0xb4 && b[1] == 0x30) {
338 		// ff 36 XX XX			push	XXXX
339 		// ff 36 XX XX			push	argv
340 		// ff 36 XX XX			push	argc
341 		// 9a XX XX XX XX		lcall	_main
342 		// 50				push	ax
343 		for (n = 0; n < sizeof (b) - 18; n++) {
344 			if (b[n] == 0xff && b[n + 4] == 0xff && b[n + 8] == 0xff && b[n + 12] == 0x9a && b[n + 17] == 0x50) {
345 				const ut16 call_addr = r_read_ble16 (b + n + 13, 0);
346 				const ut16 call_seg = r_read_ble16 (b + n + 15, 0);
347 				entry->vaddr = r_bin_mz_va_to_la (call_seg, call_addr);
348 				entry->paddr = r_bin_mz_la_to_pa (bin, entry->vaddr);
349 				return entry;
350 			}
351 		}
352 	}
353 
354 	R_FREE (entry);
355 	return NULL;
356 }
357