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

Add pe module delay load imports #448

Merged
merged 2 commits into from Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 57 additions & 0 deletions crates/examples/src/readobj/pe.rs
Expand Up @@ -91,6 +91,7 @@ fn print_pe<Pe: ImageNtHeaders>(p: &mut Printer<'_>, data: &[u8]) {
if let Some(ref sections) = sections {
print_export_dir(p, data, &sections, &data_directories);
print_import_dir::<Pe>(p, data, &sections, &data_directories);
print_delay_load_dir::<Pe>(p, data, &sections, &data_directories);
print_reloc_dir(p, data, machine, &sections, &data_directories);
print_resource_dir(p, data, &sections, &data_directories);
}
Expand Down Expand Up @@ -309,6 +310,62 @@ fn print_import_dir<Pe: ImageNtHeaders>(
Some(())
}

fn print_delay_load_dir<Pe: ImageNtHeaders>(
p: &mut Printer<'_>,
data: &[u8],
sections: &SectionTable,
data_directories: &DataDirectories,
) -> Option<()> {
let import_table = data_directories
.delay_load_import_table(data, sections)
.print_err(p)??;
let mut import_descs = import_table.descriptors().print_err(p)?;
p.group("ImageDelayLoadDirectory", |p| {
while let Some(Some(import_desc)) = import_descs.next().print_err(p) {
p.group("ImageDelayLoadDescriptor", |p| {
p.field_hex("Attributes", import_desc.attributes.get(LE));
let dll_name = import_desc.dll_name_rva.get(LE);
vthib marked this conversation as resolved.
Show resolved Hide resolved
p.field_string("DllName", dll_name, import_table.name(dll_name));
p.field_hex("ModuleHandle", import_desc.module_handle_rva.get(LE));
p.field_hex(
"ImportAddressTable",
import_desc.import_address_table_rva.get(LE),
);
p.field_hex("ImportNameTable", import_desc.import_name_table_rva.get(LE));
p.field_hex(
"BoundImportAddressTable",
import_desc.bound_import_address_table_rva.get(LE),
);
p.field_hex(
"UnloadInformationTable",
import_desc.unload_information_table_rva.get(LE),
);
p.field_hex("TimeDateStamp", import_desc.time_date_stamp.get(LE));

let mut name_thunks = import_table
.thunks(import_desc.import_name_table_rva.get(LE))
.print_err(p);
philipc marked this conversation as resolved.
Show resolved Hide resolved

if let Some(thunks) = name_thunks.as_mut() {
while let Some(Some(thunk)) = thunks.next::<Pe>().print_err(p) {
p.group("Thunk", |p| {
if thunk.is_ordinal() {
p.field("Ordinal", thunk.ordinal());
} else if let Some((hint, name)) =
import_table.hint_name(thunk.address()).print_err(p)
{
p.field("Hint", hint);
p.field_inline_string("Name", name);
}
});
}
}
});
}
});
Some(())
}

fn print_resource_dir(
p: &mut Printer<'_>,
data: &[u8],
Expand Down
15 changes: 15 additions & 0 deletions src/pe.rs
Expand Up @@ -1977,6 +1977,21 @@ pub struct ImageDelayloadDescriptor {
pub time_date_stamp: U32<LE>,
}

impl ImageDelayloadDescriptor {
/// Tell whether this delay-load import descriptor is the null descriptor
/// (used to mark the end of the iterator array in a PE)
pub fn is_null(&self) -> bool {
self.attributes.get(LE) == 0
&& self.dll_name_rva.get(LE) == 0
&& self.module_handle_rva.get(LE) == 0
&& self.import_address_table_rva.get(LE) == 0
&& self.import_name_table_rva.get(LE) == 0
&& self.bound_import_address_table_rva.get(LE) == 0
&& self.unload_information_table_rva.get(LE) == 0
&& self.time_date_stamp.get(LE) == 0
}
}

/// Delay load version 2 flag for `ImageDelayloadDescriptor::attributes`.
pub const IMAGE_DELAYLOAD_RVA_BASED: u32 = 0x8000_0000;

Expand Down
28 changes: 27 additions & 1 deletion src/read/pe/data_directory.rs
Expand Up @@ -3,7 +3,10 @@ use core::slice;
use crate::read::{Error, ReadError, ReadRef, Result};
use crate::{pe, LittleEndian as LE};

use super::{ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, SectionTable};
use super::{
DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory,
SectionTable,
};

/// The table of data directories in a PE file.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -105,6 +108,29 @@ impl<'data> DataDirectories<'data> {
Ok(Some(ImportTable::new(section_data, section_va, import_va)))
}

/// Returns the partially parsed delay-load import directory.
///
/// `data` must be the entire file data.
pub fn delay_load_import_table<R: ReadRef<'data>>(
&self,
data: R,
sections: &SectionTable<'data>,
) -> Result<Option<DelayLoadImportTable<'data>>> {
let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) {
Some(data_dir) => data_dir,
None => return Ok(None),
};
let import_va = data_dir.virtual_address.get(LE);
let (section_data, section_va) = sections
.pe_data_containing(data, import_va)
.read_error("Invalid import data dir virtual address")?;
Ok(Some(DelayLoadImportTable::new(
section_data,
section_va,
import_va,
)))
}

