756 lines
19 KiB
C
756 lines
19 KiB
C
// SPDX-FileCopyrightText: 2019 GustavoLCR <gugulcr@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "ne.h"
|
|
|
|
static char *__get_target_os(rz_bin_ne_obj_t *bin) {
|
|
switch (bin->ne_header->targOS) {
|
|
case 1:
|
|
return "OS/2";
|
|
case 2:
|
|
return "Windows";
|
|
case 3:
|
|
return "European MS-DOS 4.x";
|
|
case 4:
|
|
return "Windows 386";
|
|
case 5:
|
|
return "BOSS (Borland Operating System Services)";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static int __translate_perms(int flags) {
|
|
int perms = 0;
|
|
if (flags & IS_RX) {
|
|
if (flags & IS_DATA) {
|
|
perms = RZ_PERM_R;
|
|
} else {
|
|
perms = RZ_PERM_X;
|
|
}
|
|
}
|
|
if (!perms) {
|
|
perms = RZ_PERM_RWX;
|
|
}
|
|
return perms;
|
|
}
|
|
|
|
static char *__read_nonnull_str_at(RzBuffer *buf, ut64 offset) {
|
|
ut8 sz;
|
|
if (!rz_buf_read8_at(buf, offset, &sz)) {
|
|
return NULL;
|
|
}
|
|
if (!sz) {
|
|
return NULL;
|
|
}
|
|
char *str = malloc((ut64)sz + 1);
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
rz_buf_read_at(buf, offset + 1, (ut8 *)str, sz);
|
|
str[sz] = '\0';
|
|
return str;
|
|
}
|
|
|
|
static char *__func_name_from_ord(char *module, ut16 ordinal) {
|
|
char *formats_dir = rz_path_system(RZ_SDB_FORMAT);
|
|
char *path = rz_str_newf(RZ_JOIN_3_PATHS("%s", "dll", "%s.sdb"), formats_dir, module);
|
|
free(formats_dir);
|
|
char *ord = rz_str_newf("%d", ordinal);
|
|
char *name;
|
|
if (rz_file_exists(path)) {
|
|
Sdb *sdb = sdb_new(NULL, path, 0);
|
|
name = sdb_get(sdb, ord, NULL);
|
|
if (!name) {
|
|
name = ord;
|
|
} else {
|
|
free(ord);
|
|
}
|
|
sdb_close(sdb);
|
|
free(sdb);
|
|
} else {
|
|
name = ord;
|
|
}
|
|
free(path);
|
|
return name;
|
|
}
|
|
|
|
RzPVector /*<RzBinSection *>*/ *rz_bin_ne_get_segments(rz_bin_ne_obj_t *bin) {
|
|
int i;
|
|
if (!bin) {
|
|
return NULL;
|
|
}
|
|
RzPVector *segments = rz_pvector_new((RzPVectorFree)rz_bin_section_free);
|
|
if (!segments) {
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < bin->ne_header->SegCount; i++) {
|
|
RzBinSection *bs = RZ_NEW0(RzBinSection);
|
|
NE_image_segment_entry *se = &bin->segment_entries[i];
|
|
if (!bs || !se) {
|
|
free(bs);
|
|
return segments;
|
|
}
|
|
bs->size = se->length;
|
|
bs->vsize = se->minAllocSz ? se->minAllocSz : 64000;
|
|
bs->bits = RZ_SYS_BITS_16;
|
|
bs->is_data = se->flags & IS_DATA;
|
|
bs->perm = __translate_perms(se->flags);
|
|
bs->paddr = (ut64)se->offset * bin->alignment;
|
|
bs->name = rz_str_newf("%s.%" PFMT64d, se->flags & IS_MOVEABLE ? "MOVEABLE" : "FIXED", bs->paddr);
|
|
bs->is_segment = true;
|
|
rz_pvector_push(segments, bs);
|
|
}
|
|
bin->segments = segments;
|
|
return segments;
|
|
}
|
|
|
|
static int __find_symbol_by_paddr(const void *paddr, const void *sym, void *user) {
|
|
return (int)!(*(ut64 *)paddr == ((RzBinSymbol *)sym)->paddr);
|
|
}
|
|
|
|
static void ne_sanitize_name(char *name, ut16 count) {
|
|
// expect to have names in ASCII format.
|
|
for (ut16 i = 0; i < count && name[i]; ++i) {
|
|
if (!IS_PRINTABLE(name[i])) {
|
|
name[i] = '?';
|
|
}
|
|
}
|
|
}
|
|
|
|
RzPVector /*<RzBinSymbol *>*/ *rz_bin_ne_get_symbols(rz_bin_ne_obj_t *bin) {
|
|
RzBinSymbol *sym;
|
|
ut16 off = bin->ne_header->ResidNamTable + bin->header_offset;
|
|
RzPVector *symbols = rz_pvector_new(free);
|
|
if (!symbols) {
|
|
return NULL;
|
|
}
|
|
RzList *entries = rz_bin_ne_get_entrypoints(bin);
|
|
bool resident = true, first = true;
|
|
while (true) {
|
|
ut8 sz;
|
|
if (!rz_buf_read8_at(bin->buf, off, &sz)) {
|
|
break;
|
|
}
|
|
if (!sz) {
|
|
first = true;
|
|
if (resident) {
|
|
resident = false;
|
|
off = bin->ne_header->OffStartNonResTab;
|
|
if (!rz_buf_read8_at(bin->buf, off, &sz)) {
|
|
break;
|
|
}
|
|
if (!sz) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
char *name = malloc((ut64)sz + 1);
|
|
if (!name) {
|
|
break;
|
|
}
|
|
off++;
|
|
rz_buf_read_at(bin->buf, off, (ut8 *)name, sz);
|
|
name[sz] = '\0';
|
|
off += sz;
|
|
sym = RZ_NEW0(RzBinSymbol);
|
|
if (!sym) {
|
|
break;
|
|
}
|
|
ne_sanitize_name(name, sz);
|
|
sym->name = name;
|
|
if (!first) {
|
|
sym->bind = RZ_BIN_BIND_GLOBAL_STR;
|
|
}
|
|
ut16 entry_off;
|
|
if (!rz_buf_read_le16_at(bin->buf, off, &entry_off)) {
|
|
rz_bin_symbol_free(sym);
|
|
break;
|
|
}
|
|
off += 2;
|
|
RzBinAddr *entry = rz_list_get_n(entries, entry_off);
|
|
if (entry) {
|
|
sym->paddr = entry->paddr;
|
|
} else {
|
|
sym->paddr = -1;
|
|
}
|
|
sym->ordinal = entry_off;
|
|
rz_pvector_push(symbols, sym);
|
|
first = false;
|
|
}
|
|
RzListIter *it;
|
|
RzBinAddr *en;
|
|
int i = 1;
|
|
rz_list_foreach (entries, it, en) {
|
|
if (!rz_pvector_find(symbols, &en->paddr, __find_symbol_by_paddr, NULL)) {
|
|
sym = RZ_NEW0(RzBinSymbol);
|
|
if (!sym) {
|
|
break;
|
|
}
|
|
sym->name = rz_str_newf("entry%d", i - 1);
|
|
sym->paddr = en->paddr;
|
|
sym->bind = RZ_BIN_BIND_GLOBAL_STR;
|
|
sym->ordinal = i;
|
|
rz_pvector_push(symbols, sym);
|
|
}
|
|
i++;
|
|
}
|
|
bin->symbols = symbols;
|
|
return symbols;
|
|
}
|
|
|
|
static char *__resource_type_str(int type) {
|
|
char *typeName;
|
|
switch (type) {
|
|
case 1:
|
|
typeName = "CURSOR";
|
|
break;
|
|
case 2:
|
|
typeName = "BITMAP";
|
|
break;
|
|
case 3:
|
|
typeName = "ICON";
|
|
break;
|
|
case 4:
|
|
typeName = "MENU";
|
|
break;
|
|
case 5:
|
|
typeName = "DIALOG";
|
|
break;
|
|
case 6:
|
|
typeName = "STRING";
|
|
break;
|
|
case 7:
|
|
typeName = "FONTDIR";
|
|
break;
|
|
case 8:
|
|
typeName = "FONT";
|
|
break;
|
|
case 9:
|
|
typeName = "ACCELERATOR";
|
|
break;
|
|
case 10:
|
|
typeName = "RCDATA";
|
|
break;
|
|
case 11:
|
|
typeName = "MESSAGETABLE";
|
|
break;
|
|
case 12:
|
|
typeName = "GROUP_CURSOR";
|
|
break;
|
|
case 14:
|
|
typeName = "GROUP_ICON";
|
|
break;
|
|
case 15:
|
|
typeName = "NAMETABLE";
|
|
break;
|
|
case 16:
|
|
typeName = "VERSION";
|
|
break;
|
|
case 17:
|
|
typeName = "DLGINCLUDE";
|
|
break;
|
|
case 19:
|
|
typeName = "PLUGPLAY";
|
|
break;
|
|
case 20:
|
|
typeName = "VXD";
|
|
break;
|
|
case 21:
|
|
typeName = "ANICURSOR";
|
|
break;
|
|
case 22:
|
|
typeName = "ANIICON";
|
|
break;
|
|
case 23:
|
|
typeName = "HTML";
|
|
break;
|
|
case 24:
|
|
typeName = "MANIFEST";
|
|
break;
|
|
default:
|
|
return rz_str_newf("UNKNOWN (%d)", type);
|
|
}
|
|
return strdup(typeName);
|
|
}
|
|
|
|
static void __free_resource_entry(void *entry) {
|
|
rz_ne_resource_entry *en = (rz_ne_resource_entry *)entry;
|
|
free(en->name);
|
|
free(en);
|
|
}
|
|
|
|
static void __free_resource(void *resource) {
|
|
rz_ne_resource *res = (rz_ne_resource *)resource;
|
|
free(res->name);
|
|
rz_list_free(res->entry);
|
|
free(res);
|
|
}
|
|
|
|
static bool __ne_get_resources(rz_bin_ne_obj_t *bin) {
|
|
if (!bin->resources) {
|
|
bin->resources = rz_list_newf(__free_resource);
|
|
}
|
|
ut16 resoff = bin->ne_header->ResTableOffset + bin->header_offset;
|
|
|
|
ut16 alignment;
|
|
if (!rz_buf_read_le16_at(bin->buf, resoff, &alignment) || alignment > 31) {
|
|
return false;
|
|
}
|
|
|
|
ut32 off = resoff + 2;
|
|
while (true) {
|
|
NE_image_typeinfo_entry ti = { 0 };
|
|
rz_ne_resource *res = RZ_NEW0(rz_ne_resource);
|
|
if (!res) {
|
|
break;
|
|
}
|
|
res->entry = rz_list_newf(__free_resource_entry);
|
|
if (!res->entry) {
|
|
break;
|
|
}
|
|
ut64 offset = off;
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ti.rtTypeID);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ti.rtResourceCount);
|
|
rz_buf_read_le32_offset(bin->buf, &offset, &ti.rtReserved);
|
|
if (!ti.rtTypeID) {
|
|
break;
|
|
} else if (ti.rtTypeID & 0x8000) {
|
|
res->name = __resource_type_str(ti.rtTypeID & ~0x8000);
|
|
} else {
|
|
// Offset to resident name table
|
|
res->name = __read_nonnull_str_at(bin->buf, (ut64)resoff + ti.rtTypeID);
|
|
}
|
|
off += sizeof(NE_image_typeinfo_entry);
|
|
int i;
|
|
for (i = 0; i < ti.rtResourceCount; i++) {
|
|
rz_ne_resource_entry *ren = RZ_NEW0(rz_ne_resource_entry);
|
|
if (!ren) {
|
|
break;
|
|
}
|
|
offset = off;
|
|
NE_image_nameinfo_entry ni = { 0 };
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ni.rnOffset);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ni.rnLength);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ni.rnFlags);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ni.rnID);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ni.rnHandle);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &ni.rnUsage);
|
|
ren->offset = ni.rnOffset << alignment;
|
|
ren->size = ni.rnLength;
|
|
if (ni.rnID & 0x8000) {
|
|
ren->name = rz_str_newf("%d", ni.rnID & ~0x8000);
|
|
} else {
|
|
// Offset to resident name table
|
|
ren->name = __read_nonnull_str_at(bin->buf, (ut64)resoff + ni.rnID);
|
|
}
|
|
rz_list_append(res->entry, ren);
|
|
off += sizeof(NE_image_nameinfo_entry);
|
|
}
|
|
rz_list_append(bin->resources, res);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RzPVector /*<RzBinImport *>*/ *rz_bin_ne_get_imports(rz_bin_ne_obj_t *bin) {
|
|
RzPVector *imports = rz_pvector_new((RzListFree)rz_bin_import_free);
|
|
if (!imports) {
|
|
return NULL;
|
|
}
|
|
ut16 off = bin->ne_header->ImportNameTable + bin->header_offset + 1;
|
|
int i;
|
|
for (i = 0; i < bin->ne_header->ModRefs; i++) {
|
|
RzBinImport *imp = RZ_NEW0(RzBinImport);
|
|
if (!imp) {
|
|
break;
|
|
}
|
|
ut8 sz;
|
|
if (!rz_buf_read8_at(bin->buf, off, &sz)) {
|
|
break;
|
|
}
|
|
if (!sz) {
|
|
rz_bin_import_free(imp);
|
|
break;
|
|
}
|
|
off++;
|
|
char *name = malloc((ut64)sz + 1);
|
|
if (!name) {
|
|
break;
|
|
}
|
|
rz_buf_read_at(bin->buf, off, (ut8 *)name, sz);
|
|
name[sz] = '\0';
|
|
imp->name = name;
|
|
imp->ordinal = i + 1;
|
|
rz_pvector_push(imports, imp);
|
|
off += sz;
|
|
}
|
|
bin->imports = imports;
|
|
return imports;
|
|
}
|
|
|
|
RzList /*<RzBinAddr *>*/ *rz_bin_ne_get_entrypoints(rz_bin_ne_obj_t *bin) {
|
|
RzList *entries = rz_list_newf(free);
|
|
if (!entries) {
|
|
return NULL;
|
|
}
|
|
RzBinAddr *entry;
|
|
RzPVector *segments = rz_bin_ne_get_segments(bin);
|
|
if (!segments) {
|
|
rz_list_free(entries);
|
|
return NULL;
|
|
}
|
|
if (bin->ne_header->csEntryPoint) {
|
|
entry = RZ_NEW0(RzBinAddr);
|
|
if (!entry) {
|
|
rz_list_free(entries);
|
|
rz_pvector_free(segments);
|
|
return NULL;
|
|
}
|
|
entry->bits = 16;
|
|
RzBinSection *s = (RzBinSection *)rz_pvector_at(segments, bin->ne_header->csEntryPoint - 1);
|
|
entry->paddr = bin->ne_header->ipEntryPoint + (s ? s->paddr : 0);
|
|
rz_list_append(entries, entry);
|
|
}
|
|
ut32 off = 0;
|
|
while (off < bin->ne_header->EntryTableLength) {
|
|
ut8 bundle_length = *(ut8 *)(bin->entry_table + off);
|
|
if (!bundle_length) {
|
|
break;
|
|
}
|
|
off++;
|
|
if (off >= bin->ne_header->EntryTableLength) {
|
|
break;
|
|
}
|
|
ut8 bundle_type = *(ut8 *)(bin->entry_table + off);
|
|
off++;
|
|
int i;
|
|
for (i = 0; i < bundle_length; i++) {
|
|
entry = RZ_NEW0(RzBinAddr);
|
|
if (!entry) {
|
|
rz_list_free(entries);
|
|
rz_pvector_free(segments);
|
|
return NULL;
|
|
}
|
|
off++;
|
|
if (!bundle_type) { // Skip
|
|
off--;
|
|
free(entry);
|
|
break;
|
|
} else if (bundle_type == 0xFF) { // Moveable
|
|
off += 2;
|
|
if ((off + 1) >= bin->ne_header->EntryTableLength) {
|
|
free(entry);
|
|
goto end;
|
|
}
|
|
ut8 segnum = rz_read_le8(bin->entry_table + off);
|
|
off++;
|
|
if ((off + 2) >= bin->ne_header->EntryTableLength) {
|
|
free(entry);
|
|
goto end;
|
|
}
|
|
ut16 segoff = rz_read_le16(bin->entry_table + off);
|
|
if (!segnum || segnum > bin->ne_header->SegCount) {
|
|
free(entry);
|
|
continue;
|
|
}
|
|
entry->paddr = (ut64)bin->segment_entries[segnum - 1].offset * bin->alignment + segoff;
|
|
} else { // Fixed
|
|
ut8 *p = bin->entry_table + off;
|
|
if ((off + 2) >= bin->ne_header->EntryTableLength || bundle_type > bin->ne_header->SegCount) {
|
|
free(entry);
|
|
goto end;
|
|
}
|
|
entry->paddr = (ut64)bin->segment_entries[bundle_type - 1].offset * bin->alignment + rz_read_le16(p);
|
|
}
|
|
off += 2;
|
|
rz_list_append(entries, entry);
|
|
}
|
|
}
|
|
end:
|
|
rz_pvector_free(segments);
|
|
bin->entries = entries;
|
|
return entries;
|
|
}
|
|
|
|
RzPVector /*<RzBinReloc *>*/ *rz_bin_ne_get_relocs(rz_bin_ne_obj_t *bin) {
|
|
RzPVector *segments = bin->segments;
|
|
if (!segments) {
|
|
return NULL;
|
|
}
|
|
RzList *entries = bin->entries;
|
|
if (!entries) {
|
|
return NULL;
|
|
}
|
|
RzPVector *symbols = bin->symbols;
|
|
if (!symbols) {
|
|
return NULL;
|
|
}
|
|
|
|
ut16 *modref = calloc(bin->ne_header->ModRefs, sizeof(ut16));
|
|
if (!modref) {
|
|
return NULL;
|
|
}
|
|
rz_buf_read_at(bin->buf, (ut64)bin->ne_header->ModRefTable + bin->header_offset, (ut8 *)modref, bin->ne_header->ModRefs * sizeof(ut16));
|
|
|
|
RzPVector *relocs = rz_pvector_new(free);
|
|
if (!relocs) {
|
|
free(modref);
|
|
return NULL;
|
|
}
|
|
|
|
ut64 bufsz = rz_buf_size(bin->buf);
|
|
void **it;
|
|
RzBinSection *seg;
|
|
int index = -1;
|
|
rz_pvector_foreach (segments, it) {
|
|
seg = *it;
|
|
index++;
|
|
if (!(bin->segment_entries[index].flags & RELOCINFO)) {
|
|
continue;
|
|
}
|
|
ut32 off, start = off = seg->paddr + seg->size;
|
|
if ((ut64)off + 2 > bufsz) {
|
|
continue;
|
|
}
|
|
ut16 length;
|
|
if (!rz_buf_read_le16_at(bin->buf, off, &length)) {
|
|
continue;
|
|
}
|
|
|
|
if (!length) {
|
|
continue;
|
|
}
|
|
off += 2;
|
|
while (off < start + length * sizeof(NE_image_reloc_item) && off + sizeof(NE_image_reloc_item) <= bufsz) {
|
|
RzBinReloc *reloc = RZ_NEW0(RzBinReloc);
|
|
if (!reloc) {
|
|
return NULL;
|
|
}
|
|
NE_image_reloc_item rel;
|
|
ut64 offset = off;
|
|
rz_buf_read8_offset(bin->buf, &offset, &rel.type);
|
|
rz_buf_read8_offset(bin->buf, &offset, &rel.flags);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &rel.offset);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &rel.align1);
|
|
rz_buf_read_le16_offset(bin->buf, &offset, &rel.func_ord);
|
|
reloc->paddr = seg->paddr + rel.offset;
|
|
switch (rel.type) {
|
|
case LOBYTE:
|
|
reloc->type = RZ_BIN_RELOC_8;
|
|
break;
|
|
case SEL_16:
|
|
case OFF_16:
|
|
reloc->type = RZ_BIN_RELOC_16;
|
|
break;
|
|
case POI_32:
|
|
case OFF_32:
|
|
reloc->type = RZ_BIN_RELOC_32;
|
|
break;
|
|
case POI_48:
|
|
reloc->type = RZ_BIN_RELOC_64;
|
|
break;
|
|
}
|
|
|
|
if (rel.flags & (IMPORTED_ORD | IMPORTED_NAME)) {
|
|
RzBinImport *imp = RZ_NEW0(RzBinImport);
|
|
if (!imp) {
|
|
free(reloc);
|
|
break;
|
|
}
|
|
char *name = NULL;
|
|
if (rel.index > bin->ne_header->ModRefs || !rel.index) {
|
|
name = rz_str_newf("UnknownModule%d_%x", rel.index, off); // ????
|
|
} else {
|
|
ut16 modref_val = rz_read_le16(&modref[rel.index - 1]);
|
|
offset = modref_val + bin->header_offset + bin->ne_header->ImportNameTable;
|
|
name = __read_nonnull_str_at(bin->buf, offset);
|
|
}
|
|
if (rel.flags & IMPORTED_ORD) {
|
|
imp->ordinal = rel.func_ord;
|
|
imp->name = rz_str_newf("%s.%s", name, __func_name_from_ord(name, rel.func_ord));
|
|
} else {
|
|
offset = bin->header_offset + bin->ne_header->ImportNameTable + rel.name_off;
|
|
char *func = __read_nonnull_str_at(bin->buf, offset);
|
|
imp->name = rz_str_newf("%s.%s", name, func);
|
|
free(func);
|
|
}
|
|
free(name);
|
|
reloc->import = imp;
|
|
} else if (rel.flags & OSFIXUP) {
|
|
// TODO
|
|
} else {
|
|
if (strstr(seg->name, "FIXED")) {
|
|
RzBinSection *s = (RzBinSection *)rz_pvector_at(segments, rel.segnum - 1);
|
|
if (s) {
|
|
offset = s->paddr + rel.segoff;
|
|
} else {
|
|
offset = -1;
|
|
}
|
|
} else {
|
|
RzBinAddr *entry = rz_list_get_n(entries, rel.entry_ordinal - 1);
|
|
if (entry) {
|
|
offset = entry->paddr;
|
|
} else {
|
|
offset = -1;
|
|
}
|
|
}
|
|
reloc->addend = offset;
|
|
RzBinSymbol *sym = NULL;
|
|
void **sit;
|
|
rz_pvector_foreach (symbols, sit) {
|
|
sym = *sit;
|
|
if (sym->paddr == reloc->addend) {
|
|
reloc->symbol = sym;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rel.flags & ADDITIVE) {
|
|
reloc->additive = 1;
|
|
rz_pvector_push(relocs, reloc);
|
|
} else {
|
|
do {
|
|
rz_pvector_push(relocs, reloc);
|
|
ut16 tmp_offset;
|
|
if (!rz_buf_read_le16_at(bin->buf, reloc->paddr, &tmp_offset)) {
|
|
reloc = NULL;
|
|
break;
|
|
}
|
|
|
|
offset = tmp_offset;
|
|
|
|
RzBinReloc *tmp = reloc;
|
|
reloc = RZ_NEW0(RzBinReloc);
|
|
if (!reloc) {
|
|
break;
|
|
}
|
|
*reloc = *tmp;
|
|
reloc->paddr = seg->paddr + offset;
|
|
} while (offset != 0xFFFF);
|
|
free(reloc);
|
|
}
|
|
|
|
off += sizeof(NE_image_reloc_item);
|
|
}
|
|
}
|
|
free(modref);
|
|
return relocs;
|
|
}
|
|
|
|
static bool read_ne_header(NE_image_header *ne, RzBuffer *buf, ut64 off) {
|
|
ut64 offset = off;
|
|
return (rz_buf_read8_offset(buf, &offset, (ut8 *)&ne->sig[0]) &&
|
|
rz_buf_read8_offset(buf, &offset, (ut8 *)&ne->sig[1]) &&
|
|
rz_buf_read8_offset(buf, &offset, &ne->MajLinkerVersion) &&
|
|
rz_buf_read8_offset(buf, &offset, &ne->MinLinkerVersion) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->EntryTableOffset) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->EntryTableLength) &&
|
|
rz_buf_read_le32_offset(buf, &offset, &ne->FileLoadCRC) &&
|
|
rz_buf_read8_offset(buf, &offset, &ne->ProgFlags) &&
|
|
rz_buf_read8_offset(buf, &offset, &ne->ApplFlags) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->AutoDataSegIndex) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->InitHeapSize) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->InitStackSize) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->ipEntryPoint) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->csEntryPoint) &&
|
|
rz_buf_read_le32_offset(buf, &offset, &ne->InitStack) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->SegCount) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->ModRefs) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->NoResNamesTabSiz) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->SegTableOffset) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->ResTableOffset) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->ResidNamTable) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->ModRefTable) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->ImportNameTable) &&
|
|
rz_buf_read_le32_offset(buf, &offset, &ne->OffStartNonResTab) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->MovEntryCount) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->FileAlnSzShftCnt) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->nResTabEntries) &&
|
|
rz_buf_read8_offset(buf, &offset, &ne->targOS) &&
|
|
rz_buf_read8_offset(buf, &offset, &ne->OS2EXEFlags) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->retThunkOffset) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->segrefthunksoff) &&
|
|
rz_buf_read_le16_offset(buf, &offset, &ne->mincodeswap) &&
|
|
rz_buf_read8_offset(buf, &offset, (ut8 *)&ne->expctwinver[0]) &&
|
|
rz_buf_read8_offset(buf, &offset, (ut8 *)&ne->expctwinver[1]));
|
|
}
|
|
|
|
bool rz_bin_ne_buf_init(RzBuffer *buf, rz_bin_ne_obj_t *bin) {
|
|
if (!rz_buf_read_le16_at(buf, 0x3c, &bin->header_offset)) {
|
|
return false;
|
|
}
|
|
|
|
bin->ne_header = RZ_NEW0(NE_image_header);
|
|
if (!bin->ne_header) {
|
|
return false;
|
|
}
|
|
bin->buf = buf;
|
|
if (!read_ne_header(bin->ne_header, bin->buf, bin->header_offset)) {
|
|
RZ_FREE(bin->ne_header);
|
|
return false;
|
|
}
|
|
if (bin->ne_header->FileAlnSzShftCnt > 31) {
|
|
return false;
|
|
}
|
|
bin->alignment = 1 << bin->ne_header->FileAlnSzShftCnt;
|
|
if (!bin->alignment) {
|
|
bin->alignment = 1 << 9;
|
|
}
|
|
bin->os = __get_target_os(bin);
|
|
|
|
if (!bin->ne_header->SegCount) {
|
|
return false;
|
|
}
|
|
bin->segment_entries = calloc(bin->ne_header->SegCount, sizeof(NE_image_segment_entry));
|
|
if (!bin->segment_entries) {
|
|
return false;
|
|
}
|
|
|
|
ut64 offset = bin->ne_header->SegTableOffset + bin->header_offset;
|
|
for (ut32 i = 0; i < bin->ne_header->SegCount; i++) {
|
|
NE_image_segment_entry *ne_segment_entry = bin->segment_entries + i;
|
|
rz_buf_read_le16_offset(buf, &offset, &ne_segment_entry->offset);
|
|
rz_buf_read_le16_offset(buf, &offset, &ne_segment_entry->length);
|
|
rz_buf_read_le16_offset(buf, &offset, &ne_segment_entry->flags);
|
|
rz_buf_read_le16_offset(buf, &offset, &ne_segment_entry->minAllocSz);
|
|
}
|
|
if (!bin->ne_header->EntryTableLength) {
|
|
return false;
|
|
}
|
|
bin->entry_table = calloc(1, bin->ne_header->EntryTableLength);
|
|
if (!bin->entry_table) {
|
|
return false;
|
|
}
|
|
rz_buf_read_at(buf, (ut64)bin->header_offset + bin->ne_header->EntryTableOffset, bin->entry_table, bin->ne_header->EntryTableLength);
|
|
bin->imports = rz_bin_ne_get_imports(bin);
|
|
__ne_get_resources(bin);
|
|
return true;
|
|
}
|
|
|
|
void rz_bin_ne_free(rz_bin_ne_obj_t *bin) {
|
|
if (!bin) {
|
|
return;
|
|
}
|
|
// rz_list_free (bin->imports); // double free
|
|
rz_list_free(bin->resources);
|
|
free(bin->entry_table);
|
|
free(bin->ne_header);
|
|
free(bin->resident_name_table);
|
|
free(bin->segment_entries);
|
|
free(bin);
|
|
}
|
|
|
|
rz_bin_ne_obj_t *rz_bin_ne_new_buf(RzBuffer *buf, bool verbose) {
|
|
rz_bin_ne_obj_t *bin = RZ_NEW0(rz_bin_ne_obj_t);
|
|
if (!bin) {
|
|
return NULL;
|
|
}
|
|
if (!rz_bin_ne_buf_init(buf, bin)) {
|
|
rz_bin_ne_free(bin);
|
|
return NULL;
|
|
}
|
|
return bin;
|
|
}
|