Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implement PE & MDMP base relocations. DO NOT MERGE YET! #4711

Draft
wants to merge 13 commits into
base: dev
Choose a base branch
from
14 changes: 10 additions & 4 deletions librz/bin/format/pe/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ typedef struct rz_bin_pe_reloc_block_t {
ut32 block_size;
} RzBinPeRelocBlock;

// encoding is 12 bits for type and 4 bits for offset.
// encoding is 12 bits for type and 4 bits for offset.
// See: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-reloc-section-image-only
#define PE_RELOC_ENT_TYPE(val) ((val) >> 12)
#define PE_RELOC_ENT_OFFSET(val) ((val)&0b111111111111)
typedef ut16 RzBinPeRelocEnt;
#define PE_RELOC_ENT_OFFSET(val) ((val)&0xfff)

typedef struct rz_bin_pe_reloc_ent_t {
wargio marked this conversation as resolved.
Show resolved Hide resolved
ut16 raw_val;
ut32 page_rva;
} RzBinPeRelocEnt;

struct rz_bin_pe_addr_t {
ut64 vaddr;
Expand Down Expand Up @@ -183,6 +187,7 @@ struct PE_(rz_bin_pe_obj_t) {
RzVector /*<RzBinPeRelocEnt>*/ *relocs;
wargio marked this conversation as resolved.
Show resolved Hide resolved
const char *file;
RzBuffer *b;
RzBuffer *buf_patched; // overlay over the original file with relocs patched
Sdb *kv;
RzCMS *cms;
RzSpcIndirectDataContent *spcinfo;
Expand Down Expand Up @@ -219,7 +224,8 @@ int PE_(read_image_delay_import_directory)(RzBuffer *b, ut64 addr, PE_(image_del
struct rz_bin_pe_import_t *PE_(rz_bin_pe_get_imports)(RzBinPEObj *bin);

// pe_relocs.c
int PE_(bin_pe_init_relocs)(RzBinPEObj *bin);
int PE_(bin_pe_init_relocs)(RZ_NONNULL RzBinPEObj *bin);
bool PE_(bin_pe_has_base_relocs)(RZ_NONNULL RzBinPEObj *bin);

// pe_info.c
char *PE_(rz_bin_pe_get_arch)(RzBinPEObj *bin);
Expand Down
33 changes: 18 additions & 15 deletions librz/bin/format/pe/pe_relocs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
// SPDX-License-Identifier: LGPL-3.0-only

#include "pe.h"
XVilka marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave 1 empty line between SPDX headers and first #include

#include <rz_util/ht_uu.h>

static bool read_reloc_ent_from_block(RZ_NONNULL RzVector /*<RzBinPeRelocEnt>*/ *relocs, RzBuffer *b, ut64* offset, const int big_endian, const ut32 reloc_block_end) {
bool PE_(bin_pe_has_base_relocs)(RZ_NONNULL RzBinPEObj *bin) {
rz_return_val_if_fail(bin, false);

return bin->relocs && (rz_vector_len(bin->relocs) > 0);
}

static bool read_reloc_ent_from_block(RZ_NONNULL RzVector /*<RzBinPeRelocEnt>*/ *relocs, RzBuffer *b, RzBinPeRelocBlock *block, ut64 *offset, const int big_endian) {
const ut32 reloc_block_end = *offset + block->block_size - 8; // block size includes the size of the next blocks entry, which is 8 bytes long
do {
RzBinPeRelocEnt reloc = 0;
if (!rz_buf_read_ble16_offset(b, offset, &reloc, big_endian)) {
RzBinPeRelocEnt reloc = { 0 };
if (!rz_buf_read_ble16_offset(b, offset, &reloc.raw_val, big_endian)) {
return false;
}
if (reloc == 0) { // we reached last 2 bytes of the block. these are zeroed padding bytes
return true;
}
reloc.page_rva = block->page_rva;

rz_vector_push(relocs, &reloc);

Expand All @@ -24,17 +28,16 @@ static bool read_reloc_ent_from_block(RZ_NONNULL RzVector /*<RzBinPeRelocEnt>*/
static bool get_relocs_from_data_dir(RZ_NONNULL RzBinPEObj *bin, RZ_BORROW RZ_NONNULL RzVector /*<RzBinPeRelocEnt>*/ *relocs) {
RzBuffer *b = bin->b;
const st64 o_addr = rz_buf_tell(b);

// get offset in file of first reloc block
ut64 offset = PE_(bin_pe_rva_to_paddr)(bin, bin->nt_headers->optional_header.DataDirectory[PE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
const ut64 relocs_end_offset = offset + bin->nt_headers->optional_header.DataDirectory[PE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

do {
RzBinPeRelocBlock block = { 0 };

if (!rz_buf_read_ble32_offset(b, &offset, &block.page_rva, bin->big_endian) ||
if (!rz_buf_read_ble32_offset(b, &offset, &block.page_rva, bin->big_endian) ||
!rz_buf_read_ble32_offset(b, &offset, &block.block_size, bin->big_endian) ||
!read_reloc_ent_from_block(relocs, b, &offset, bin->big_endian, offset + block.block_size)) {
!read_reloc_ent_from_block(relocs, b, &block, &offset, bin->big_endian)) {
return false;
}

Expand All @@ -49,16 +52,16 @@ int PE_(bin_pe_init_relocs)(RZ_NONNULL RzBinPEObj *bin) {
rz_return_val_if_fail(bin, false);

RzVector *ret = rz_vector_new(sizeof(RzBinPeRelocEnt), NULL, NULL);
bin->relocs = ret;
if (!ret) {
return false;
}

if (!get_relocs_from_data_dir(bin, ret)) {
rz_vector_free(ret);
if (PE_(rz_bin_pe_is_stripped_relocs)(bin) || !get_relocs_from_data_dir(bin, ret)) {
rz_vector_free(bin->relocs);
bin->relocs = NULL;
return false;
}

bin->relocs = ret;

return true;
}
44 changes: 19 additions & 25 deletions librz/bin/p/bin_pe.inc
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ static RzPVector /*<RzBinImport *>*/ *imports(RzBinFile *bf) {
return ret;
}

static const char *get_reloc_type_name(ut32 reloc_type, ut32 machine_type) {
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";
Expand Down Expand Up @@ -535,48 +535,42 @@ static const char *get_reloc_type_name(ut32 reloc_type, ut32 machine_type) {
}
}

static RzBinReloc *reloc_convert_pe_to_general(RzBinPEObj *bin, RzBinPeRelocEnt *pe_reloc) {
// XXX: Shouldn't we maybe allocate this sequentially instead of calling calloc for each reloc?
RzBinReloc *general_reloc = RZ_NEW0(RzBinReloc);
if (!general_reloc) {
rz_bin_reloc_free(general_reloc);
return NULL;
}

general_reloc->vaddr = PE_RELOC_ENT_OFFSET(*pe_reloc);
general_reloc->paddr = PE_RELOC_ENT_OFFSET(*pe_reloc) + PE_(rz_bin_pe_get_image_base)(bin);
general_reloc->type = PE_RELOC_ENT_TYPE(*pe_reloc);
general_reloc->print_name = get_reloc_type_name(PE_RELOC_ENT_TYPE(*pe_reloc), bin->nt_headers->file_header.Machine);

return general_reloc;
}

static RzPVector /*<RzBinReloc *>*/ *relocs(RzBinFile *bf) {
// 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.
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
RzPVector *ret = NULL;

RzBinPEObj *bin = bf->o->bin_obj;

// TODO: patch relocs
if (bin->relocs == NULL) {
wargio marked this conversation as resolved.
Show resolved Hide resolved
return NULL;
}

RzPVector *ret = NULL;
if (!(ret = rz_pvector_new((RzPVectorFree)rz_bin_reloc_free))) {
return NULL;
}

// stripped relocs means that the binary has no base relocations
if (!PE_(rz_bin_pe_is_stripped_relocs)((RzBinPEObj *)bin)) {
if (PE_(bin_pe_has_base_relocs)(bin)) {
wargio marked this conversation as resolved.
Show resolved Hide resolved
RzBinPeRelocEnt *pe_reloc;
rz_vector_foreach (bin->relocs, pe_reloc) {
RzBinReloc *general_reloc;
if (!(general_reloc = reloc_convert_pe_to_general(bin, pe_reloc))) {
RzBinReloc *general_reloc = RZ_NEW0(RzBinReloc);
if (!general_reloc) {
RZ_LOG_WARN("Failed to convert PE reloc to general reloc\n");
rz_bin_reloc_free(general_reloc);
continue;
}

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);

rz_pvector_push(ret, general_reloc);
}
}

// TODO: take care of COFF relocs
rz_vector_free(bin->relocs);

return ret;
}
Expand Down
16 changes: 0 additions & 16 deletions test/db/formats/pe/imports_tinyW7
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,3 @@ CMDS=q!
EXPECT=<<EOF
EOF
RUN

NAME=PE: resolve reloc name by ordinal from sdb
FILE=bins/pe/imports_tinyW7.exe
CMDS=<<EOF
ir
ii
EOF
EXPECT=<<EOF
vaddr paddr type name
----------------------
nth vaddr bind type lib name
------------------------------------------------
284 0x00401048 NONE FUNC kernel32 Ordinal_284
1268 0x00401034 NONE FUNC msvcrt Ordinal_1268
EOF
RUN
Loading
Loading