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