* fix: format with clang-format-20 * fix: fix clang-format linter with rewrite '^#define.*/\*.*\\$' * fix: update clang-format to version 20 in workflows and documentation * fix: add SPDX license information to .git-blame-ignore-revs
809 lines
25 KiB
C++
809 lines
25 KiB
C++
// SPDX-FileCopyrightText: 2009-2019 nibble <nibble.ds@gmail.com>
|
|
// SPDX-FileCopyrightText: 2009-2019 pancake <pancake@nopcode.org>
|
|
// SPDX-FileCopyrightText: 2009-2019 alvarofe <alvaro.felipe91@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_types.h>
|
|
#include <rz_util.h>
|
|
#include <rz_lib.h>
|
|
#include <rz_bin.h>
|
|
#include "../i/private.h"
|
|
#include "pe/pe.h"
|
|
|
|
static Sdb *pe_get_sdb(RzBinFile *bf) {
|
|
RzBinObject *o = bf->o;
|
|
struct PE_(rz_bin_pe_obj_t) * bin;
|
|
if (!o || !o->bin_obj) {
|
|
return NULL;
|
|
}
|
|
bin = (struct PE_(rz_bin_pe_obj_t) *)o->bin_obj;
|
|
return bin ? bin->kv : NULL;
|
|
}
|
|
|
|
static bool pe_load_buffer(RzBinFile *bf, RzBinObject *obj, RzBuffer *buf, Sdb *sdb) {
|
|
rz_return_val_if_fail(bf && obj && buf, false);
|
|
struct PE_(rz_bin_pe_obj_t) *res = PE_(rz_bin_pe_new_buf)(buf, bf->rbin->verbose);
|
|
if (res) {
|
|
sdb_ns_set(sdb, "info", res->kv);
|
|
obj->bin_obj = res;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void pe_destroy(RzBinFile *bf) {
|
|
PE_(rz_bin_pe_free)((struct PE_(rz_bin_pe_obj_t) *)bf->o->bin_obj);
|
|
}
|
|
|
|
static ut64 pe_baddr(RzBinFile *bf) {
|
|
return PE_(rz_bin_pe_get_image_base)(bf->o->bin_obj);
|
|
}
|
|
|
|
static RzBinAddr *pe_binsym(RzBinFile *bf, RzBinSpecialSymbol type) {
|
|
struct rz_bin_pe_addr_t *peaddr = NULL;
|
|
RzBinAddr *ret = NULL;
|
|
if (bf && bf->o && bf->o->bin_obj) {
|
|
switch (type) {
|
|
case RZ_BIN_SPECIAL_SYMBOL_MAIN:
|
|
peaddr = PE_(rz_bin_pe_get_main_vaddr)(bf->o->bin_obj);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (peaddr && (ret = RZ_NEW0(RzBinAddr))) {
|
|
ret->paddr = peaddr->paddr;
|
|
ret->vaddr = peaddr->vaddr;
|
|
}
|
|
free(peaddr);
|
|
return ret;
|
|
}
|
|
|
|
static void add_tls_callbacks(RzBinFile *bf, RzPVector /*<RzBinAddr *>*/ *vec) {
|
|
PE_DWord paddr, vaddr, haddr;
|
|
int count = 0;
|
|
RzBinAddr *ptr = NULL;
|
|
struct PE_(rz_bin_pe_obj_t) *bin = (struct PE_(rz_bin_pe_obj_t) *)(bf->o->bin_obj);
|
|
char *key;
|
|
char tmpbuf[128];
|
|
|
|
do {
|
|
key = rz_strf(tmpbuf, "pe.tls_callback%d_paddr", count);
|
|
paddr = sdb_num_get(bin->kv, key);
|
|
if (!paddr) {
|
|
break;
|
|
}
|
|
|
|
key = rz_strf(tmpbuf, "pe.tls_callback%d_vaddr", count);
|
|
vaddr = sdb_num_get(bin->kv, key);
|
|
if (!vaddr) {
|
|
break;
|
|
}
|
|
|
|
key = rz_strf(tmpbuf, "pe.tls_callback%d_haddr", count);
|
|
haddr = sdb_num_get(bin->kv, key);
|
|
if (!haddr) {
|
|
break;
|
|
}
|
|
if ((ptr = RZ_NEW0(RzBinAddr))) {
|
|
ptr->paddr = paddr;
|
|
ptr->vaddr = vaddr;
|
|
ptr->hpaddr = haddr;
|
|
ptr->type = RZ_BIN_ENTRY_TYPE_TLS;
|
|
rz_pvector_push(vec, ptr);
|
|
}
|
|
count++;
|
|
} while (vaddr);
|
|
}
|
|
|
|
static RzPVector /*<RzBinAddr *>*/ *pe_entries(RzBinFile *bf) {
|
|
struct rz_bin_pe_addr_t *entry = NULL;
|
|
RzBinAddr *ptr = NULL;
|
|
RzPVector *ret;
|
|
|
|
if (!(ret = rz_pvector_new(free))) {
|
|
return NULL;
|
|
}
|
|
if (!(entry = PE_(rz_bin_pe_get_entrypoint)(bf->o->bin_obj))) {
|
|
return ret;
|
|
}
|
|
if ((ptr = RZ_NEW0(RzBinAddr))) {
|
|
ptr->paddr = entry->paddr;
|
|
ptr->vaddr = entry->vaddr;
|
|
ptr->hpaddr = entry->haddr;
|
|
ptr->type = RZ_BIN_ENTRY_TYPE_PROGRAM;
|
|
rz_pvector_push(ret, ptr);
|
|
}
|
|
free(entry);
|
|
// get TLS callback addresses
|
|
add_tls_callbacks(bf, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ut32 perm_of_section_perm(ut64 perm) {
|
|
ut32 r = 0;
|
|
if (RZ_BIN_PE_SCN_IS_EXECUTABLE(perm)) {
|
|
r |= RZ_PERM_X;
|
|
r |= RZ_PERM_R; // implicit
|
|
}
|
|
if (RZ_BIN_PE_SCN_IS_WRITABLE(perm)) {
|
|
r |= RZ_PERM_W;
|
|
}
|
|
if (RZ_BIN_PE_SCN_IS_READABLE(perm)) {
|
|
r |= RZ_PERM_R;
|
|
}
|
|
if (RZ_BIN_PE_SCN_IS_SHAREABLE(perm)) {
|
|
r |= RZ_PERM_SHAR;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static RzPVector /*<RzBinMap *>*/ *pe_maps(RzBinFile *bf) {
|
|
struct PE_(rz_bin_pe_obj_t) *bin = (struct PE_(rz_bin_pe_obj_t) *)bf->o->bin_obj;
|
|
struct rz_bin_pe_section_t *sections = NULL;
|
|
if (!bin || !(sections = bin->sections)) {
|
|
return NULL;
|
|
}
|
|
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_map_free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
ut64 ba = pe_baddr(bf);
|
|
RzBinMap *map = RZ_NEW0(RzBinMap);
|
|
if (!map) {
|
|
return ret;
|
|
}
|
|
map->name = rz_str_dup("header");
|
|
map->paddr = 0;
|
|
ut32 aligned_hdr_size = UT32_MAX;
|
|
if (bin->nt_headers->optional_header.FileAlignment != 0) {
|
|
aligned_hdr_size = RZ_ROUND(bin->nt_headers->optional_header.SizeOfHeaders, bin->nt_headers->optional_header.FileAlignment);
|
|
}
|
|
map->psize = RZ_MIN(bin->size, aligned_hdr_size);
|
|
map->vaddr = ba;
|
|
map->vsize = RZ_ROUND(map->psize, 4096);
|
|
map->perm = RZ_PERM_R;
|
|
rz_pvector_push(ret, map);
|
|
PE_(rz_bin_pe_check_sections)(bin, §ions);
|
|
for (size_t i = 0; !sections[i].last; i++) {
|
|
RzBinMap *map = RZ_NEW0(RzBinMap);
|
|
if (!map) {
|
|
break;
|
|
}
|
|
map->paddr = sections[i].paddr;
|
|
map->name = rz_str_dup((char *)sections[i].name);
|
|
map->psize = sections[i].size;
|
|
if (map->psize > bin->size) {
|
|
if (sections[i].vsize < bin->size) {
|
|
map->psize = sections[i].vsize;
|
|
} else {
|
|
// hack give it page size
|
|
map->psize = 4096;
|
|
}
|
|
}
|
|
map->vsize = sections[i].vsize;
|
|
if (!map->vsize && map->psize) {
|
|
map->vsize = map->psize;
|
|
}
|
|
map->vaddr = sections[i].vaddr + ba;
|
|
map->perm = perm_of_section_perm(sections[i].perm);
|
|
rz_pvector_push(ret, map);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static RzPVector /*<RzBinSection *>*/ *pe_sections(RzBinFile *bf) {
|
|
RzPVector *ret = NULL;
|
|
RzBinSection *ptr = NULL;
|
|
struct rz_bin_pe_section_t *sections = NULL;
|
|
struct PE_(rz_bin_pe_obj_t) *bin = (struct PE_(rz_bin_pe_obj_t) *)bf->o->bin_obj;
|
|
ut64 ba = pe_baddr(bf);
|
|
int n_nonzero = 0;
|
|
int i;
|
|
if (!(ret = rz_pvector_new((RzPVectorFree)rz_bin_section_free))) {
|
|
return NULL;
|
|
}
|
|
if (!bin || !(sections = bin->sections)) {
|
|
rz_pvector_free(ret);
|
|
return NULL;
|
|
}
|
|
PE_(rz_bin_pe_check_sections)(bin, §ions);
|
|
for (i = 0; !sections[i].last; i++) {
|
|
if (!(ptr = RZ_NEW0(RzBinSection))) {
|
|
break;
|
|
}
|
|
ptr->name = rz_str_dup((char *)sections[i].name);
|
|
ptr->size = sections[i].size;
|
|
ptr->vsize = sections[i].vsize;
|
|
ptr->flags = sections[i].flags;
|
|
ptr->paddr = sections[i].paddr;
|
|
ptr->vaddr = sections[i].vaddr + ba;
|
|
ptr->perm = perm_of_section_perm(sections[i].perm);
|
|
if ((ptr->perm & RZ_PERM_R) && !(ptr->perm & RZ_PERM_X) && ptr->size > 0) {
|
|
ptr->is_data = true;
|
|
}
|
|
if (ptr->size != 0) {
|
|
n_nonzero++;
|
|
}
|
|
rz_pvector_push(ret, ptr);
|
|
}
|
|
if (n_nonzero == 1 && ptr && ptr->perm & RZ_PERM_R) {
|
|
// if there is only one section, then we expect to have data in here also.
|
|
ptr->is_data = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void find_pe_overlay(RzBinFile *bf) {
|
|
ut64 pe_overlay_size;
|
|
ut64 pe_overlay_offset = PE_(bin_pe_get_overlay)(bf->o->bin_obj, &pe_overlay_size);
|
|
if (pe_overlay_offset) {
|
|
sdb_num_set(bf->sdb, "pe_overlay.offset", pe_overlay_offset);
|
|
sdb_num_set(bf->sdb, "pe_overlay.size", pe_overlay_size);
|
|
}
|
|
}
|
|
|
|
static inline bool is_thumb(struct PE_(rz_bin_pe_obj_t) * bin, ut64 address) {
|
|
switch (bin->nt_headers->file_header.Machine) {
|
|
case PE_IMAGE_FILE_MACHINE_ARM:
|
|
case PE_IMAGE_FILE_MACHINE_ARMNT:
|
|
return address & 1;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_go_pclntab(ut8 *magic) {
|
|
#define IS_GOPCLNTAB_1_2_LE(x) (x[0] == 0xfb && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_2_BE(x) (x[3] == 0xfb && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_16_LE(x) (x[0] == 0xfa && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_16_BE(x) (x[3] == 0xfa && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_18_LE(x) (x[0] == 0xf0 && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_18_BE(x) (x[3] == 0xf0 && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_20_LE(x) (x[0] == 0xf1 && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
#define IS_GOPCLNTAB_1_20_BE(x) (x[3] == 0xf1 && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff && x[4] == 0x00 && x[5] == 0x00)
|
|
return IS_GOPCLNTAB_1_2_LE(magic) || IS_GOPCLNTAB_1_2_BE(magic) ||
|
|
IS_GOPCLNTAB_1_16_LE(magic) || IS_GOPCLNTAB_1_16_BE(magic) ||
|
|
IS_GOPCLNTAB_1_18_LE(magic) || IS_GOPCLNTAB_1_18_BE(magic) ||
|
|
IS_GOPCLNTAB_1_20_LE(magic) || IS_GOPCLNTAB_1_20_BE(magic);
|
|
#undef IS_GOPCLNTAB_1_2_LE
|
|
#undef IS_GOPCLNTAB_1_2_BE
|
|
#undef IS_GOPCLNTAB_1_16_LE
|
|
#undef IS_GOPCLNTAB_1_16_BE
|
|
#undef IS_GOPCLNTAB_1_18_LE
|
|
#undef IS_GOPCLNTAB_1_18_BE
|
|
#undef IS_GOPCLNTAB_1_20_LE
|
|
#undef IS_GOPCLNTAB_1_20_BE
|
|
}
|
|
|
|
static ut64 find_go_pclntab(RzBinFile *bf, ut32 *size, ut64 *vaddr) {
|
|
ut8 magic[16];
|
|
struct PE_(rz_bin_pe_obj_t) *bin = (struct PE_(rz_bin_pe_obj_t) *)bf->o->bin_obj;
|
|
struct rz_bin_pe_section_t *sections = bin->sections;
|
|
if (!sections) {
|
|
return 0;
|
|
}
|
|
ut64 ba = pe_baddr(bf);
|
|
for (int i = 0; !sections[i].last; i++) {
|
|
if (!strstr((char *)sections[i].name, "data")) {
|
|
continue;
|
|
}
|
|
ut64 offset = sections[i].paddr;
|
|
ut32 section_size = sections[i].size;
|
|
for (ut32 pos = 0; pos < section_size; pos += 8) {
|
|
if ((section_size - pos) < 16) {
|
|
break;
|
|
}
|
|
rz_buf_read_at(bf->buf, offset + pos, magic, sizeof(magic));
|
|
if ((magic[6] != 1 && magic[6] != 2 && magic[6] != 4) || // pc quantum
|
|
(magic[7] != 4 && magic[7] != 8)) { // pointer size
|
|
continue;
|
|
}
|
|
if (is_go_pclntab(magic)) {
|
|
if (size) {
|
|
*size = section_size - pos;
|
|
}
|
|
if (vaddr) {
|
|
*vaddr = ba + sections[i].vaddr + pos;
|
|
}
|
|
return offset + pos;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static RzPVector /*<RzBinSymbol *>*/ *pe_symbols(RzBinFile *bf) {
|
|
RzPVector *ret = NULL;
|
|
ut64 go_pclntab_paddr = 0;
|
|
ut64 go_pclntab_vaddr = 0;
|
|
ut32 go_pclntab_size = 0;
|
|
RzBinSymbol *ptr = NULL;
|
|
struct rz_bin_pe_export_t *symbols = NULL;
|
|
struct rz_bin_pe_import_t *imports = NULL;
|
|
struct PE_(rz_bin_pe_obj_t) *bin = bf->o->bin_obj;
|
|
int i;
|
|
|
|
if (!(ret = rz_pvector_new((RzPVectorFree)rz_bin_symbol_free))) {
|
|
return NULL;
|
|
}
|
|
int file_bits = PE_(rz_bin_pe_get_bits(bin));
|
|
if ((symbols = PE_(rz_bin_pe_get_exports)(bf->o->bin_obj))) {
|
|
for (i = 0; !symbols[i].last; i++) {
|
|
if (!(ptr = RZ_NEW0(RzBinSymbol))) {
|
|
break;
|
|
}
|
|
ptr->name = rz_str_dup((char *)symbols[i].name);
|
|
ptr->libname = RZ_STR_ISNOTEMPTY(symbols[i].libname) ? rz_str_dup((char *)symbols[i].libname) : NULL;
|
|
ptr->forwarder = rz_str_constpool_get(&bf->rbin->constpool, (char *)symbols[i].forwarder);
|
|
// strncpy (ptr->bind, "NONE", RZ_BIN_SIZEOF_STRINGS);
|
|
ptr->bind = RZ_BIN_BIND_GLOBAL_STR;
|
|
ptr->type = RZ_BIN_TYPE_FUNC_STR;
|
|
ptr->size = 0;
|
|
if (is_thumb(bin, symbols[i].vaddr)) {
|
|
ptr->bits = 16;
|
|
ptr->vaddr = symbols[i].vaddr - 1;
|
|
ptr->paddr = symbols[i].paddr - 1;
|
|
} else {
|
|
ptr->bits = file_bits;
|
|
ptr->vaddr = symbols[i].vaddr;
|
|
ptr->paddr = symbols[i].paddr;
|
|
}
|
|
ptr->ordinal = symbols[i].ordinal;
|
|
rz_pvector_push(ret, ptr);
|
|
}
|
|
free(symbols);
|
|
}
|
|
|
|
if ((go_pclntab_paddr = find_go_pclntab(bf, &go_pclntab_size, &go_pclntab_vaddr))) {
|
|
if (!(ptr = RZ_NEW0(RzBinSymbol))) {
|
|
return ret;
|
|
}
|
|
ptr->name = rz_str_dup("gopclntab");
|
|
ptr->bind = RZ_BIN_BIND_GLOBAL_STR;
|
|
ptr->type = RZ_BIN_TYPE_NOTYPE_STR;
|
|
ptr->size = go_pclntab_size;
|
|
ptr->bits = file_bits;
|
|
ptr->vaddr = go_pclntab_vaddr;
|
|
ptr->paddr = go_pclntab_paddr;
|
|
rz_pvector_push(ret, ptr);
|
|
}
|
|
|
|
if ((imports = PE_(rz_bin_pe_get_imports)(bf->o->bin_obj))) {
|
|
for (i = 0; !imports[i].last; i++) {
|
|
if (!(ptr = RZ_NEW0(RzBinSymbol))) {
|
|
break;
|
|
}
|
|
// strncpy (ptr->name, (char*)symbols[i].name, RZ_BIN_SIZEOF_STRINGS);
|
|
ptr->name = rz_str_dup((const char *)imports[i].name);
|
|
ptr->libname = rz_str_dup((const char *)imports[i].libname);
|
|
ptr->is_imported = true;
|
|
// strncpy (ptr->forwarder, (char*)imports[i].forwarder, RZ_BIN_SIZEOF_STRINGS);
|
|
ptr->bind = "NONE";
|
|
ptr->type = RZ_BIN_TYPE_FUNC_STR;
|
|
ptr->size = 0;
|
|
ptr->vaddr = imports[i].vaddr;
|
|
ptr->paddr = imports[i].paddr;
|
|
ptr->ordinal = imports[i].ordinal;
|
|
rz_pvector_push(ret, ptr);
|
|
}
|
|
free(imports);
|
|
}
|
|
|
|
// CLR symbols
|
|
RzList *clr_symbols = PE_(rz_bin_pe_get_clr_symbols)(bf->o->bin_obj);
|
|
RzListIter *iter;
|
|
RzBinSymbol *sym;
|
|
if (clr_symbols) {
|
|
rz_list_foreach (clr_symbols, iter, sym) {
|
|
rz_pvector_push(ret, sym);
|
|
}
|
|
clr_symbols->length = 0;
|
|
clr_symbols->head = clr_symbols->tail = NULL;
|
|
rz_list_free(clr_symbols);
|
|
}
|
|
|
|
find_pe_overlay(bf);
|
|
return ret;
|
|
}
|
|
|
|
static void filter_import(ut8 *n) {
|
|
int I;
|
|
for (I = 0; n[I]; I++) {
|
|
if (n[I] < 30 || n[I] >= 0x7f) {
|
|
n[I] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static RzPVector /*<RzBinImport *>*/ *pe_imports(RzBinFile *bf) {
|
|
RzBinImport *ptr = NULL;
|
|
struct rz_bin_pe_import_t *imports = NULL;
|
|
int i;
|
|
|
|
if (!bf || !bf->o || !bf->o->bin_obj) {
|
|
return NULL;
|
|
}
|
|
RzBinPEObj *bin = (RzBinPEObj *)bf->o->bin_obj;
|
|
bin->has_canary = false;
|
|
RzPVector *ret = rz_pvector_new((RzListFree)rz_bin_import_free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!(imports = PE_(rz_bin_pe_get_imports)(bin))) {
|
|
return ret;
|
|
}
|
|
bool has_canary = false;
|
|
for (i = 0; !imports[i].last; i++) {
|
|
if (!(ptr = RZ_NEW0(RzBinImport))) {
|
|
break;
|
|
}
|
|
filter_import(imports[i].name);
|
|
ptr->name = rz_str_dup((char *)imports[i].name);
|
|
ptr->libname = rz_str_dup((char *)imports[i].libname);
|
|
ptr->bind = "NONE";
|
|
ptr->type = "FUNC";
|
|
ptr->ordinal = imports[i].ordinal;
|
|
// NOTE(eddyb) a PE hint is just an optional possible DLL export table
|
|
// index for the import. There is no point in exposing it.
|
|
// ptr->hint = imports[i].hint;
|
|
rz_pvector_push(ret, ptr);
|
|
|
|
// __security_init_cookie is a function imported from msvcrt.dll (libc) that when called
|
|
// initiliazes the stack canary. So if the function is imported, we can
|
|
// conclude the binary uses stack canary.
|
|
if (!has_canary && !strcmp(ptr->name, "__security_init_cookie")) {
|
|
has_canary = true;
|
|
}
|
|
}
|
|
bin->has_canary = has_canary;
|
|
free(imports);
|
|
return ret;
|
|
}
|
|
|
|
static const char *get_reloc_type_name(const ut8 reloc_type, const ut32 machine_type) {
|
|
switch (reloc_type) {
|
|
case PE_IMAGE_REL_BASED_ABSOLUTE:
|
|
return "IMAGE_REL_BASED_ABSOLUTE";
|
|
case PE_IMAGE_REL_BASED_HIGH:
|
|
return "IMAGE_REL_BASED_HIGH";
|
|
case PE_IMAGE_REL_BASED_LOW:
|
|
return "IMAGE_REL_BASED_LOW";
|
|
case PE_IMAGE_REL_BASED_HIGHLOW:
|
|
return "IMAGE_REL_BASED_HIGHLOW";
|
|
case PE_IMAGE_REL_BASED_HIGHADJ:
|
|
return "IMAGE_REL_BASED_HIGHADJ";
|
|
case PE_IMAGE_REL_BASED_MIPS_JMPADDR_ARM_MOV32_RISCV_HIGH20:
|
|
switch (machine_type) {
|
|
case PE_IMAGE_FILE_MACHINE_MIPS16:
|
|
case PE_IMAGE_FILE_MACHINE_MIPSFPU:
|
|
case PE_IMAGE_FILE_MACHINE_MIPSFPU16:
|
|
case PE_IMAGE_FILE_MACHINE_R4000:
|
|
case PE_IMAGE_FILE_MACHINE_WCEMIPSV2:
|
|
return "IMAGE_REL_BASED_MIPS_JMPADDR";
|
|
case PE_IMAGE_FILE_MACHINE_ARM:
|
|
case PE_IMAGE_FILE_MACHINE_ARM64:
|
|
case PE_IMAGE_FILE_MACHINE_ARMNT:
|
|
case PE_IMAGE_FILE_MACHINE_THUMB:
|
|
return "IMAGE_REL_BASED_ARM_MOV32";
|
|
case PE_IMAGE_FILE_MACHINE_RISCV32:
|
|
case PE_IMAGE_FILE_MACHINE_RISCV64:
|
|
case PE_IMAGE_FILE_MACHINE_RISCV128:
|
|
return "IMAGE_REL_BASED_RISCV_HIGH20";
|
|
default:
|
|
RZ_LOG_WARN("Unknown machine type for PE relocation type number: %d\n", reloc_type);
|
|
return "IMAGE_REL_BASED_UNKNOWN";
|
|
}
|
|
|
|
case PE_IMAGE_REL_BASED_THUMB_MOV32_RISCV_LOW12I:
|
|
switch (machine_type) {
|
|
case PE_IMAGE_FILE_MACHINE_THUMB:
|
|
return "IMAGE_REL_BASED_THUMB_MOV32";
|
|
case PE_IMAGE_FILE_MACHINE_RISCV32:
|
|
case PE_IMAGE_FILE_MACHINE_RISCV64:
|
|
case PE_IMAGE_FILE_MACHINE_RISCV128:
|
|
return "IMAGE_REL_BASED_RISCV_LOW12I";
|
|
default:
|
|
RZ_LOG_WARN("Unknown machine type for PE relocation type number: %d\n", reloc_type);
|
|
return "IMAGE_REL_BASED_UNKNOWN";
|
|
}
|
|
case PE_IMAGE_REL_BASED_RISCV_LOW12S_LOONGARCH32_MARK_LA:
|
|
switch (machine_type) {
|
|
case PE_IMAGE_FILE_MACHINE_RISCV32:
|
|
case PE_IMAGE_FILE_MACHINE_RISCV64:
|
|
case PE_IMAGE_FILE_MACHINE_RISCV128:
|
|
return "IMAGE_REL_BASED_RISCV_LOW12S";
|
|
case PE_IMAGE_FILE_MACHINE_LOONGARCH32:
|
|
case PE_IMAGE_FILE_MACHINE_LOONGARCH64:
|
|
return "IMAGE_REL_BASED_LOONGARCH32_MARK_LA";
|
|
default:
|
|
RZ_LOG_WARN("Unknown machine type for PE relocation type number: %d\n", reloc_type);
|
|
return "IMAGE_REL_BASED_UNKNOWN";
|
|
}
|
|
case PE_IMAGE_REL_BASED_MIPS_JMPADDR16:
|
|
switch (machine_type) {
|
|
case PE_IMAGE_FILE_MACHINE_MIPS16:
|
|
case PE_IMAGE_FILE_MACHINE_MIPSFPU:
|
|
case PE_IMAGE_FILE_MACHINE_MIPSFPU16:
|
|
case PE_IMAGE_FILE_MACHINE_R4000:
|
|
case PE_IMAGE_FILE_MACHINE_WCEMIPSV2:
|
|
return "IMAGE_REL_BASED_MIPS_JMPADDR16";
|
|
default:
|
|
RZ_LOG_WARN("Unknown machine type for PE relocation type number: %d\n", reloc_type);
|
|
return "IMAGE_REL_BASED_UNKNOWN";
|
|
}
|
|
case PE_IMAGE_REL_BASED_DIR64:
|
|
return "IMAGE_REL_BASED_DIR64";
|
|
default:
|
|
RZ_LOG_WARN("Unknown PE relocation type number: %d\n", reloc_type);
|
|
return "IMAGE_REL_BASED_UNKNOWN";
|
|
}
|
|
}
|
|
|
|
// NOTE: No patching is needed because base relocations (as their name suggests) patch relocations based off the delta
|
|
// between the executable expected load address, and the actual load address. And since Rizin always loads
|
|
// the binaries at their preferred base address, the delta is always 0 and so no patching is needed.
|
|
static RzPVector /*<RzBinReloc *>*/ *pe_relocs(RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
RzBinPEObj *bin = bf->o->bin_obj;
|
|
|
|
if (!bin->relocs) {
|
|
return NULL;
|
|
}
|
|
|
|
RzPVector *ret = NULL;
|
|
if (!(ret = rz_pvector_new((RzPVectorFree)rz_bin_reloc_free))) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!PE_(bin_pe_has_base_relocs)(bin)) {
|
|
rz_pvector_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
RzBinPeRelocEnt *pe_reloc;
|
|
rz_vector_foreach (bin->relocs, pe_reloc) {
|
|
RzBinReloc *general_reloc = RZ_NEW0(RzBinReloc);
|
|
if (!general_reloc) {
|
|
RZ_LOG_ERROR("Failed to allocate PE RzBinReloc\n");
|
|
return ret;
|
|
}
|
|
|
|
general_reloc->vaddr = PE_RELOC_ENT_OFFSET(pe_reloc->raw_val) + pe_reloc->page_rva;
|
|
general_reloc->paddr = PE_(bin_pe_rva_to_paddr(bin, general_reloc->vaddr));
|
|
general_reloc->type = PE_RELOC_ENT_TYPE(pe_reloc->raw_val);
|
|
general_reloc->print_name = get_reloc_type_name(PE_RELOC_ENT_TYPE(pe_reloc->raw_val), bin->nt_headers->file_header.Machine);
|
|
|
|
// Allocating symbol just so we can have a name for the reloc
|
|
general_reloc->symbol = RZ_NEW0(RzBinSymbol);
|
|
general_reloc->symbol->name = rz_str_newf("pe_%08" PFMT64x, general_reloc->vaddr);
|
|
|
|
rz_pvector_push(ret, general_reloc);
|
|
}
|
|
|
|
rz_vector_free(bin->relocs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static RzPVector /*<char *>*/ *pe_libs(RzBinFile *bf) {
|
|
return PE_(rz_bin_pe_get_libs)(bf->o->bin_obj);
|
|
}
|
|
|
|
static RzPVector /*<RzBinResource *>*/ *pe_resources(RzBinFile *bf) {
|
|
struct PE_(rz_bin_pe_obj_t) *obj = bf->o->bin_obj;
|
|
if (!obj) {
|
|
return NULL;
|
|
}
|
|
|
|
RzPVector *res = rz_pvector_new((RzPVectorFree)rz_bin_resource_free);
|
|
if (!res) {
|
|
return NULL;
|
|
}
|
|
rz_pe_resource *rs;
|
|
RzListIter *it;
|
|
size_t index = 0;
|
|
rz_list_foreach (obj->resources, it, rs) {
|
|
RzBinResource *br = RZ_NEW0(RzBinResource);
|
|
if (!br) {
|
|
goto err;
|
|
}
|
|
|
|
br->index = index++;
|
|
br->name = rz_str_dup(rs->name);
|
|
if (!br->name) {
|
|
rz_bin_resource_free(br);
|
|
goto err;
|
|
}
|
|
br->time = rz_str_dup(rs->timestr);
|
|
if (!br->time) {
|
|
rz_bin_resource_free(br);
|
|
goto err;
|
|
}
|
|
br->vaddr = PE_(rz_bin_pe_get_image_base)(obj) + rs->data->OffsetToData;
|
|
br->size = rs->data->Size;
|
|
br->type = rz_str_dup(rs->type);
|
|
if (!br->type) {
|
|
rz_bin_resource_free(br);
|
|
goto err;
|
|
}
|
|
br->language = rz_str_dup(rs->language);
|
|
if (!br->language) {
|
|
rz_bin_resource_free(br);
|
|
goto err;
|
|
}
|
|
|
|
rz_pvector_push(res, br);
|
|
}
|
|
return res;
|
|
|
|
err:
|
|
rz_pvector_free(res);
|
|
return NULL;
|
|
}
|
|
|
|
static inline bool haschr(const struct PE_(rz_bin_pe_obj_t) * bin, ut16 dllCharacteristic) {
|
|
return bin->nt_headers->optional_header.DllCharacteristics & dllCharacteristic;
|
|
}
|
|
|
|
static int compare_strings(const void *a, const void *b, RZ_UNUSED void *user) {
|
|
return strcmp((const char *)a, (const char *)b);
|
|
}
|
|
|
|
static RzBinInfo *pe_info(RzBinFile *bf) {
|
|
struct PE_(rz_bin_pe_obj_t) * bin;
|
|
SDebugInfo di = { { 0 } };
|
|
RzBinInfo *ret = RZ_NEW0(RzBinInfo);
|
|
ut32 claimed_checksum, actual_checksum, pe_overlay;
|
|
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
bin = bf->o->bin_obj;
|
|
ret->file = rz_str_dup(bf->file);
|
|
ret->bclass = PE_(rz_bin_pe_get_class)(bf->o->bin_obj);
|
|
ret->rclass = rz_str_dup("pe");
|
|
ret->os = PE_(rz_bin_pe_get_os)(bf->o->bin_obj);
|
|
ret->arch = PE_(rz_bin_pe_get_arch)(bf->o->bin_obj);
|
|
ret->cpu = PE_(rz_bin_pe_get_cpu)(bf->o->bin_obj);
|
|
ret->machine = PE_(rz_bin_pe_get_machine)(bf->o->bin_obj);
|
|
ret->subsystem = PE_(rz_bin_pe_get_subsystem)(bf->o->bin_obj);
|
|
ret->default_cc = PE_(rz_bin_pe_get_cc)(bf->o->bin_obj);
|
|
ret->compiler = PE_(rz_bin_pe_get_compiler)(bf->o->bin_obj);
|
|
|
|
RzPVector *libs = PE_(rz_bin_pe_get_libs)(bf->o->bin_obj);
|
|
if (libs && rz_pvector_find(libs, "mscoree.dll", compare_strings, NULL)) {
|
|
ret->lang = "cil";
|
|
}
|
|
if (libs && rz_pvector_find(libs, "msvbvm60.dll", compare_strings, NULL)) {
|
|
ret->lang = "vb";
|
|
}
|
|
rz_pvector_free(libs);
|
|
|
|
if (PE_(rz_bin_pe_is_dll)(bf->o->bin_obj)) {
|
|
ret->type = rz_str_dup("DLL (Dynamic Link Library)");
|
|
} else {
|
|
ret->type = rz_str_dup("EXEC (Executable file)");
|
|
}
|
|
claimed_checksum = PE_(bin_pe_get_claimed_checksum)(bf->o->bin_obj);
|
|
actual_checksum = PE_(bin_pe_get_actual_checksum)(bf->o->bin_obj);
|
|
pe_overlay = sdb_num_get(bf->sdb, "pe_overlay.size");
|
|
ret->bits = PE_(rz_bin_pe_get_bits)(bf->o->bin_obj);
|
|
ret->big_endian = PE_(rz_bin_pe_is_big_endian)(bf->o->bin_obj);
|
|
ret->dbg_info = 0;
|
|
ret->has_canary = PE_(rz_bin_pe_has_canary)(bf->o->bin_obj);
|
|
ret->has_nx = haschr(bin, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT);
|
|
ret->has_pi = haschr(bin, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE);
|
|
ret->claimed_checksum = rz_str_newf("0x%08x", claimed_checksum);
|
|
ret->actual_checksum = rz_str_newf("0x%08x", actual_checksum);
|
|
ret->pe_overlay = pe_overlay > 0;
|
|
ret->signature = bin ? bin->is_signed : false;
|
|
Sdb *db = sdb_ns(bf->sdb, "pe", true);
|
|
sdb_bool_set(db, "canary", ret->has_canary);
|
|
sdb_bool_set(db, "highva", haschr(bin, IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA));
|
|
sdb_bool_set(db, "aslr", haschr(bin, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE));
|
|
sdb_bool_set(db, "forceintegrity", haschr(bin, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY));
|
|
sdb_bool_set(db, "nx", haschr(bin, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT));
|
|
sdb_bool_set(db, "isolation", !haschr(bin, IMAGE_DLLCHARACTERISTICS_NO_ISOLATION));
|
|
sdb_bool_set(db, "seh", !haschr(bin, IMAGE_DLLCHARACTERISTICS_NO_SEH));
|
|
sdb_bool_set(db, "bind", !haschr(bin, IMAGE_DLLCHARACTERISTICS_NO_BIND));
|
|
sdb_bool_set(db, "appcontainer", haschr(bin, IMAGE_DLLCHARACTERISTICS_APPCONTAINER));
|
|
sdb_bool_set(db, "wdmdriver", haschr(bin, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER));
|
|
sdb_bool_set(db, "guardcf", haschr(bin, IMAGE_DLLCHARACTERISTICS_GUARD_CF));
|
|
sdb_bool_set(db, "terminalserveraware", haschr(bin, IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE));
|
|
sdb_num_set(db, "bits", ret->bits);
|
|
sdb_set(db, "claimed_checksum", ret->claimed_checksum);
|
|
sdb_set(db, "actual_checksum", ret->actual_checksum);
|
|
sdb_bool_set(db, "is_authhash_valid", PE_(bin_pe_is_authhash_valid)(bf->o->bin_obj));
|
|
|
|
ret->has_va = true;
|
|
|
|
if (PE_(rz_bin_pe_is_stripped_debug)(bf->o->bin_obj)) {
|
|
ret->dbg_info |= RZ_BIN_DBG_STRIPPED;
|
|
}
|
|
if (PE_(rz_bin_pe_is_stripped_line_nums)(bf->o->bin_obj)) {
|
|
ret->dbg_info |= RZ_BIN_DBG_LINENUMS;
|
|
}
|
|
if (PE_(rz_bin_pe_is_stripped_local_syms)(bf->o->bin_obj)) {
|
|
ret->dbg_info |= RZ_BIN_DBG_SYMS;
|
|
}
|
|
if (PE_(rz_bin_pe_is_stripped_relocs)(bf->o->bin_obj)) {
|
|
ret->dbg_info |= RZ_BIN_DBG_RELOCS;
|
|
}
|
|
if (PE_(rz_bin_pe_get_debug_data)(bf->o->bin_obj, &di)) {
|
|
ret->guid = rz_str_ndup(di.guidstr, GUIDSTR_LEN);
|
|
if (ret->guid) {
|
|
ret->debug_file_name = rz_str_ndup(di.file_name, DBG_FILE_NAME_LEN);
|
|
if (!ret->debug_file_name) {
|
|
RZ_FREE(ret->guid);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ut64 pe_get_vaddr(RzBinFile *bf, ut64 baddr, ut64 paddr, ut64 vaddr) {
|
|
return baddr + vaddr;
|
|
}
|
|
|
|
static RzPVector /*<RzBinFileHash *>*/ *pe_compute_hashes(RzBinFile *bf) {
|
|
RzPVector *file_hashes = rz_pvector_new((RzPVectorFree)rz_bin_file_hash_free);
|
|
const char *authentihash = PE_(bin_pe_get_authentihash)(bf->o->bin_obj);
|
|
if (authentihash) {
|
|
RzBinFileHash *authhash = RZ_NEW0(RzBinFileHash);
|
|
if (authhash) {
|
|
authhash->type = rz_str_dup("authentihash");
|
|
authhash->hex = rz_str_dup(authentihash);
|
|
rz_pvector_push(file_hashes, authhash);
|
|
}
|
|
}
|
|
|
|
return file_hashes;
|
|
}
|
|
|
|
/**
|
|
* Currently only used by CIL plugin to resolve `call` and `jmp` targets
|
|
* \returns offset on success, UT64_MAX on fail
|
|
*/
|
|
static ut64 pe_get_offset(RzBinFile *bf, int type, int idx) {
|
|
struct PE_(rz_bin_pe_obj_t) * bin;
|
|
Pe_image_clr *clr;
|
|
|
|
if (!bf || !bf->o ||
|
|
!(bin = bf->o->bin_obj) ||
|
|
!(clr = bin->clr)) {
|
|
return UT64_MAX;
|
|
}
|
|
|
|
if (type == 'd') { // MethodDef index
|
|
Pe_image_metadata_methoddef *methoddef = rz_pvector_at(clr->methoddefs, idx - 1);
|
|
return PE_(rz_bin_pe_get_clr_methoddef_offset)(bin, methoddef);
|
|
}
|
|
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static RzPVector /*<RzBinString *>*/ *pe_strings(RzBinFile *bf) {
|
|
RzBinStringSearchOpt opt;
|
|
rz_bin_string_search_opt_init(&opt);
|
|
opt.mode = RZ_BIN_STRING_SEARCH_MODE_AUTO;
|
|
opt.min_length = RZ_BIN_STRING_SEARCH_MIN_STRING;
|
|
// Sticking with string guessing for now.
|
|
// PE doesn't specifiy the encoding, but commonly it is UTF-8 for symbol names
|
|
// and such, but UTF-16-le for Windows strings.
|
|
// Doing two searches here is easily possible
|
|
// but breaks a few tests which need investigation.
|
|
// So it should be done in a separated PR.
|
|
opt.string_encoding = RZ_STRING_ENC_GUESS;
|
|
|
|
RzPVector *strings = rz_bin_file_strings(bf, &opt);
|
|
if (!strings) {
|
|
RZ_LOG_ERROR("Failed to search for strings. Abort.\n");
|
|
return NULL;
|
|
}
|
|
return strings;
|
|
}
|