diff --git a/crates/examples/src/readobj/pe.rs b/crates/examples/src/readobj/pe.rs index 517fb3a3..d95facd0 100644 --- a/crates/examples/src/readobj/pe.rs +++ b/crates/examples/src/readobj/pe.rs @@ -91,6 +91,7 @@ fn print_pe(p: &mut Printer<'_>, data: &[u8]) { if let Some(ref sections) = sections { print_export_dir(p, data, §ions, &data_directories); print_import_dir::(p, data, §ions, &data_directories); + print_delay_load_dir::(p, data, §ions, &data_directories); print_reloc_dir(p, data, machine, §ions, &data_directories); print_resource_dir(p, data, §ions, &data_directories); } @@ -309,6 +310,62 @@ fn print_import_dir( Some(()) } +fn print_delay_load_dir( + 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); + 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); + + if let Some(thunks) = name_thunks.as_mut() { + while let Some(Some(thunk)) = thunks.next::().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], diff --git a/src/pe.rs b/src/pe.rs index c89b86ca..00105ada 100644 --- a/src/pe.rs +++ b/src/pe.rs @@ -1977,6 +1977,21 @@ pub struct ImageDelayloadDescriptor { pub time_date_stamp: U32, } +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; diff --git a/src/read/pe/data_directory.rs b/src/read/pe/data_directory.rs index 8c195535..f5d98774 100644 --- a/src/read/pe/data_directory.rs +++ b/src/read/pe/data_directory.rs @@ -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)] @@ -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>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result>> { + 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. diff --git a/src/read/pe/import.rs b/src/read/pe/import.rs index 809a9628..a5535dc3 100644 --- a/src/read/pe/import.rs +++ b/src/read/pe/import.rs @@ -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> { + 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> { + 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(&self, thunk: Pe::ImageThunkData) -> Result> { + 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::>() + .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> { + let import_desc = self + .data + .read::() + .read_error("Missing PE null delay-load import descriptor")?; + if import_desc.is_null() { + Ok(None) + } else { + Ok(Some(import_desc)) + } + } +}