From 96d6cf8b5bf72b4b07e93acf7470b06eb86fa0e2 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Tue, 17 Dec 2024 05:17:40 +0900 Subject: [PATCH 1/2] PE: fix address size incompatibility on 32-bit builds --- src/pe/mod.rs | 55 +++++++++++++++++++++++++++++++++++---- src/pe/optional_header.rs | 6 ++--- src/pe/tls.rs | 32 ++++++++++++----------- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/pe/mod.rs b/src/pe/mod.rs index e921f2e2..c34d40cb 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -56,10 +56,10 @@ pub struct PE<'a> { pub is_lib: bool, /// Whether the binary is 64-bit (PE32+) pub is_64: bool, - /// the entry point of the binary - pub entry: usize, + /// The entry point RVA of the binary + pub entry: u32, /// The binary's RVA, or image base - useful for computing virtual addreses - pub image_base: usize, + pub image_base: u64, /// Data about any exported symbols in this binary (e.g., if it's a `dll`) pub export_data: Option>, /// Data for any imported symbols, and from which `dll`, etc., in this binary @@ -146,8 +146,8 @@ impl<'a> PE<'a> { } }; - entry = optional_header.standard_fields.address_of_entry_point as usize; - image_base = optional_header.windows_fields.image_base as usize; + entry = optional_header.standard_fields.address_of_entry_point; + image_base = optional_header.windows_fields.image_base; is_64 = optional_header.container()? == container::Container::Big; debug!( "entry {:#x} image_base {:#x} is_64: {}", @@ -734,6 +734,44 @@ mod tests { 0x0, 0x0, 0x0, 0x45, 0x78, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x0, ]; + static HEADERONLY_EMPTY_PE64: &[u8; 512] = &[ + 0x4D, 0x5A, 0x78, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, + 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, + 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, + 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x24, 0x00, 0x00, + 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x00, 0x00, 0x5D, 0x3F, 0xC8, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + #[test] fn string_table_excludes_length() { let coff = Coff::parse(&&COFF_FILE_SINGLE_STRING_IN_STRING_TABLE[..]).unwrap(); @@ -769,4 +807,11 @@ mod tests { panic!("must not parse PE with invalid PE header"); } } + + #[test] + fn pe64_image_base_and_entry_nonoverflows() { + let binary = PE::parse(HEADERONLY_EMPTY_PE64).unwrap(); + assert_eq!(binary.entry, 0x1020u32); + assert_eq!(binary.image_base, 0x140000000u64); + } } diff --git a/src/pe/optional_header.rs b/src/pe/optional_header.rs index e3fba43f..c7f3bb95 100644 --- a/src/pe/optional_header.rs +++ b/src/pe/optional_header.rs @@ -119,7 +119,7 @@ pub struct StandardFields { /// * For device drivers, this is the address of the initialization function. /// /// The entry point function is optional for DLLs. When no entry point is present, this member is zero. - pub address_of_entry_point: u64, + pub address_of_entry_point: u32, /// A pointer to the beginning of the code section (.text), relative to the image base. pub base_of_code: u64, /// A pointer to the beginning of the data section (.data), relative to the image base. Absent in 64-bit PE32+. @@ -139,7 +139,7 @@ impl From for StandardFields { size_of_code: u64::from(fields.size_of_code), size_of_initialized_data: u64::from(fields.size_of_initialized_data), size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data), - address_of_entry_point: u64::from(fields.address_of_entry_point), + address_of_entry_point: fields.address_of_entry_point, base_of_code: u64::from(fields.base_of_code), base_of_data: fields.base_of_data, } @@ -171,7 +171,7 @@ impl From for StandardFields { size_of_code: u64::from(fields.size_of_code), size_of_initialized_data: u64::from(fields.size_of_initialized_data), size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data), - address_of_entry_point: u64::from(fields.address_of_entry_point), + address_of_entry_point: fields.address_of_entry_point, base_of_code: u64::from(fields.base_of_code), base_of_data: 0, } diff --git a/src/pe/tls.rs b/src/pe/tls.rs index 2369bb0e..4ab43e90 100644 --- a/src/pe/tls.rs +++ b/src/pe/tls.rs @@ -117,7 +117,7 @@ impl ImageTlsDirectory { impl<'a> TlsData<'a> { pub fn parse( bytes: &'a [u8], - image_base: usize, + image_base: u64, dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, @@ -134,7 +134,7 @@ impl<'a> TlsData<'a> { pub fn parse_with_opts( bytes: &'a [u8], - image_base: usize, + image_base: u64, dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, @@ -159,7 +159,7 @@ impl<'a> TlsData<'a> { ))); } - if (itd.start_address_of_raw_data as usize) < image_base { + if itd.start_address_of_raw_data < image_base { return Err(error::Error::Malformed(format!( "tls start_address_of_raw_data ({:#x}) is less than image base ({:#x})", itd.start_address_of_raw_data, image_base @@ -167,10 +167,10 @@ impl<'a> TlsData<'a> { } // VA to RVA - let rva = itd.start_address_of_raw_data as usize - image_base; + let rva = itd.start_address_of_raw_data - image_base; let size = itd.end_address_of_raw_data - itd.start_address_of_raw_data; - let offset = - utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| { + let offset = utils::find_offset(rva as usize, sections, file_alignment, opts) + .ok_or_else(|| { error::Error::Malformed(format!( "cannot map tls start_address_of_raw_data rva ({:#x}) into offset", rva @@ -181,7 +181,7 @@ impl<'a> TlsData<'a> { // Parse the index if any if itd.address_of_index != 0 { - if (itd.address_of_index as usize) < image_base { + if itd.address_of_index < image_base { return Err(error::Error::Malformed(format!( "tls address_of_index ({:#x}) is less than image base ({:#x})", itd.address_of_index, image_base @@ -189,14 +189,14 @@ impl<'a> TlsData<'a> { } // VA to RVA - let rva = itd.address_of_index as usize - image_base; - let offset = utils::find_offset(rva, sections, file_alignment, opts); + let rva = itd.address_of_index - image_base; + let offset = utils::find_offset(rva as usize, sections, file_alignment, opts); slot = offset.and_then(|x| bytes.pread_with::(x, scroll::LE).ok()); } // Parse the callbacks if any if itd.address_of_callbacks != 0 { - if (itd.address_of_callbacks as usize) < image_base { + if itd.address_of_callbacks < image_base { return Err(error::Error::Malformed(format!( "tls address_of_callbacks ({:#x}) is less than image base ({:#x})", itd.address_of_callbacks, image_base @@ -204,9 +204,9 @@ impl<'a> TlsData<'a> { } // VA to RVA - let rva = itd.address_of_callbacks as usize - image_base; - let offset = - utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| { + let rva = itd.address_of_callbacks - image_base; + let offset = utils::find_offset(rva as usize, sections, file_alignment, opts) + .ok_or_else(|| { error::Error::Malformed(format!( "cannot map tls address_of_callbacks rva ({:#x}) into offset", rva @@ -224,9 +224,11 @@ impl<'a> TlsData<'a> { break; } // Each callback is an VA so convert it to RVA - let callback_rva = callback as usize - image_base; + let callback_rva = callback - image_base; // Check if the callback is in the image - if utils::find_offset(callback_rva, sections, file_alignment, opts).is_none() { + if utils::find_offset(callback_rva as usize, sections, file_alignment, opts) + .is_none() + { return Err(error::Error::Malformed(format!( "cannot map tls callback ({:#x})", callback From d12cc96845e81f6f2d607fb9a600e348cd99ea60 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Tue, 17 Dec 2024 05:26:05 +0900 Subject: [PATCH 2/2] Fix doc that image base in PE actually a VA --- src/pe/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pe/mod.rs b/src/pe/mod.rs index c34d40cb..c3c2693b 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -58,7 +58,7 @@ pub struct PE<'a> { pub is_64: bool, /// The entry point RVA of the binary pub entry: u32, - /// The binary's RVA, or image base - useful for computing virtual addreses + /// The binary's VA, or image base - useful for computing virtual addreses pub image_base: u64, /// Data about any exported symbols in this binary (e.g., if it's a `dll`) pub export_data: Option>,