/// Returns the blocks in the base relocation directory.
///
/// `data` must be the entire file data.
Expand Down
114 changes: 114 additions & 0 deletions src/read/pe/import.rs
Expand Up @@ -216,3 +216,117 @@ impl ImageThunkData for pe::ImageThunkData32 {
self.0.get(LE) & 0x7fff_ffff
}
}

/// Information for parsing a PE delay-load import table.
#[derive(Debug, Clone)]
pub struct DelayLoadImportTable<'data> {
section_data: Bytes<'data>,
section_address: u32,
import_address: u32,
}

impl<'data> DelayLoadImportTable<'data> {
/// Create a new delay load import table parser.
///
/// The import descriptors start at `import_address`.
/// This table works in the same way the import table does: descriptors will be
/// parsed until a null entry.
///
/// `section_data` should be from the section containing `import_address`, and
/// `section_address` should be the address of that section. Pointers within the
/// descriptors and thunks may point to anywhere within the section data.
pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self {
DelayLoadImportTable {
section_data: Bytes(section_data),
section_address,
import_address,
}
}

/// Return an iterator for the import descriptors.
pub fn descriptors(&self) -> Result<DelayLoadDescriptorIterator<'data>> {
let offset = self.import_address.wrapping_sub(self.section_address);
let mut data = self.section_data;
data.skip(offset as usize)
.read_error("Invalid PE delay-load import descriptor address")?;
Ok(DelayLoadDescriptorIterator { data })
}

/// Return a library name given its address.
///
/// This address may be from [`pe::ImageDelayloadDescriptor::dll_name_rva`].
pub fn name(&self, address: u32) -> Result<&'data [u8]> {
self.section_data
.read_string_at(address.wrapping_sub(self.section_address) as usize)
.read_error("Invalid PE import descriptor name")
}

/// Return a list of thunks given its address.
///
/// This address may be from the INT, i.e. from
/// [`pe::ImageDelayloadDescriptor::import_name_table_rva`].
///
/// Please note that others RVA values from [`pe::ImageDelayloadDescriptor`] are used
/// by the delay loader at runtime to store values, and thus do not point inside the same
/// section as the INT. Calling this function on those addresses will fail.
pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> {
let offset = address.wrapping_sub(self.section_address);
let mut data = self.section_data;
data.skip(offset as usize)
.read_error("Invalid PE delay load import thunk table address")?;
Ok(ImportThunkList { data })
}

/// Parse a thunk.
pub fn import<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> {
if thunk.is_ordinal() {
Ok(Import::Ordinal(thunk.ordinal()))
} else {
let (hint, name) = self.hint_name(thunk.address())?;
Ok(Import::Name(hint, name))
}
}

/// Return the hint and name at the given address.
///
/// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`].
///
/// The hint is an index into the export name pointer table in the target library.
pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> {
let offset = address.wrapping_sub(self.section_address);
let mut data = self.section_data;
data.skip(offset as usize)
.read_error("Invalid PE delay load import thunk address")?;
let hint = data
.read::<U16Bytes<LE>>()
.read_error("Missing PE delay load import thunk hint")?
.get(LE);
let name = data
.read_string()
.read_error("Missing PE delay load import thunk name")?;
Ok((hint, name))
}
}

/// A fallible iterator for the descriptors in the delay-load data directory.
#[derive(Debug, Clone)]
pub struct DelayLoadDescriptorIterator<'data> {
data: Bytes<'data>,
}

impl<'data> DelayLoadDescriptorIterator<'data> {
/// Return the next descriptor.
///
/// Returns `Ok(None)` when a null descriptor is found.
pub fn next(&mut self) -> Result<Option<&'data pe::ImageDelayloadDescriptor>> {
let import_desc = self
.data
.read::<pe::ImageDelayloadDescriptor>()
.read_error("Missing PE null delay-load import descriptor")?;
if import_desc.is_null() {
Ok(None)
} else {
Ok(Some(import_desc))
}
}
}