From 60e78aacfd511bf49e40b1e5e05b5e136c09cbb9 Mon Sep 17 00:00:00 2001 From: Valaphee <32491319+valaphee@users.noreply.github.com> Date: Fri, 19 May 2023 15:37:06 +0200 Subject: [PATCH] Support reading PE data directly from memory (sections are page aligned) --- src/read/any.rs | 4 +-- src/read/coff/file.rs | 2 +- src/read/coff/section.rs | 4 ++- src/read/pe/data_directory.rs | 6 ++--- src/read/pe/file.rs | 9 ++++--- src/read/pe/section.rs | 50 +++++++++++++++++------------------ 6 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/read/any.rs b/src/read/any.rs index 342ad75f..8a8e65a6 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -253,9 +253,9 @@ impl<'data, R: ReadRef<'data>> File<'data, R> { #[cfg(feature = "wasm")] FileKind::Wasm => FileInternal::Wasm(wasm::WasmFile::parse(data)?), #[cfg(feature = "pe")] - FileKind::Pe32 => FileInternal::Pe32(pe::PeFile32::parse(data)?), + FileKind::Pe32 => FileInternal::Pe32(pe::PeFile32::parse(data, false)?), #[cfg(feature = "pe")] - FileKind::Pe64 => FileInternal::Pe64(pe::PeFile64::parse(data)?), + FileKind::Pe64 => FileInternal::Pe64(pe::PeFile64::parse(data, false)?), #[cfg(feature = "coff")] FileKind::Coff => FileInternal::Coff(coff::CoffFile::parse(data)?), #[cfg(feature = "coff")] diff --git a/src/read/coff/file.rs b/src/read/coff/file.rs index 4219f8f0..b7e7998f 100644 --- a/src/read/coff/file.rs +++ b/src/read/coff/file.rs @@ -259,7 +259,7 @@ pub trait CoffHeader: Debug + Pod { data: R, offset: u64, ) -> read::Result> { - SectionTable::parse(self, data, offset) + SectionTable::parse(self, data, offset, false) } /// Read the symbol table and string table. diff --git a/src/read/coff/section.rs b/src/read/coff/section.rs index 75804034..0c2967d5 100644 --- a/src/read/coff/section.rs +++ b/src/read/coff/section.rs @@ -15,6 +15,7 @@ use super::{CoffFile, CoffHeader, CoffRelocationIterator}; #[derive(Debug, Default, Clone, Copy)] pub struct SectionTable<'data> { sections: &'data [pe::ImageSectionHeader], + pub(crate) va_space: bool, } impl<'data> SectionTable<'data> { @@ -26,11 +27,12 @@ impl<'data> SectionTable<'data> { header: &Coff, data: R, offset: u64, + va_space: bool, ) -> Result { let sections = data .read_slice_at(offset, header.number_of_sections() as usize) .read_error("Invalid COFF/PE section headers")?; - Ok(SectionTable { sections }) + Ok(SectionTable { sections, va_space }) } /// Iterate over the section headers. diff --git a/src/read/pe/data_directory.rs b/src/read/pe/data_directory.rs index 0e10244b..a8078f42 100644 --- a/src/read/pe/data_directory.rs +++ b/src/read/pe/data_directory.rs @@ -170,7 +170,7 @@ impl pe::ImageDataDirectory { (self.virtual_address.get(LE), self.size.get(LE)) } - /// Return the file offset and size of this directory entry. + /// Return the offset and size of this directory entry. /// /// This function has some limitations: /// - It requires that the data is contained in a single section. @@ -178,9 +178,9 @@ impl pe::ImageDataDirectory { /// not desirable for all data directories. /// - It uses the `virtual_address` of the directory entry as an address, /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. - pub fn file_range(&self, sections: &SectionTable<'_>) -> Result<(u32, u32)> { + pub fn range(&self, sections: &SectionTable<'_>) -> Result<(u32, u32)> { let (offset, section_size) = sections - .pe_file_range_at(self.virtual_address.get(LE)) + .pe_range_at(self.virtual_address.get(LE)) .read_error("Invalid data dir virtual address")?; let size = self.size.get(LE); if size > section_size { diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 0f8ce9f2..2adea767 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -40,12 +40,12 @@ where Pe: ImageNtHeaders, R: ReadRef<'data>, { - /// Parse the raw PE file data. - pub fn parse(data: R) -> Result { + /// Parse the raw PE data. + pub fn parse(data: R, va_space: bool) -> Result { let dos_header = pe::ImageDosHeader::parse(data)?; let mut offset = dos_header.nt_headers_offset().into(); let (nt_headers, data_directories) = Pe::parse(data, &mut offset)?; - let sections = nt_headers.sections(data, offset)?; + let sections = nt_headers.sections(data, offset, va_space)?; let coff_symbols = nt_headers.symbols(data); let image_base = nt_headers.optional_header().image_base(); @@ -613,8 +613,9 @@ pub trait ImageNtHeaders: Debug + Pod { &self, data: R, offset: u64, + va_space: bool, ) -> read::Result> { - SectionTable::parse(self.file_header(), data, offset) + SectionTable::parse(self.file_header(), data, offset, va_space) } /// Read the COFF symbol table and string table. diff --git a/src/read/pe/section.rs b/src/read/pe/section.rs index 2880e401..c27c820f 100644 --- a/src/read/pe/section.rs +++ b/src/read/pe/section.rs @@ -91,12 +91,12 @@ where #[inline] fn file_range(&self) -> (u64, u64) { - let (offset, size) = self.section.pe_file_range(); + let (offset, size) = self.section.pe_range(self.file.common.sections.va_space); (u64::from(offset), u64::from(size)) } fn data(&self) -> Result<&'data [u8]> { - self.section.pe_data(self.file.data) + self.section.pe_data(self.file.data, self.file.common.sections.va_space) } fn data_range(&self, address: u64, size: u64) -> Result> { @@ -221,7 +221,7 @@ where #[inline] fn file_range(&self) -> Option<(u64, u64)> { - let (offset, size) = self.section.pe_file_range(); + let (offset, size) = self.section.pe_range(false); if size == 0 { None } else { @@ -230,7 +230,7 @@ where } fn data(&self) -> Result<&'data [u8]> { - self.section.pe_data(self.file.data) + self.section.pe_data(self.file.data, false) } fn data_range(&self, address: u64, size: u64) -> Result> { @@ -292,12 +292,12 @@ where } impl<'data> SectionTable<'data> { - /// Return the file offset of the given virtual address, and the size up + /// Return the offset of the given virtual address, and the size up /// to the end of the section containing it. /// /// Returns `None` if no section contains the address. - pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> { - self.iter().find_map(|section| section.pe_file_range_at(va)) + pub fn pe_range_at(&self, va: u32) -> Option<(u32, u32)> { + self.iter().find_map(|section| section.pe_range_at(va, self.va_space)) } /// Return the data starting at the given virtual address, up to the end of the @@ -307,7 +307,7 @@ impl<'data> SectionTable<'data> { /// /// Returns `None` if no section contains the address. pub fn pe_data_at>(&self, data: R, va: u32) -> Option<&'data [u8]> { - self.iter().find_map(|section| section.pe_data_at(data, va)) + self.iter().find_map(|section| section.pe_data_at(data, va, self.va_space)) } /// Return the data of the section that contains the given virtual address in a PE file. @@ -321,7 +321,7 @@ impl<'data> SectionTable<'data> { va: u32, ) -> Option<(&'data [u8], u32)> { self.iter() - .find_map(|section| section.pe_data_containing(data, va)) + .find_map(|section| section.pe_data_containing(data, va, self.va_space)) } /// Return the section that contains a given virtual address. @@ -331,24 +331,28 @@ impl<'data> SectionTable<'data> { } impl pe::ImageSectionHeader { - /// Return the offset and size of the section in a PE file. + /// Return the offset and size of the section /// /// The size of the range will be the minimum of the file size and virtual size. - pub fn pe_file_range(&self) -> (u32, u32) { + pub fn pe_range(&self, va_space: bool) -> (u32, u32) { // Pointer and size will be zero for uninitialized data; we don't need to validate this. - let offset = self.pointer_to_raw_data.get(LE); + let offset = if va_space { + self.virtual_address.get(LE) + } else { + self.pointer_to_raw_data.get(LE) + }; let size = cmp::min(self.virtual_size.get(LE), self.size_of_raw_data.get(LE)); (offset, size) } - /// Return the file offset of the given virtual address, and the remaining size up + /// Return the offset of the given virtual address, and the remaining size up /// to the end of the section. /// /// Returns `None` if the section does not contain the address. - pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> { + pub fn pe_range_at(&self, va: u32, va_space: bool) -> Option<(u32, u32)> { let section_va = self.virtual_address.get(LE); let offset = va.checked_sub(section_va)?; - let (section_offset, section_size) = self.pe_file_range(); + let (section_offset, section_size) = self.pe_range(va_space); // Address must be within section (and not at its end). if offset < section_size { Some((section_offset.checked_add(offset)?, section_size - offset)) @@ -357,16 +361,11 @@ impl pe::ImageSectionHeader { } } - /// Return the virtual address and size of the section. - pub fn pe_address_range(&self) -> (u32, u32) { - (self.virtual_address.get(LE), self.virtual_size.get(LE)) - } - /// Return the section data in a PE file. /// /// The length of the data will be the minimum of the file size and virtual size. - pub fn pe_data<'data, R: ReadRef<'data>>(&self, data: R) -> Result<&'data [u8]> { - let (offset, size) = self.pe_file_range(); + pub fn pe_data<'data, R: ReadRef<'data>>(&self, data: R, va_space: bool) -> Result<&'data [u8]> { + let (offset, size) = self.pe_range(va_space); data.read_bytes_at(offset.into(), size.into()) .read_error("Invalid PE section offset or size") } @@ -377,8 +376,8 @@ impl pe::ImageSectionHeader { /// Ignores sections with invalid data. /// /// Returns `None` if the section does not contain the address. - pub fn pe_data_at<'data, R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> { - let (offset, size) = self.pe_file_range_at(va)?; + pub fn pe_data_at<'data, R: ReadRef<'data>>(&self, data: R, va: u32, va_space: bool) -> Option<&'data [u8]> { + let (offset, size) = self.pe_range_at(va, va_space)?; data.read_bytes_at(offset.into(), size.into()).ok() } @@ -403,10 +402,11 @@ impl pe::ImageSectionHeader { &self, data: R, va: u32, + va_space: bool, ) -> Option<(&'data [u8], u32)> { let section_va = self.virtual_address.get(LE); let offset = va.checked_sub(section_va)?; - let (section_offset, section_size) = self.pe_file_range(); + let (section_offset, section_size) = self.pe_range(va_space); // Address must be within section (and not at its end). if offset < section_size { let section_data = data