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