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

Support reading PE data directly from memory (sections are page aligned) #548

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/read/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 1 addition & 1 deletion src/read/coff/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ pub trait CoffHeader: Debug + Pod {
data: R,
offset: u64,
) -> read::Result<SectionTable<'data>> {
SectionTable::parse(self, data, offset)
SectionTable::parse(self, data, offset, false)
}

/// Read the symbol table and string table.
Expand Down
4 changes: 3 additions & 1 deletion src/read/coff/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -26,11 +27,12 @@ impl<'data> SectionTable<'data> {
header: &Coff,
data: R,
offset: u64,
va_space: bool,
) -> Result<Self> {
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.
Expand Down
6 changes: 3 additions & 3 deletions src/read/pe/data_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,17 @@ 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.
/// - It uses the size field of the directory entry, which is
/// 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 {
Expand Down
9 changes: 5 additions & 4 deletions src/read/pe/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
/// Parse the raw PE file data.
pub fn parse(data: R) -> Result<Self> {
/// Parse the raw PE data.
pub fn parse(data: R, va_space: bool) -> Result<Self> {
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();

Expand Down Expand Up @@ -613,8 +613,9 @@ pub trait ImageNtHeaders: Debug + Pod {
&self,
data: R,
offset: u64,
va_space: bool,
) -> read::Result<SectionTable<'data>> {
SectionTable::parse(self.file_header(), data, offset)
SectionTable::parse(self.file_header(), data, offset, va_space)
}

/// Read the COFF symbol table and string table.
Expand Down
50 changes: 25 additions & 25 deletions src/read/pe/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<&'data [u8]>> {
Expand Down Expand Up @@ -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 {
Expand All @@ -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<Option<&'data [u8]>> {
Expand Down Expand Up @@ -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
Expand All @@ -307,7 +307,7 @@ impl<'data> SectionTable<'data> {
///
/// Returns `None` if no section contains the address.
pub fn pe_data_at<R: ReadRef<'data>>(&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.
Expand All @@ -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.
Expand All @@ -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))
Expand All @@ -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")
}
Expand All @@ -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()
}

Expand All @@ -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
Expand Down