rizin/librz/bin/p/bin_pe.inc
billow 7a77b8f7bd
linter: update clang-format installation to version 20 (#5451)
* 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
2025-10-12 08:02:03 +08:00

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, &sections);
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, &sections);
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;
}