From 61eb513a7775b0f3418259cf81fa2c07cbd872e0 Mon Sep 17 00:00:00 2001 From: Jujstme Date: Tue, 20 Jan 2026 20:06:26 +0100 Subject: [PATCH] Fixed PE export table enumeration logic The code for recovering the PE export entries previously assumed that every function/symbol exported into the export table had a given name. However, this is a false assumption, as some functions can be exported just by their ordinal. In practice, the previous code was flawed as it might've reported garbage data if the number of named function and the name of globally exported functions did not match. This PR fixes this issue by checking the name ordinals array, reading the index `i` of the corresponding ordinal and using it to recover the correct index into into the function address array. --- src/file_format/pe.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/file_format/pe.rs b/src/file_format/pe.rs index 101ac17..2f10689 100644 --- a/src/file_format/pe.rs +++ b/src/file_format/pe.rs @@ -100,9 +100,10 @@ struct OptionalCOFFHeader { struct ExportedSymbolsTableDef { _unk: [u8; 0x14], number_of_functions: u32, - _unk_1: u32, + number_of_names: u32, function_address_array_index: u32, function_name_array_index: u32, + name_ordinals_array_index: u32, } /// The machine type (architecture) of a module in a process. An image file can @@ -340,12 +341,20 @@ pub fn symbols( } .unwrap_or_default(); - (0..symbols_def.number_of_functions).filter_map(move |i| { + (0..symbols_def.number_of_names).filter_map(move |i| { + let ordinal = process + .read::(address + symbols_def.name_ordinals_array_index + i.wrapping_mul(2)) + .ok() + .map(|val| val as u32) + .filter(|&val| val < symbols_def.number_of_functions)?; + Some(Symbol { address: address + process .read::( - address + symbols_def.function_address_array_index + i.wrapping_mul(4), + address + + symbols_def.function_address_array_index + + ordinal.wrapping_mul(4), ) .ok()?, name_addr: address