1 /* radare - LGPL - Copyright 2019 - GustavoLCR */
2 
3 #include "ne.h"
4 
__get_target_os(r_bin_ne_obj_t * bin)5 static char *__get_target_os(r_bin_ne_obj_t *bin) {
6 	switch (bin->ne_header->targOS) {
7 	case 1:
8 		return "OS/2";
9 	case 2:
10 		return "Windows";
11 	case 3:
12 		return "European MS-DOS 4.x";
13 	case 4:
14 		return "Windows 386";
15 	case 5:
16 		return "BOSS (Borland Operating System Services)";
17 	default:
18 		return "Unknown";
19 	}
20 }
21 
__translate_perms(int flags)22 static int __translate_perms(int flags) {
23 	int perms = 0;
24 	if (flags & IS_RX) {
25 		if (flags & IS_DATA) {
26 			perms = R_PERM_R;
27 		} else {
28 			perms = R_PERM_X;
29 		}
30 	}
31 	if (!perms) {
32 		perms = R_PERM_RWX;
33 	}
34 	return perms;
35 }
36 
__read_nonnull_str_at(RBuffer * buf,ut64 offset)37 static char *__read_nonnull_str_at(RBuffer *buf, ut64 offset) {
38 	ut8 sz = r_buf_read8_at (buf, offset);
39 	if (!sz) {
40 		return NULL;
41 	}
42 	char *str = malloc ((ut64)sz + 1);
43 	if (!str) {
44 		return NULL;
45 	}
46 	r_buf_read_at (buf, offset + 1, (ut8 *)str, sz);
47 	str[sz] = '\0';
48 	return str;
49 }
50 
__func_name_from_ord(char * module,ut16 ordinal)51 static char *__func_name_from_ord(char *module, ut16 ordinal) {
52 	char *path = r_str_newf (R_JOIN_4_PATHS ("%s", R2_SDB_FORMAT, "dll", "%s.sdb"), r_sys_prefix (NULL), module);
53 	char *ord = r_str_newf ("%d", ordinal);
54 	char *name;
55 	if (r_file_exists (path)) {
56 		Sdb *sdb = sdb_new (NULL, path, 0);
57 		name = sdb_get (sdb, ord, NULL);
58 		if (!name) {
59 			name = ord;
60 		} else {
61 			free (ord);
62 		}
63 		sdb_close (sdb);
64 		free (sdb);
65 	} else {
66 		name = ord;
67 	}
68 	return name;
69 }
70 
r_bin_ne_get_segments(r_bin_ne_obj_t * bin)71 RList *r_bin_ne_get_segments(r_bin_ne_obj_t *bin) {
72 	int i;
73 	if (!bin) {
74 		return NULL;
75 	}
76 	RList *segments = r_list_newf (free);
77 	for (i = 0; i < bin->ne_header->SegCount; i++) {
78 		RBinSection *bs = R_NEW0 (RBinSection);
79 		NE_image_segment_entry *se = &bin->segment_entries[i];
80 		if (!bs) {
81 			return segments;
82 		}
83 		bs->size = se->length;
84 		bs->vsize = se->minAllocSz ? se->minAllocSz : 64000;
85 		bs->bits = R_SYS_BITS_16;
86 		bs->is_data = se->flags & IS_DATA;
87 		bs->perm = __translate_perms (se->flags);
88 		bs->paddr = (ut64)se->offset * bin->alignment;
89 		bs->name = r_str_newf ("%s.%" PFMT64d, se->flags & IS_MOVEABLE ? "MOVEABLE" : "FIXED", bs->paddr);
90 		bs->is_segment = true;
91 		r_list_append (segments, bs);
92 	}
93 	bin->segments = segments;
94 	return segments;
95 }
96 
__find_symbol_by_paddr(const void * paddr,const void * sym)97 static int __find_symbol_by_paddr (const void *paddr, const void *sym) {
98 	return (int)!(*(ut64 *)paddr == ((RBinSymbol *)sym)->paddr);
99 }
100 
r_bin_ne_get_symbols(r_bin_ne_obj_t * bin)101 RList *r_bin_ne_get_symbols(r_bin_ne_obj_t *bin) {
102 	RBinSymbol *sym;
103 	ut16 off = bin->ne_header->ResidNamTable + bin->header_offset;
104 	RList *symbols = r_list_newf (free);
105 	if (!symbols) {
106 		return NULL;
107 	}
108 	RList *entries = r_bin_ne_get_entrypoints (bin);
109 	bool resident = true, first = true;
110 	while (true) {
111 		ut8 sz = r_buf_read8_at (bin->buf, off);
112 		if (!sz) {
113 			first = true;
114 			if (resident) {
115 				resident = false;
116 				off = bin->ne_header->OffStartNonResTab;
117 				sz = r_buf_read8_at (bin->buf, off);
118 				if (!sz) {
119 					break;
120 				}
121 			} else {
122 				break;
123 			}
124 		}
125 		char *name = malloc ((ut64)sz + 1);
126 		if (!name) {
127 			break;
128 		}
129 		off++;
130 		r_buf_read_at (bin->buf, off, (ut8 *)name, sz);
131 		name[sz] = '\0';
132 		off += sz;
133 		sym = R_NEW0 (RBinSymbol);
134 		if (!sym) {
135 			break;
136 		}
137 		sym->name = name;
138 		if (!first) {
139 			sym->bind = R_BIN_BIND_GLOBAL_STR;
140 		}
141 		ut16 entry_off = r_buf_read_le16_at (bin->buf, off);
142 		off += 2;
143 		RBinAddr *entry = r_list_get_n (entries, entry_off);
144 		if (entry) {
145 			sym->paddr = entry->paddr;
146 		} else {
147 			sym->paddr = -1;
148 		}
149 		sym->ordinal = entry_off;
150 		r_list_append (symbols, sym);
151 		first = false;
152 	}
153 	RListIter *it;
154 	RBinAddr *en;
155 	int i = 1;
156 	r_list_foreach (entries, it, en) {
157 		if (!r_list_find (symbols, &en->paddr, __find_symbol_by_paddr)) {
158 			sym = R_NEW0 (RBinSymbol);
159 			if (!sym) {
160 				break;
161 			}
162 			sym->name = r_str_newf ("entry%d", i - 1);
163 			sym->paddr = en->paddr;
164 			sym->bind = R_BIN_BIND_GLOBAL_STR;
165 			sym->ordinal = i;
166 			r_list_append (symbols, sym);
167 		}
168 		i++;
169 	}
170 	bin->symbols = symbols;
171 	return symbols;
172 }
173 
__resource_type_str(int type)174 static char *__resource_type_str(int type) {
175 	char *typeName;
176 	switch (type) {
177 	case 1:
178 		typeName = "CURSOR";
179 		break;
180 	case 2:
181 		typeName = "BITMAP";
182 		break;
183 	case 3:
184 		typeName = "ICON";
185 		break;
186 	case 4:
187 		typeName = "MENU";
188 		break;
189 	case 5:
190 		typeName = "DIALOG";
191 		break;
192 	case 6:
193 		typeName = "STRING";
194 		break;
195 	case 7:
196 		typeName = "FONTDIR";
197 		break;
198 	case 8:
199 		typeName = "FONT";
200 		break;
201 	case 9:
202 		typeName = "ACCELERATOR";
203 		break;
204 	case 10:
205 		typeName = "RCDATA";
206 		break;
207 	case 11:
208 		typeName = "MESSAGETABLE";
209 		break;
210 	case 12:
211 		typeName = "GROUP_CURSOR";
212 		break;
213 	case 14:
214 		typeName = "GROUP_ICON";
215 		break;
216 	case 15:
217 		typeName = "NAMETABLE";
218 		break;
219 	case 16:
220 		typeName = "VERSION";
221 		break;
222 	case 17:
223 		typeName = "DLGINCLUDE";
224 		break;
225 	case 19:
226 		typeName = "PLUGPLAY";
227 		break;
228 	case 20:
229 		typeName = "VXD";
230 		break;
231 	case 21:
232 		typeName = "ANICURSOR";
233 		break;
234 	case 22:
235 		typeName = "ANIICON";
236 		break;
237 	case 23:
238 		typeName = "HTML";
239 		break;
240 	case 24:
241 		typeName = "MANIFEST";
242 		break;
243 	default:
244 		return r_str_newf ("UNKNOWN (%d)", type);
245 	}
246 	return strdup (typeName);
247 }
248 
__free_resource_entry(void * entry)249 static void __free_resource_entry(void *entry) {
250 	r_ne_resource_entry *en = (r_ne_resource_entry *)entry;
251 	free (en->name);
252 	free (en);
253 }
254 
__free_resource(void * resource)255 static void __free_resource(void *resource) {
256 	r_ne_resource *res = (r_ne_resource *)resource;
257 	free (res->name);
258 	r_list_free (res->entry);
259 	free (res);
260 }
261 
__ne_get_resources(r_bin_ne_obj_t * bin)262 static bool __ne_get_resources(r_bin_ne_obj_t *bin) {
263 	if (!bin->resources) {
264 		bin->resources = r_list_newf (__free_resource);
265 	}
266 	ut16 resoff = bin->ne_header->ResTableOffset + bin->header_offset;
267 	ut16 alignment = r_buf_read_le16_at (bin->buf, resoff);
268 	ut32 off = resoff + 2;
269 	while (true) {
270 		NE_image_typeinfo_entry ti = {0};
271 		r_ne_resource *res = R_NEW0 (r_ne_resource);
272 		if (!res) {
273 			break;
274 		}
275 		res->entry = r_list_newf (__free_resource_entry);
276 		if (!res->entry) {
277 			break;
278 		}
279 		r_buf_read_at (bin->buf, off, (ut8 *)&ti, sizeof (ti));
280 		if (!ti.rtTypeID) {
281 			break;
282 		} else if (ti.rtTypeID & 0x8000) {
283 			res->name = __resource_type_str (ti.rtTypeID & ~0x8000);
284 		} else {
285 			// Offset to resident name table
286 			res->name = __read_nonnull_str_at (bin->buf, (ut64)resoff + ti.rtTypeID);
287 		}
288 		off += sizeof (NE_image_typeinfo_entry);
289 		int i;
290 		for (i = 0; i < ti.rtResourceCount; i++) {
291 			NE_image_nameinfo_entry ni;
292 			r_ne_resource_entry *ren = R_NEW0 (r_ne_resource_entry);
293 			if (!ren) {
294 				break;
295 			}
296 			r_buf_read_at (bin->buf, off, (ut8 *)&ni, sizeof (NE_image_nameinfo_entry));
297 			ren->offset = ni.rnOffset << alignment;
298 			ren->size = ni.rnLength;
299 			if (ni.rnID & 0x8000) {
300 				ren->name = r_str_newf ("%d", ni.rnID & ~0x8000);
301 			} else {
302 				// Offset to resident name table
303 				ren->name = __read_nonnull_str_at (bin->buf, (ut64)resoff + ni.rnID);
304 			}
305 			r_list_append (res->entry, ren);
306 			off += sizeof (NE_image_nameinfo_entry);
307 		}
308 		r_list_append (bin->resources, res);
309 	}
310 	return true;
311 }
312 
r_bin_ne_get_imports(r_bin_ne_obj_t * bin)313 RList *r_bin_ne_get_imports(r_bin_ne_obj_t *bin) {
314 	RList *imports = r_list_newf (free);
315 	if (!imports) {
316 		return NULL;
317 	}
318 	ut16 off = bin->ne_header->ImportNameTable + bin->header_offset + 1;
319 	int i;
320 	for (i = 0; i < bin->ne_header->ModRefs; i++) {
321 		RBinImport *imp = R_NEW0 (RBinImport);
322 		if (!imp) {
323 			break;
324 		}
325 		ut8 sz = r_buf_read8_at (bin->buf, off);
326 		if (!sz) {
327 			r_bin_import_free (imp);
328 			break;
329 		}
330 		off++;
331 		char *name = malloc ((ut64)sz + 1);
332 		if (!name) {
333 			break;
334 		}
335 		r_buf_read_at (bin->buf, off, (ut8 *)name, sz);
336 		name[sz] = '\0';
337 		imp->name = name;
338 		imp->ordinal = i + 1;
339 		r_list_append (imports, imp);
340 		off += sz;
341 	}
342 	bin->imports = imports;
343 	return imports;
344 }
345 
r_bin_ne_get_entrypoints(r_bin_ne_obj_t * bin)346 RList *r_bin_ne_get_entrypoints(r_bin_ne_obj_t *bin) {
347 	RList *entries = r_list_newf (free);
348 	if (!entries) {
349 		return NULL;
350 	}
351 	RBinAddr *entry;
352 	RList *segments = r_bin_ne_get_segments (bin);
353 	if (!segments) {
354 		r_list_free (entries);
355 		return NULL;
356 	}
357 	if (bin->ne_header->csEntryPoint) {
358 		entry = R_NEW0 (RBinAddr);
359 		if (!entry) {
360 			r_list_free (entries);
361 			return NULL;
362 		}
363 		entry->bits = 16;
364 		RBinSection *s = r_list_get_n (segments, bin->ne_header->csEntryPoint - 1);
365 		entry->paddr = bin->ne_header->ipEntryPoint + (s? s->paddr: 0);
366 		r_list_append (entries, entry);
367 	}
368 	int off = 0;
369 	while (off < bin->ne_header->EntryTableLength) {
370 		ut8 bundle_length = *(ut8 *)(bin->entry_table + off);
371 		if (!bundle_length) {
372 			break;
373 		}
374 		off++;
375 		ut8 bundle_type = *(ut8 *)(bin->entry_table + off);
376 		off++;
377 		int i;
378 		for (i = 0; i < bundle_length; i++) {
379 			entry = R_NEW0 (RBinAddr);
380 			if (!entry) {
381 				r_list_free (entries);
382 				return NULL;
383 			}
384 			off++;
385 			if (!bundle_type) { // Skip
386 				off--;
387 				free (entry);
388 				break;
389 			} else if (bundle_type == 0xFF) { // Moveable
390 				off += 2;
391 				ut8 segnum = *(bin->entry_table + off);
392 				off++;
393 				ut16 segoff = *(ut16 *)(bin->entry_table + off);
394 				entry->paddr = (ut64)bin->segment_entries[segnum - 1].offset * bin->alignment + segoff;
395 			} else { // Fixed
396 				entry->paddr = (ut64)bin->segment_entries[bundle_type - 1].offset * bin->alignment + *(ut16 *)(bin->entry_table + off);
397 			}
398 			off += 2;
399 			r_list_append (entries, entry);
400 		}
401 	}
402 	r_list_free (segments);
403 	bin->entries = entries;
404 	return entries;
405 }
406 
r_bin_ne_get_relocs(r_bin_ne_obj_t * bin)407 RList *r_bin_ne_get_relocs(r_bin_ne_obj_t *bin) {
408 	RList *segments = bin->segments;
409 	if (!segments) {
410 		return NULL;
411 	}
412 	RList *entries = bin->entries;
413 	if (!entries) {
414 		return NULL;
415 	}
416 	RList *symbols = bin->symbols;
417 	if (!symbols) {
418 		return NULL;
419 	}
420 
421 	ut16 *modref = malloc (bin->ne_header->ModRefs * sizeof (ut16));
422 	if (!modref) {
423 		return NULL;
424 	}
425 	r_buf_read_at (bin->buf, (ut64)bin->ne_header->ModRefTable + bin->header_offset, (ut8 *)modref, bin->ne_header->ModRefs * sizeof (ut16));
426 
427 	RList *relocs = r_list_newf (free);
428 	if (!relocs) {
429 		free (modref);
430 		return NULL;
431 	}
432 
433 	RListIter *it;
434 	RBinSection *seg;
435 	int index = -1;
436 	r_list_foreach (segments, it, seg) {
437 		index++;
438 		if (!(bin->segment_entries[index].flags & RELOCINFO)) {
439 			continue;
440 		}
441 		ut32 off, start = off = seg->paddr + seg->size;
442 		ut16 length = r_buf_read_le16_at (bin->buf, off);
443 		if (!length) {
444 			continue;
445 		}
446 		off += 2;
447 		while (off < start + length * sizeof (NE_image_reloc_item)) {
448 			RBinReloc *reloc = R_NEW0 (RBinReloc);
449 			if (!reloc) {
450 				return NULL;
451 			}
452 			NE_image_reloc_item rel;
453 			r_buf_read_at (bin->buf, off, (ut8 *)&rel, sizeof (rel));
454 			reloc->paddr = seg->paddr + rel.offset;
455 			switch (rel.type) {
456 			case LOBYTE:
457 				reloc->type = R_BIN_RELOC_8;
458 				break;
459 			case SEL_16:
460 			case OFF_16:
461 				reloc->type = R_BIN_RELOC_16;
462 				break;
463 			case POI_32:
464 			case OFF_32:
465 				reloc->type = R_BIN_RELOC_32;
466 				break;
467 			case POI_48:
468 				reloc->type = R_BIN_RELOC_64;
469 				break;
470 			}
471 
472 			ut32 offset;
473 			if (rel.flags & (IMPORTED_ORD | IMPORTED_NAME)) {
474 				RBinImport *imp = R_NEW0 (RBinImport);
475 				if (!imp) {
476 					free (reloc);
477 					break;
478 				}
479 				char *name;
480 				if (rel.index > bin->ne_header->ModRefs) {
481 					name = r_str_newf ("UnknownModule%d_%x", rel.index, off); // ????
482 				} else {
483 					offset = modref[rel.index - 1] + bin->header_offset + bin->ne_header->ImportNameTable;
484 					name = __read_nonnull_str_at (bin->buf, offset);
485 				}
486 				if (rel.flags & IMPORTED_ORD) {
487 					imp->ordinal = rel.func_ord;
488 					imp->name = r_str_newf ("%s.%s", name, __func_name_from_ord(name, rel.func_ord));
489 				} else {
490 					offset = bin->header_offset + bin->ne_header->ImportNameTable + rel.name_off;
491 					char *func = __read_nonnull_str_at (bin->buf, offset);
492 					imp->name = r_str_newf ("%s.%s", name, func);
493 					free (func);
494 				}
495 				free (name);
496 				reloc->import = imp;
497 			} else if (rel.flags & OSFIXUP) {
498 				// TODO
499 			} else {
500 				if (strstr (seg->name, "FIXED")) {
501 					RBinSection *s = r_list_get_n (segments, rel.segnum - 1);
502 					if (s) {
503 						offset = s->paddr + rel.segoff;
504 					} else {
505 						offset = -1;
506 					}
507 				} else {
508 					RBinAddr *entry = r_list_get_n (entries, rel.entry_ordinal - 1);
509 					if (entry) {
510 						offset = entry->paddr;
511 					} else {
512 						offset = -1;
513 					}
514 				}
515 				reloc->addend = offset;
516 				RBinSymbol *sym = NULL;
517 				RListIter *sit;
518 				r_list_foreach (symbols, sit, sym) {
519 					if (sym->paddr == reloc->addend) {
520 						reloc->symbol = sym;
521 						break;
522 					}
523 				}
524 			}
525 
526 			if (rel.flags & ADDITIVE) {
527 				reloc->additive = 1;
528 				r_list_append (relocs, reloc);
529 			} else {
530 				do {
531 					r_list_append (relocs, reloc);
532 
533 					offset = r_buf_read_le16_at (bin->buf, reloc->paddr);
534 					RBinReloc *tmp = reloc;
535 					reloc = R_NEW0 (RBinReloc);
536 					if (!reloc) {
537 						break;
538 					}
539 					*reloc = *tmp;
540 					reloc->paddr = seg->paddr + offset;
541 				} while (offset != 0xFFFF);
542 				free (reloc);
543 			}
544 
545 			off += sizeof (NE_image_reloc_item);
546 		}
547 	}
548 	free (modref);
549 	return relocs;
550 }
551 
__init(RBuffer * buf,r_bin_ne_obj_t * bin)552 void __init(RBuffer *buf, r_bin_ne_obj_t *bin) {
553 	bin->header_offset = r_buf_read_le16_at (buf, 0x3c);
554 	bin->ne_header = R_NEW0 (NE_image_header);
555 	if (!bin->ne_header) {
556 		return;
557 	}
558 	bin->buf = buf;
559 	r_buf_read_at (buf, bin->header_offset, (ut8 *)bin->ne_header, sizeof (NE_image_header));
560 	bin->alignment = 1 << bin->ne_header->FileAlnSzShftCnt;
561 	if (!bin->alignment) {
562 		bin->alignment = 1 << 9;
563 	}
564 	bin->os = __get_target_os (bin);
565 
566 	ut16 offset = bin->ne_header->SegTableOffset + bin->header_offset;
567 	ut16 size = bin->ne_header->SegCount * sizeof (NE_image_segment_entry);
568 	bin->segment_entries = calloc (1, size);
569 	if (!bin->segment_entries) {
570 		return;
571 	}
572 	r_buf_read_at (buf, offset, (ut8 *)bin->segment_entries, size);
573 	bin->entry_table = calloc (1, bin->ne_header->EntryTableLength);
574 	r_buf_read_at (buf, (ut64)bin->header_offset + bin->ne_header->EntryTableOffset, bin->entry_table, bin->ne_header->EntryTableLength);
575 	bin->imports = r_bin_ne_get_imports (bin);
576 	__ne_get_resources (bin);
577 }
578 
r_bin_ne_free(r_bin_ne_obj_t * bin)579 void r_bin_ne_free(r_bin_ne_obj_t *bin) {
580 	// r_list_free (bin->imports); // double free
581 	r_list_free (bin->resources);
582 	free (bin->entry_table);
583 	free (bin->ne_header);
584 	free (bin->resident_name_table);
585 	free (bin->segment_entries);
586 }
587 
r_bin_ne_new_buf(RBuffer * buf,bool verbose)588 r_bin_ne_obj_t *r_bin_ne_new_buf(RBuffer *buf, bool verbose) {
589 	r_bin_ne_obj_t *bin = R_NEW0 (r_bin_ne_obj_t);
590 	if (!bin) {
591 		return NULL;
592 	}
593 	__init(buf, bin);
594 	return bin;
595 }
596