Skip to content

Commit

Permalink
Implement PE base relocations
Browse files Browse the repository at this point in the history
1. Everything is based off of Microsoft's PE format documentation [found here](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format)
2. Up until now, imports were used for relocations in the PE & MDMP format,
   which is wrong. PE uses base relocations.
3. PE relocations do not have symbols associated with them, and thus
   have no name. In order to still have flags in relocs flagspace, dummy
   symbols were allocated with generic names composed of the relocation's
   virtual address space.

* Remove old code of setting import entries to PE relocations
* Parse PE base relocations
* Implement converting from the specific RzBinPeRelocEnt type to the
  general RzBinReloc type
* Implement corrent type naming of PE relocations
* Add missing `PE_IMAGE_FILE_MACHINE` types
* Add PE base relocation types
* Fix PE format 'has_canary' function
* Remove 'RzBinPEObj.endian' and use 'RzBinPEObj.big_endian' instead
* Removed a.exe and b.exe tests, as they don't contain base relocations
  and swapped them with correct tests
* Corrected old PE relocation tests
* Add new PE base relocation tests
  • Loading branch information
Roeegg2 authored and XVilka committed Nov 21, 2024
1 parent 59cb50d commit 82b119d
Show file tree
Hide file tree
Showing 16 changed files with 1,659 additions and 325 deletions.
3 changes: 2 additions & 1 deletion librz/bin/format/pe/pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ static int bin_pe_init(RzBinPEObj *bin) {
(bin);
PE_(bin_pe_init_security)
(bin);
PE_(bin_pe_init_relocs)
(bin);

bin->big_endian = PE_(rz_bin_pe_is_big_endian)(bin);

Expand All @@ -81,7 +83,6 @@ static int bin_pe_init(RzBinPEObj *bin) {
(bin);
PE_(bin_pe_parse_resource)
(bin);
bin->relocs = NULL;
return true;
}

Expand Down
30 changes: 25 additions & 5 deletions librz/bin/format/pe/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@

#define PE_SCN_ALIGN_MASK 0x00F00000

// For the following relocation structs,
// See: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-reloc-section-image-only
typedef struct rz_bin_pe_reloc_block_t {
ut32 page_rva; ///< RVA of the block
ut32 block_size; ///< Size of the block
} RzBinPeRelocBlock;

// encoding is 12 bits for type and 4 bits for offset.
#define PE_RELOC_ENT_TYPE(val) ((val) >> 12)
#define PE_RELOC_ENT_OFFSET(val) ((val)&0xfff)

typedef struct rz_bin_pe_reloc_ent_t {
ut16 raw_val; ///< Type + Offset of the relocation entry
ut32 page_rva; ///< RVA of the block to which the relocation entry belongs
} RzBinPeRelocEnt;

struct rz_bin_pe_addr_t {
ut64 vaddr;
ut64 paddr;
Expand Down Expand Up @@ -165,12 +181,11 @@ struct PE_(rz_bin_pe_obj_t) {
int import_directory_size;
ut64 size;
int num_sections;
int endian;
bool verbose;
int big_endian;
RzList /*<Pe_image_rich_entry *>*/ *rich_entries;
RzPVector /*<RzBinReloc *>*/ *relocs;
RzList /*<rz_pe_resource *>*/ *resources;
RzVector /*<RzBinPeRelocEnt>*/ *relocs;
const char *file;
RzBuffer *b;
Sdb *kv;
Expand All @@ -182,9 +197,10 @@ struct PE_(rz_bin_pe_obj_t) {
RzHash *hash;
};

#define MAX_METADATA_STRING_LENGTH 256
#define COFF_SYMBOL_SIZE 18
#define PE_READ_STRUCT_FIELD(var, struct_type, field, size) var->field = rz_read_le##size(buf + offsetof(struct_type, field))
#define MAX_METADATA_STRING_LENGTH 256
#define COFF_SYMBOL_SIZE 18
#define PE_READ_STRUCT_FIELD(var, struct_type, field, size) var->field = rz_read_le##size(buf + offsetof(struct_type, field))
#define PE_READ_STRUCT_FIELD_L(buf, var, struct_type, field, size) var.field = rz_read_le##size(buf + offsetof(struct_type, field))

// pe_clr.c
RZ_OWN RzList /*<RzBinSymbol *>*/ *PE_(rz_bin_pe_get_clr_symbols)(RzBinPEObj *bin);
Expand All @@ -207,6 +223,10 @@ int PE_(read_image_import_directory)(RzBuffer *b, ut64 addr, PE_(image_import_di
int PE_(read_image_delay_import_directory)(RzBuffer *b, ut64 addr, PE_(image_delay_import_directory) * directory);
struct rz_bin_pe_import_t *PE_(rz_bin_pe_get_imports)(RzBinPEObj *bin);

// pe_relocs.c
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);
char *PE_(rz_bin_pe_get_cc)(RzBinPEObj *bin);
Expand Down
5 changes: 5 additions & 0 deletions librz/bin/format/pe/pe64_relocs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-FileCopyrightText: 2024 Roee Toledano <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

#define RZ_BIN_PE64 1
#include "pe_relocs.c"
4 changes: 2 additions & 2 deletions librz/bin/format/pe/pe_exports.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,13 @@ struct rz_bin_pe_export_t *PE_(rz_bin_pe_get_exports)(RzBinPEObj *bin) {
}
for (i = 0; i < bin->export_directory->NumberOfFunctions; i++) {
// get vaddr from AddressOfFunctions array
function_rva = rz_read_at_ble32((ut8 *)func_rvas, i * sizeof(PE_VWord), bin->endian);
function_rva = rz_read_at_ble32((ut8 *)func_rvas, i * sizeof(PE_VWord), bin->big_endian);
// have exports by name?
if (bin->export_directory->NumberOfNames > 0) {
// search for value of i into AddressOfOrdinals
name_vaddr = 0;
for (n = 0; n < bin->export_directory->NumberOfNames; n++) {
PE_Word fo = rz_read_at_ble16((ut8 *)ordinals, n * sizeof(PE_Word), bin->endian);
PE_Word fo = rz_read_at_ble16((ut8 *)ordinals, n * sizeof(PE_Word), bin->big_endian);
// if exist this index into AddressOfOrdinals
if (i == fo) {
function_ordinal = fo;
Expand Down
72 changes: 72 additions & 0 deletions librz/bin/format/pe/pe_relocs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: 2024 Roee Toledano <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

#include "pe.h"

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) {
// block size includes the size of the next blocks entry, which is 8 bytes long
const ut32 reloc_block_end = *offset + block->block_size - 8;
do {
RzBinPeRelocEnt reloc = { 0 };
if (!rz_buf_read_ble16_offset(b, offset, &reloc.raw_val, big_endian)) {
return false;
}
reloc.page_rva = block->page_rva;

rz_vector_push(relocs, &reloc);

} while (*offset < reloc_block_end);

return true;
}

static bool get_relocs_from_data_dir(RZ_NONNULL RzBinPEObj *bin, RZ_BORROW RZ_NONNULL RzVector /*<RzBinPeRelocEnt>*/ *relocs) {
rz_return_val_if_fail(bin->b && bin->nt_headers && bin->optional_header, false);
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->optional_header->DataDirectory[PE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
const ut64 relocs_end_offset = offset + bin->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) ||
!rz_buf_read_ble32_offset(b, &offset, &block.block_size, bin->big_endian) ||
!read_reloc_ent_from_block(relocs, b, &block, &offset, bin->big_endian)) {
return false;
}

} while (offset < relocs_end_offset);

rz_buf_seek(b, o_addr, RZ_BUF_SET);

return true;
}

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);
if (!ret) {
bin->relocs = NULL;
return false;
}

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;
}
87 changes: 51 additions & 36 deletions librz/bin/format/pe/pe_specs.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,42 +61,44 @@ typedef struct {
#define PE_IMAGE_FILE_TYPE_PE32 0x10b
#define PE_IMAGE_FILE_TYPE_PE32PLUS 0x20b

#define PE_IMAGE_FILE_MACHINE_UNKNOWN 0x0000
#define PE_IMAGE_FILE_MACHINE_ALPHA 0x0184
#define PE_IMAGE_FILE_MACHINE_ALPHA64 0x0284
#define PE_IMAGE_FILE_MACHINE_AM33 0x01d3
#define PE_IMAGE_FILE_MACHINE_AMD64 0x8664
#define PE_IMAGE_FILE_MACHINE_ARM 0x01c0
#define PE_IMAGE_FILE_MACHINE_ARMNT 0x01c4
#define PE_IMAGE_FILE_MACHINE_ARM64 0xaa64
#define PE_IMAGE_FILE_MACHINE_AXP64 PE_IMAGE_FILE_MACHINE_ALPHA64
#define PE_IMAGE_FILE_MACHINE_CEE 0xc0ee
#define PE_IMAGE_FILE_MACHINE_CEF 0x0cef
#define PE_IMAGE_FILE_MACHINE_EBC 0x0ebc
#define PE_IMAGE_FILE_MACHINE_I386 0x014c
#define PE_IMAGE_FILE_MACHINE_IA64 0x0200
#define PE_IMAGE_FILE_MACHINE_M32R 0x9041
#define PE_IMAGE_FILE_MACHINE_M68K 0x0268
#define PE_IMAGE_FILE_MACHINE_MIPS16 0x0266
#define PE_IMAGE_FILE_MACHINE_MIPSFPU 0x0366
#define PE_IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
#define PE_IMAGE_FILE_MACHINE_POWERPC 0x01f0
#define PE_IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define PE_IMAGE_FILE_MACHINE_POWERPCBE 0x01f2
#define PE_IMAGE_FILE_MACHINE_R10000 0x0168
#define PE_IMAGE_FILE_MACHINE_R3000 0x0162
#define PE_IMAGE_FILE_MACHINE_R4000 0x0166
#define PE_IMAGE_FILE_MACHINE_SH3 0x01a2
#define PE_IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define PE_IMAGE_FILE_MACHINE_SH3E 0x01a4
#define PE_IMAGE_FILE_MACHINE_SH4 0x01a6
#define PE_IMAGE_FILE_MACHINE_SH5 0x01a8
#define PE_IMAGE_FILE_MACHINE_THUMB 0x01c2
#define PE_IMAGE_FILE_MACHINE_TRICORE 0x0520
#define PE_IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169
#define PE_IMAGE_FILE_MACHINE_RISCV32 0x5032
#define PE_IMAGE_FILE_MACHINE_RISCV64 0x5064
#define PE_IMAGE_FILE_MACHINE_RISCV128 0x5128
#define PE_IMAGE_FILE_MACHINE_UNKNOWN 0x0000
#define PE_IMAGE_FILE_MACHINE_ALPHA 0x0184
#define PE_IMAGE_FILE_MACHINE_ALPHA64 0x0284
#define PE_IMAGE_FILE_MACHINE_AM33 0x01d3
#define PE_IMAGE_FILE_MACHINE_AMD64 0x8664
#define PE_IMAGE_FILE_MACHINE_ARM 0x01c0
#define PE_IMAGE_FILE_MACHINE_ARMNT 0x01c4
#define PE_IMAGE_FILE_MACHINE_ARM64 0xaa64
#define PE_IMAGE_FILE_MACHINE_AXP64 PE_IMAGE_FILE_MACHINE_ALPHA64
#define PE_IMAGE_FILE_MACHINE_CEE 0xc0ee
#define PE_IMAGE_FILE_MACHINE_CEF 0x0cef
#define PE_IMAGE_FILE_MACHINE_EBC 0x0ebc
#define PE_IMAGE_FILE_MACHINE_I386 0x014c
#define PE_IMAGE_FILE_MACHINE_IA64 0x0200
#define PE_IMAGE_FILE_MACHINE_LOONGARCH32 0x6232
#define PE_IMAGE_FILE_MACHINE_LOONGARCH64 0x6234
#define PE_IMAGE_FILE_MACHINE_M32R 0x9041
#define PE_IMAGE_FILE_MACHINE_M68K 0x0268
#define PE_IMAGE_FILE_MACHINE_MIPS16 0x0266
#define PE_IMAGE_FILE_MACHINE_MIPSFPU 0x0366
#define PE_IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
#define PE_IMAGE_FILE_MACHINE_POWERPC 0x01f0
#define PE_IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define PE_IMAGE_FILE_MACHINE_POWERPCBE 0x01f2
#define PE_IMAGE_FILE_MACHINE_R10000 0x0168
#define PE_IMAGE_FILE_MACHINE_R3000 0x0162
#define PE_IMAGE_FILE_MACHINE_R4000 0x0166
#define PE_IMAGE_FILE_MACHINE_SH3 0x01a2
#define PE_IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define PE_IMAGE_FILE_MACHINE_SH3E 0x01a4
#define PE_IMAGE_FILE_MACHINE_SH4 0x01a6
#define PE_IMAGE_FILE_MACHINE_SH5 0x01a8
#define PE_IMAGE_FILE_MACHINE_THUMB 0x01c2
#define PE_IMAGE_FILE_MACHINE_TRICORE 0x0520
#define PE_IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169
#define PE_IMAGE_FILE_MACHINE_RISCV32 0x5032
#define PE_IMAGE_FILE_MACHINE_RISCV64 0x5064
#define PE_IMAGE_FILE_MACHINE_RISCV128 0x5128

#define PE_IMAGE_FILE_RELOCS_STRIPPED 0x0001
#define PE_IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
Expand Down Expand Up @@ -530,6 +532,19 @@ typedef struct {
#define PE_RESOURCE_ENTRY_HTML 23
#define PE_RESOURCE_ENTRY_MANIFEST 24

// relocation types
#define PE_IMAGE_REL_BASED_ABSOLUTE 0
#define PE_IMAGE_REL_BASED_HIGH 1
#define PE_IMAGE_REL_BASED_LOW 2
#define PE_IMAGE_REL_BASED_HIGHLOW 3
#define PE_IMAGE_REL_BASED_HIGHADJ 4
#define PE_IMAGE_REL_BASED_MIPS_JMPADDR_ARM_MOV32_RISCV_HIGH20 5
// ... 6 is reserved
#define PE_IMAGE_REL_BASED_THUMB_MOV32_RISCV_LOW12I 7
#define PE_IMAGE_REL_BASED_RISCV_LOW12S_LOONGARCH32_MARK_LA 8
#define PE_IMAGE_REL_BASED_MIPS_JMPADDR16 9
#define PE_IMAGE_REL_BASED_DIR64 10

#define STRINGFILEINFO_TEXT "StringFileInfo"
#define TRANSLATION_TEXT "Translation"
#define VARFILEINFO_TEXT "VarFileInfo"
Expand Down
2 changes: 2 additions & 0 deletions librz/bin/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ rz_bin_sources = [
'format/pe/pe64_hdr.c',
'format/pe/pe_imports.c',
'format/pe/pe64_imports.c',
'format/pe/pe_relocs.c',
'format/pe/pe64_relocs.c',
'format/pe/pe_exports.c',
'format/pe/pe64_exports.c',
'format/pe/pe_rsrc.c',
Expand Down
Loading

0 comments on commit 82b119d

Please sign in to comment.