diff options
| author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 | 
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 | 
| commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
| tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/object/src/read/pe | |
| parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
| download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip  | |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/object/src/read/pe')
| -rw-r--r-- | vendor/object/src/read/pe/data_directory.rs | 213 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/export.rs | 333 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/file.rs | 1050 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/import.rs | 337 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/mod.rs | 68 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/relocation.rs | 92 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/resource.rs | 209 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/rich.rs | 91 | ||||
| -rw-r--r-- | vendor/object/src/read/pe/section.rs | 440 | 
9 files changed, 2833 insertions, 0 deletions
diff --git a/vendor/object/src/read/pe/data_directory.rs b/vendor/object/src/read/pe/data_directory.rs new file mode 100644 index 0000000..a17179f --- /dev/null +++ b/vendor/object/src/read/pe/data_directory.rs @@ -0,0 +1,213 @@ +use core::slice; + +use crate::read::{Error, ReadError, ReadRef, Result}; +use crate::{pe, LittleEndian as LE}; + +use super::{ +    DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, +    SectionTable, +}; + +/// The table of data directories in a PE file. +/// +/// Returned by [`ImageNtHeaders::parse`](super::ImageNtHeaders::parse). +#[derive(Debug, Clone, Copy)] +pub struct DataDirectories<'data> { +    entries: &'data [pe::ImageDataDirectory], +} + +impl<'data> DataDirectories<'data> { +    /// Parse the data directory table. +    /// +    /// `data` must be the remaining optional data following the +    /// [optional header](pe::ImageOptionalHeader64).  `number` must be from the +    /// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes) +    /// field of the optional header. +    pub fn parse(data: &'data [u8], number: u32) -> Result<Self> { +        let entries = data +            .read_slice_at(0, number as usize) +            .read_error("Invalid PE number of RVA and sizes")?; +        Ok(DataDirectories { entries }) +    } + +    /// The number of data directories. +    #[allow(clippy::len_without_is_empty)] +    pub fn len(&self) -> usize { +        self.entries.len() +    } + +    /// Iterator over the data directories. +    pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> { +        self.entries.iter() +    } + +    /// Iterator which gives the directories as well as their index (one of the IMAGE_DIRECTORY_ENTRY_* constants). +    pub fn enumerate(&self) -> core::iter::Enumerate<slice::Iter<'data, pe::ImageDataDirectory>> { +        self.entries.iter().enumerate() +    } + +    /// Returns the data directory at the given index. +    /// +    /// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants. +    /// +    /// Returns `None` if the index is larger than the table size, +    /// or if the entry at the index has a zero virtual address. +    pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> { +        self.entries +            .get(index) +            .filter(|d| d.virtual_address.get(LE) != 0) +    } + +    /// Returns the unparsed export directory. +    /// +    /// `data` must be the entire file data. +    pub fn export_directory<R: ReadRef<'data>>( +        &self, +        data: R, +        sections: &SectionTable<'data>, +    ) -> Result<Option<&'data pe::ImageExportDirectory>> { +        let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { +            Some(data_dir) => data_dir, +            None => return Ok(None), +        }; +        let export_data = data_dir.data(data, sections)?; +        ExportTable::parse_directory(export_data).map(Some) +    } + +    /// Returns the partially parsed export directory. +    /// +    /// `data` must be the entire file data. +    pub fn export_table<R: ReadRef<'data>>( +        &self, +        data: R, +        sections: &SectionTable<'data>, +    ) -> Result<Option<ExportTable<'data>>> { +        let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { +            Some(data_dir) => data_dir, +            None => return Ok(None), +        }; +        let export_va = data_dir.virtual_address.get(LE); +        let export_data = data_dir.data(data, sections)?; +        ExportTable::parse(export_data, export_va).map(Some) +    } + +    /// Returns the partially parsed import directory. +    /// +    /// `data` must be the entire file data. +    pub fn import_table<R: ReadRef<'data>>( +        &self, +        data: R, +        sections: &SectionTable<'data>, +    ) -> Result<Option<ImportTable<'data>>> { +        let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_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(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. +    pub fn relocation_blocks<R: ReadRef<'data>>( +        &self, +        data: R, +        sections: &SectionTable<'data>, +    ) -> Result<Option<RelocationBlockIterator<'data>>> { +        let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) { +            Some(data_dir) => data_dir, +            None => return Ok(None), +        }; +        let reloc_data = data_dir.data(data, sections)?; +        Ok(Some(RelocationBlockIterator::new(reloc_data))) +    } + +    /// Returns the resource directory. +    /// +    /// `data` must be the entire file data. +    pub fn resource_directory<R: ReadRef<'data>>( +        &self, +        data: R, +        sections: &SectionTable<'data>, +    ) -> Result<Option<ResourceDirectory<'data>>> { +        let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE) { +            Some(data_dir) => data_dir, +            None => return Ok(None), +        }; +        let rsrc_data = data_dir.data(data, sections)?; +        Ok(Some(ResourceDirectory::new(rsrc_data))) +    } +} + +impl pe::ImageDataDirectory { +    /// Return the virtual address range of this directory entry. +    pub fn address_range(&self) -> (u32, u32) { +        (self.virtual_address.get(LE), self.size.get(LE)) +    } + +    /// Return the file 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)> { +        let (offset, section_size) = sections +            .pe_file_range_at(self.virtual_address.get(LE)) +            .read_error("Invalid data dir virtual address")?; +        let size = self.size.get(LE); +        if size > section_size { +            return Err(Error("Invalid data dir size")); +        } +        Ok((offset, size)) +    } + +    /// Get the data referenced by 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 data<'data, R: ReadRef<'data>>( +        &self, +        data: R, +        sections: &SectionTable<'data>, +    ) -> Result<&'data [u8]> { +        sections +            .pe_data_at(data, self.virtual_address.get(LE)) +            .read_error("Invalid data dir virtual address")? +            .get(..self.size.get(LE) as usize) +            .read_error("Invalid data dir size") +    } +} diff --git a/vendor/object/src/read/pe/export.rs b/vendor/object/src/read/pe/export.rs new file mode 100644 index 0000000..1aba844 --- /dev/null +++ b/vendor/object/src/read/pe/export.rs @@ -0,0 +1,333 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::read::{ByteString, Bytes, Error, ReadError, ReadRef, Result}; +use crate::{pe, LittleEndian as LE, U16Bytes, U32Bytes}; + +/// Where an export is pointing to. +#[derive(Clone, Copy)] +pub enum ExportTarget<'data> { +    /// The address of the export, relative to the image base. +    Address(u32), +    /// Forwarded to an export ordinal in another DLL. +    /// +    /// This gives the name of the DLL, and the ordinal. +    ForwardByOrdinal(&'data [u8], u32), +    /// Forwarded to an export name in another DLL. +    /// +    /// This gives the name of the DLL, and the export name. +    ForwardByName(&'data [u8], &'data [u8]), +} + +impl<'data> ExportTarget<'data> { +    /// Returns true if the target is an address. +    pub fn is_address(&self) -> bool { +        match self { +            ExportTarget::Address(_) => true, +            _ => false, +        } +    } + +    /// Returns true if the export is forwarded to another DLL. +    pub fn is_forward(&self) -> bool { +        !self.is_address() +    } +} + +/// An export from a PE file. +/// +/// There are multiple kinds of PE exports (with or without a name, and local or forwarded). +#[derive(Clone, Copy)] +pub struct Export<'data> { +    /// The ordinal of the export. +    /// +    /// These are sequential, starting at a base specified in the DLL. +    pub ordinal: u32, +    /// The name of the export, if known. +    pub name: Option<&'data [u8]>, +    /// The target of this export. +    pub target: ExportTarget<'data>, +} + +impl<'a> Debug for Export<'a> { +    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { +        f.debug_struct("Export") +            .field("ordinal", &self.ordinal) +            .field("name", &self.name.map(ByteString)) +            .field("target", &self.target) +            .finish() +    } +} + +impl<'a> Debug for ExportTarget<'a> { +    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { +        match self { +            ExportTarget::Address(address) => write!(f, "Address({:#x})", address), +            ExportTarget::ForwardByOrdinal(library, ordinal) => write!( +                f, +                "ForwardByOrdinal({:?}.#{})", +                ByteString(library), +                ordinal +            ), +            ExportTarget::ForwardByName(library, name) => write!( +                f, +                "ForwardByName({:?}.{:?})", +                ByteString(library), +                ByteString(name) +            ), +        } +    } +} + +/// A partially parsed PE export table. +/// +/// Returned by [`DataDirectories::export_table`](super::DataDirectories::export_table). +#[derive(Debug, Clone)] +pub struct ExportTable<'data> { +    data: Bytes<'data>, +    virtual_address: u32, +    directory: &'data pe::ImageExportDirectory, +    addresses: &'data [U32Bytes<LE>], +    names: &'data [U32Bytes<LE>], +    name_ordinals: &'data [U16Bytes<LE>], +} + +impl<'data> ExportTable<'data> { +    /// Parse the export table given its section data and address. +    pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> { +        let directory = Self::parse_directory(data)?; +        let data = Bytes(data); + +        let mut addresses = &[][..]; +        let address_of_functions = directory.address_of_functions.get(LE); +        if address_of_functions != 0 { +            addresses = data +                .read_slice_at::<U32Bytes<_>>( +                    address_of_functions.wrapping_sub(virtual_address) as usize, +                    directory.number_of_functions.get(LE) as usize, +                ) +                .read_error("Invalid PE export address table")?; +        } + +        let mut names = &[][..]; +        let mut name_ordinals = &[][..]; +        let address_of_names = directory.address_of_names.get(LE); +        let address_of_name_ordinals = directory.address_of_name_ordinals.get(LE); +        if address_of_names != 0 { +            if address_of_name_ordinals == 0 { +                return Err(Error("Missing PE export ordinal table")); +            } + +            let number = directory.number_of_names.get(LE) as usize; +            names = data +                .read_slice_at::<U32Bytes<_>>( +                    address_of_names.wrapping_sub(virtual_address) as usize, +                    number, +                ) +                .read_error("Invalid PE export name pointer table")?; +            name_ordinals = data +                .read_slice_at::<U16Bytes<_>>( +                    address_of_name_ordinals.wrapping_sub(virtual_address) as usize, +                    number, +                ) +                .read_error("Invalid PE export ordinal table")?; +        } + +        Ok(ExportTable { +            data, +            virtual_address, +            directory, +            addresses, +            names, +            name_ordinals, +        }) +    } + +    /// Parse the export directory given its section data. +    pub fn parse_directory(data: &'data [u8]) -> Result<&'data pe::ImageExportDirectory> { +        data.read_at::<pe::ImageExportDirectory>(0) +            .read_error("Invalid PE export dir size") +    } + +    /// Returns the header of the export table. +    pub fn directory(&self) -> &'data pe::ImageExportDirectory { +        self.directory +    } + +    /// Returns the base value of ordinals. +    /// +    /// Adding this to an address index will give an ordinal. +    pub fn ordinal_base(&self) -> u32 { +        self.directory.base.get(LE) +    } + +    /// Returns the unparsed address table. +    /// +    /// An address table entry may be a local address, or the address of a forwarded export entry. +    /// See [`Self::is_forward`] and [`Self::target_from_address`]. +    pub fn addresses(&self) -> &'data [U32Bytes<LE>] { +        self.addresses +    } + +    /// Returns the unparsed name pointer table. +    /// +    /// A name pointer table entry can be used with [`Self::name_from_pointer`]. +    pub fn name_pointers(&self) -> &'data [U32Bytes<LE>] { +        self.names +    } + +    /// Returns the unparsed ordinal table. +    /// +    /// An ordinal table entry is a 0-based index into the address table. +    /// See [`Self::address_by_index`] and [`Self::target_by_index`]. +    pub fn name_ordinals(&self) -> &'data [U16Bytes<LE>] { +        self.name_ordinals +    } + +    /// Returns an iterator for the entries in the name pointer table and ordinal table. +    /// +    /// A name pointer table entry can be used with [`Self::name_from_pointer`]. +    /// +    /// An ordinal table entry is a 0-based index into the address table. +    /// See [`Self::address_by_index`] and [`Self::target_by_index`]. +    pub fn name_iter(&self) -> impl Iterator<Item = (u32, u16)> + 'data { +        self.names +            .iter() +            .map(|x| x.get(LE)) +            .zip(self.name_ordinals.iter().map(|x| x.get(LE))) +    } + +    /// Returns the export address table entry at the given address index. +    /// +    /// This may be a local address, or the address of a forwarded export entry. +    /// See [`Self::is_forward`] and [`Self::target_from_address`]. +    /// +    /// `index` is a 0-based index into the export address table. +    pub fn address_by_index(&self, index: u32) -> Result<u32> { +        Ok(self +            .addresses +            .get(index as usize) +            .read_error("Invalid PE export address index")? +            .get(LE)) +    } + +    /// Returns the export address table entry at the given ordinal. +    /// +    /// This may be a local address, or the address of a forwarded export entry. +    /// See [`Self::is_forward`] and [`Self::target_from_address`]. +    pub fn address_by_ordinal(&self, ordinal: u32) -> Result<u32> { +        self.address_by_index(ordinal.wrapping_sub(self.ordinal_base())) +    } + +    /// Returns the target of the export at the given address index. +    /// +    /// `index` is a 0-based index into the export address table. +    pub fn target_by_index(&self, index: u32) -> Result<ExportTarget<'data>> { +        self.target_from_address(self.address_by_index(index)?) +    } + +    /// Returns the target of the export at the given ordinal. +    pub fn target_by_ordinal(&self, ordinal: u32) -> Result<ExportTarget<'data>> { +        self.target_from_address(self.address_by_ordinal(ordinal)?) +    } + +    /// Convert an export address table entry into a target. +    pub fn target_from_address(&self, address: u32) -> Result<ExportTarget<'data>> { +        Ok(if let Some(forward) = self.forward_string(address)? { +            let i = forward +                .iter() +                .position(|x| *x == b'.') +                .read_error("Missing PE forwarded export separator")?; +            let library = &forward[..i]; +            match &forward[i + 1..] { +                [b'#', digits @ ..] => { +                    let ordinal = +                        parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?; +                    ExportTarget::ForwardByOrdinal(library, ordinal) +                } +                [] => { +                    return Err(Error("Missing PE forwarded export name")); +                } +                name => ExportTarget::ForwardByName(library, name), +            } +        } else { +            ExportTarget::Address(address) +        }) +    } + +    fn forward_offset(&self, address: u32) -> Option<usize> { +        let offset = address.wrapping_sub(self.virtual_address) as usize; +        if offset < self.data.len() { +            Some(offset) +        } else { +            None +        } +    } + +    /// Return true if the export address table entry is a forward. +    pub fn is_forward(&self, address: u32) -> bool { +        self.forward_offset(address).is_some() +    } + +    /// Return the forward string if the export address table entry is a forward. +    pub fn forward_string(&self, address: u32) -> Result<Option<&'data [u8]>> { +        if let Some(offset) = self.forward_offset(address) { +            self.data +                .read_string_at(offset) +                .read_error("Invalid PE forwarded export address") +                .map(Some) +        } else { +            Ok(None) +        } +    } + +    /// Convert an export name pointer table entry into a name. +    pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> { +        let offset = name_pointer.wrapping_sub(self.virtual_address); +        self.data +            .read_string_at(offset as usize) +            .read_error("Invalid PE export name pointer") +    } + +    /// Returns the parsed exports in this table. +    pub fn exports(&self) -> Result<Vec<Export<'data>>> { +        // First, let's list all exports. +        let mut exports = Vec::new(); +        let ordinal_base = self.ordinal_base(); +        for (i, address) in self.addresses.iter().enumerate() { +            // Convert from an array index to an ordinal. +            let ordinal = ordinal_base.wrapping_add(i as u32); +            let target = self.target_from_address(address.get(LE))?; +            exports.push(Export { +                ordinal, +                target, +                // Might be populated later. +                name: None, +            }); +        } + +        // Now, check whether some (or all) of them have an associated name. +        // `ordinal_index` is a 0-based index into `addresses`. +        for (name_pointer, ordinal_index) in self.name_iter() { +            let name = self.name_from_pointer(name_pointer)?; +            exports +                .get_mut(ordinal_index as usize) +                .read_error("Invalid PE export ordinal")? +                .name = Some(name); +        } + +        Ok(exports) +    } +} + +fn parse_ordinal(digits: &[u8]) -> Option<u32> { +    if digits.is_empty() { +        return None; +    } +    let mut result: u32 = 0; +    for &c in digits { +        let x = (c as char).to_digit(10)?; +        result = result.checked_mul(10)?.checked_add(x)?; +    } +    Some(result) +} diff --git a/vendor/object/src/read/pe/file.rs b/vendor/object/src/read/pe/file.rs new file mode 100644 index 0000000..5372bdd --- /dev/null +++ b/vendor/object/src/read/pe/file.rs @@ -0,0 +1,1050 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::{mem, str}; + +use core::convert::TryInto; + +use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; +use crate::read::{ +    self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, +    Object, ObjectComdat, ObjectKind, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, +}; +use crate::{pe, ByteString, Bytes, CodeView, LittleEndian as LE, Pod, SubArchitecture, U32}; + +use super::{ +    DataDirectories, ExportTable, ImageThunkData, ImportTable, PeSection, PeSectionIterator, +    PeSegment, PeSegmentIterator, RichHeaderInfo, SectionTable, +}; + +/// A PE32 (32-bit) image file. +/// +/// This is a file that starts with [`pe::ImageNtHeaders32`], and corresponds +/// to [`crate::FileKind::Pe32`]. +pub type PeFile32<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders32, R>; +/// A PE32+ (64-bit) image file. +/// +/// This is a file that starts with [`pe::ImageNtHeaders64`], and corresponds +/// to [`crate::FileKind::Pe64`]. +pub type PeFile64<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders64, R>; + +/// A PE image file. +/// +/// Most functionality is provided by the [`Object`] trait implementation. +#[derive(Debug)] +pub struct PeFile<'data, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    pub(super) dos_header: &'data pe::ImageDosHeader, +    pub(super) nt_headers: &'data Pe, +    pub(super) data_directories: DataDirectories<'data>, +    pub(super) common: CoffCommon<'data, R>, +    pub(super) data: R, +} + +impl<'data, Pe, R> PeFile<'data, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    /// Parse the raw PE file data. +    pub fn parse(data: R) -> 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 coff_symbols = nt_headers.symbols(data); +        let image_base = nt_headers.optional_header().image_base(); + +        Ok(PeFile { +            dos_header, +            nt_headers, +            data_directories, +            common: CoffCommon { +                sections, +                // The PE file format deprecates the COFF symbol table (https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image) +                // We do not want to prevent parsing the rest of the PE file for a corrupt COFF header, but rather return an empty symbol table +                symbols: coff_symbols.unwrap_or_default(), +                image_base, +            }, +            data, +        }) +    } + +    /// Returns this binary data. +    pub fn data(&self) -> R { +        self.data +    } + +    /// Return the DOS header of this file. +    pub fn dos_header(&self) -> &'data pe::ImageDosHeader { +        self.dos_header +    } + +    /// Return the NT Headers of this file. +    pub fn nt_headers(&self) -> &'data Pe { +        self.nt_headers +    } + +    /// Returns information about the rich header of this file (if any). +    pub fn rich_header_info(&self) -> Option<RichHeaderInfo<'_>> { +        RichHeaderInfo::parse(self.data, self.dos_header.nt_headers_offset().into()) +    } + +    /// Returns the section table of this binary. +    pub fn section_table(&self) -> SectionTable<'data> { +        self.common.sections +    } + +    /// Returns the data directories of this file. +    pub fn data_directories(&self) -> DataDirectories<'data> { +        self.data_directories +    } + +    /// Returns the data directory at the given index. +    pub fn data_directory(&self, id: usize) -> Option<&'data pe::ImageDataDirectory> { +        self.data_directories.get(id) +    } + +    /// Returns the export table of this file. +    /// +    /// The export table is located using the data directory. +    pub fn export_table(&self) -> Result<Option<ExportTable<'data>>> { +        self.data_directories +            .export_table(self.data, &self.common.sections) +    } + +    /// Returns the import table of this file. +    /// +    /// The import table is located using the data directory. +    pub fn import_table(&self) -> Result<Option<ImportTable<'data>>> { +        self.data_directories +            .import_table(self.data, &self.common.sections) +    } + +    pub(super) fn section_alignment(&self) -> u64 { +        u64::from(self.nt_headers.optional_header().section_alignment()) +    } +} + +impl<'data, Pe, R> read::private::Sealed for PeFile<'data, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> Object<'data, 'file> for PeFile<'data, Pe, R> +where +    'data: 'file, +    Pe: ImageNtHeaders, +    R: 'file + ReadRef<'data>, +{ +    type Segment = PeSegment<'data, 'file, Pe, R>; +    type SegmentIterator = PeSegmentIterator<'data, 'file, Pe, R>; +    type Section = PeSection<'data, 'file, Pe, R>; +    type SectionIterator = PeSectionIterator<'data, 'file, Pe, R>; +    type Comdat = PeComdat<'data, 'file, Pe, R>; +    type ComdatIterator = PeComdatIterator<'data, 'file, Pe, R>; +    type Symbol = CoffSymbol<'data, 'file, R>; +    type SymbolIterator = CoffSymbolIterator<'data, 'file, R>; +    type SymbolTable = CoffSymbolTable<'data, 'file, R>; +    type DynamicRelocationIterator = NoDynamicRelocationIterator; + +    fn architecture(&self) -> Architecture { +        match self.nt_headers.file_header().machine.get(LE) { +            pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, +            pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, +            pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, +            pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, +            _ => Architecture::Unknown, +        } +    } + +    fn sub_architecture(&self) -> Option<SubArchitecture> { +        match self.nt_headers.file_header().machine.get(LE) { +            pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), +            _ => None, +        } +    } + +    #[inline] +    fn is_little_endian(&self) -> bool { +        // Only little endian is supported. +        true +    } + +    #[inline] +    fn is_64(&self) -> bool { +        self.nt_headers.is_type_64() +    } + +    fn kind(&self) -> ObjectKind { +        let characteristics = self.nt_headers.file_header().characteristics.get(LE); +        if characteristics & pe::IMAGE_FILE_DLL != 0 { +            ObjectKind::Dynamic +        } else if characteristics & pe::IMAGE_FILE_SYSTEM != 0 { +            ObjectKind::Unknown +        } else { +            ObjectKind::Executable +        } +    } + +    fn segments(&'file self) -> PeSegmentIterator<'data, 'file, Pe, R> { +        PeSegmentIterator { +            file: self, +            iter: self.common.sections.iter(), +        } +    } + +    fn section_by_name_bytes( +        &'file self, +        section_name: &[u8], +    ) -> Option<PeSection<'data, 'file, Pe, R>> { +        self.common +            .sections +            .section_by_name(self.common.symbols.strings(), section_name) +            .map(|(index, section)| PeSection { +                file: self, +                index: SectionIndex(index), +                section, +            }) +    } + +    fn section_by_index( +        &'file self, +        index: SectionIndex, +    ) -> Result<PeSection<'data, 'file, Pe, R>> { +        let section = self.common.sections.section(index.0)?; +        Ok(PeSection { +            file: self, +            index, +            section, +        }) +    } + +    fn sections(&'file self) -> PeSectionIterator<'data, 'file, Pe, R> { +        PeSectionIterator { +            file: self, +            iter: self.common.sections.iter().enumerate(), +        } +    } + +    fn comdats(&'file self) -> PeComdatIterator<'data, 'file, Pe, R> { +        PeComdatIterator { file: self } +    } + +    fn symbol_by_index(&'file self, index: SymbolIndex) -> Result<CoffSymbol<'data, 'file, R>> { +        let symbol = self.common.symbols.symbol(index.0)?; +        Ok(CoffSymbol { +            file: &self.common, +            index, +            symbol, +        }) +    } + +    fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { +        CoffSymbolIterator { +            file: &self.common, +            index: 0, +        } +    } + +    fn symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R>> { +        Some(CoffSymbolTable { file: &self.common }) +    } + +    fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { +        CoffSymbolIterator { +            file: &self.common, +            // Hack: don't return any. +            index: self.common.symbols.len(), +        } +    } + +    fn dynamic_symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R>> { +        None +    } + +    fn dynamic_relocations(&'file self) -> Option<NoDynamicRelocationIterator> { +        None +    } + +    fn imports(&self) -> Result<Vec<Import<'data>>> { +        let mut imports = Vec::new(); +        if let Some(import_table) = self.import_table()? { +            let mut import_descs = import_table.descriptors()?; +            while let Some(import_desc) = import_descs.next()? { +                let library = import_table.name(import_desc.name.get(LE))?; +                let mut first_thunk = import_desc.original_first_thunk.get(LE); +                if first_thunk == 0 { +                    first_thunk = import_desc.first_thunk.get(LE); +                } +                let mut thunks = import_table.thunks(first_thunk)?; +                while let Some(thunk) = thunks.next::<Pe>()? { +                    if !thunk.is_ordinal() { +                        let (_hint, name) = import_table.hint_name(thunk.address())?; +                        imports.push(Import { +                            library: ByteString(library), +                            name: ByteString(name), +                        }); +                    } +                } +            } +        } +        Ok(imports) +    } + +    fn exports(&self) -> Result<Vec<Export<'data>>> { +        let mut exports = Vec::new(); +        if let Some(export_table) = self.export_table()? { +            for (name_pointer, address_index) in export_table.name_iter() { +                let name = export_table.name_from_pointer(name_pointer)?; +                let address = export_table.address_by_index(address_index.into())?; +                if !export_table.is_forward(address) { +                    exports.push(Export { +                        name: ByteString(name), +                        address: self.common.image_base.wrapping_add(address.into()), +                    }) +                } +            } +        } +        Ok(exports) +    } + +    fn pdb_info(&self) -> Result<Option<CodeView<'_>>> { +        let data_dir = match self.data_directory(pe::IMAGE_DIRECTORY_ENTRY_DEBUG) { +            Some(data_dir) => data_dir, +            None => return Ok(None), +        }; +        let debug_data = data_dir.data(self.data, &self.common.sections).map(Bytes)?; +        let debug_data_size = data_dir.size.get(LE) as usize; + +        let count = debug_data_size / mem::size_of::<pe::ImageDebugDirectory>(); +        let rem = debug_data_size % mem::size_of::<pe::ImageDebugDirectory>(); +        if rem != 0 || count < 1 { +            return Err(Error("Invalid PE debug dir size")); +        } + +        let debug_dirs = debug_data +            .read_slice_at::<pe::ImageDebugDirectory>(0, count) +            .read_error("Invalid PE debug dir size")?; + +        for debug_dir in debug_dirs { +            if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW { +                continue; +            } + +            let info = self +                .data +                .read_slice_at::<u8>( +                    debug_dir.pointer_to_raw_data.get(LE) as u64, +                    debug_dir.size_of_data.get(LE) as usize, +                ) +                .read_error("Invalid CodeView Info address")?; + +            let mut info = Bytes(info); + +            let sig = info +                .read_bytes(4) +                .read_error("Invalid CodeView signature")?; +            if sig.0 != b"RSDS" { +                continue; +            } + +            let guid: [u8; 16] = info +                .read_bytes(16) +                .read_error("Invalid CodeView GUID")? +                .0 +                .try_into() +                .unwrap(); + +            let age = info.read::<U32<LE>>().read_error("Invalid CodeView Age")?; + +            let path = info +                .read_string() +                .read_error("Invalid CodeView file path")?; + +            return Ok(Some(CodeView { +                path: ByteString(path), +                guid, +                age: age.get(LE), +            })); +        } +        Ok(None) +    } + +    fn has_debug_symbols(&self) -> bool { +        self.section_by_name(".debug_info").is_some() +    } + +    fn relative_address_base(&self) -> u64 { +        self.common.image_base +    } + +    fn entry(&self) -> u64 { +        u64::from(self.nt_headers.optional_header().address_of_entry_point()) +            .wrapping_add(self.common.image_base) +    } + +    fn flags(&self) -> FileFlags { +        FileFlags::Coff { +            characteristics: self.nt_headers.file_header().characteristics.get(LE), +        } +    } +} + +/// An iterator for the COMDAT section groups in a [`PeFile32`]. +pub type PeComdatIterator32<'data, 'file, R = &'data [u8]> = +    PeComdatIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the COMDAT section groups in a [`PeFile64`]. +pub type PeComdatIterator64<'data, 'file, R = &'data [u8]> = +    PeComdatIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the COMDAT section groups in a [`PeFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeComdatIterator<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    #[allow(unused)] +    file: &'file PeFile<'data, Pe, R>, +} + +impl<'data, 'file, Pe, R> Iterator for PeComdatIterator<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    type Item = PeComdat<'data, 'file, Pe, R>; + +    #[inline] +    fn next(&mut self) -> Option<Self::Item> { +        None +    } +} + +/// A COMDAT section group in a [`PeFile32`]. +pub type PeComdat32<'data, 'file, R = &'data [u8]> = +    PeComdat<'data, 'file, pe::ImageNtHeaders32, R>; +/// A COMDAT section group in a [`PeFile64`]. +pub type PeComdat64<'data, 'file, R = &'data [u8]> = +    PeComdat<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A COMDAT section group in a [`PeFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeComdat<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    #[allow(unused)] +    file: &'file PeFile<'data, Pe, R>, +} + +impl<'data, 'file, Pe, R> read::private::Sealed for PeComdat<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> ObjectComdat<'data> for PeComdat<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    type SectionIterator = PeComdatSectionIterator<'data, 'file, Pe, R>; + +    #[inline] +    fn kind(&self) -> ComdatKind { +        unreachable!(); +    } + +    #[inline] +    fn symbol(&self) -> SymbolIndex { +        unreachable!(); +    } + +    #[inline] +    fn name_bytes(&self) -> Result<&[u8]> { +        unreachable!(); +    } + +    #[inline] +    fn name(&self) -> Result<&str> { +        unreachable!(); +    } + +    #[inline] +    fn sections(&self) -> Self::SectionIterator { +        unreachable!(); +    } +} + +/// An iterator for the sections in a COMDAT section group in a [`PeFile32`]. +pub type PeComdatSectionIterator32<'data, 'file, R = &'data [u8]> = +    PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the sections in a COMDAT section group in a [`PeFile64`]. +pub type PeComdatSectionIterator64<'data, 'file, R = &'data [u8]> = +    PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the sections in a COMDAT section group in a [`PeFile`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeComdatSectionIterator<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    #[allow(unused)] +    file: &'file PeFile<'data, Pe, R>, +} + +impl<'data, 'file, Pe, R> Iterator for PeComdatSectionIterator<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    type Item = SectionIndex; + +    fn next(&mut self) -> Option<Self::Item> { +        None +    } +} + +impl pe::ImageDosHeader { +    /// Read the DOS header. +    /// +    /// Also checks that the `e_magic` field in the header is valid. +    pub fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> { +        // DOS header comes first. +        let dos_header = data +            .read_at::<pe::ImageDosHeader>(0) +            .read_error("Invalid DOS header size or alignment")?; +        if dos_header.e_magic.get(LE) != pe::IMAGE_DOS_SIGNATURE { +            return Err(Error("Invalid DOS magic")); +        } +        Ok(dos_header) +    } + +    /// Return the file offset of the nt_headers. +    #[inline] +    pub fn nt_headers_offset(&self) -> u32 { +        self.e_lfanew.get(LE) +    } +} + +/// Find the optional header and read its `magic` field. +/// +/// It can be useful to know this magic value before trying to +/// fully parse the NT headers. +pub fn optional_header_magic<'data, R: ReadRef<'data>>(data: R) -> Result<u16> { +    let dos_header = pe::ImageDosHeader::parse(data)?; +    // NT headers are at an offset specified in the DOS header. +    let offset = dos_header.nt_headers_offset().into(); +    // It doesn't matter which NT header type is used for the purpose +    // of reading the optional header magic. +    let nt_headers = data +        .read_at::<pe::ImageNtHeaders32>(offset) +        .read_error("Invalid NT headers offset, size, or alignment")?; +    if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE { +        return Err(Error("Invalid PE magic")); +    } +    Ok(nt_headers.optional_header().magic()) +} + +/// A trait for generic access to [`pe::ImageNtHeaders32`] and [`pe::ImageNtHeaders64`]. +#[allow(missing_docs)] +pub trait ImageNtHeaders: Debug + Pod { +    type ImageOptionalHeader: ImageOptionalHeader; +    type ImageThunkData: ImageThunkData; + +    /// Return true if this type is a 64-bit header. +    /// +    /// This is a property of the type, not a value in the header data. +    fn is_type_64(&self) -> bool; + +    /// Return true if the magic field in the optional header is valid. +    fn is_valid_optional_magic(&self) -> bool; + +    /// Return the signature +    fn signature(&self) -> u32; + +    /// Return the file header. +    fn file_header(&self) -> &pe::ImageFileHeader; + +    /// Return the optional header. +    fn optional_header(&self) -> &Self::ImageOptionalHeader; + +    // Provided methods. + +    /// Read the NT headers, including the data directories. +    /// +    /// `data` must be for the entire file. +    /// +    /// `offset` must be headers offset, which can be obtained from [`pe::ImageDosHeader::nt_headers_offset`]. +    /// It is updated to point after the optional header, which is where the section headers are located. +    /// +    /// Also checks that the `signature` and `magic` fields in the headers are valid. +    fn parse<'data, R: ReadRef<'data>>( +        data: R, +        offset: &mut u64, +    ) -> read::Result<(&'data Self, DataDirectories<'data>)> { +        // Note that this does not include the data directories in the optional header. +        let nt_headers = data +            .read::<Self>(offset) +            .read_error("Invalid PE headers offset or size")?; +        if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE { +            return Err(Error("Invalid PE magic")); +        } +        if !nt_headers.is_valid_optional_magic() { +            return Err(Error("Invalid PE optional header magic")); +        } + +        // Read the rest of the optional header, and then read the data directories from that. +        let optional_data_size = +            u64::from(nt_headers.file_header().size_of_optional_header.get(LE)) +                .checked_sub(mem::size_of::<Self::ImageOptionalHeader>() as u64) +                .read_error("PE optional header size is too small")?; +        let optional_data = data +            .read_bytes(offset, optional_data_size) +            .read_error("Invalid PE optional header size")?; +        let data_directories = DataDirectories::parse( +            optional_data, +            nt_headers.optional_header().number_of_rva_and_sizes(), +        )?; + +        Ok((nt_headers, data_directories)) +    } + +    /// Read the section table. +    /// +    /// `data` must be for the entire file. +    /// `offset` must be after the optional file header. +    #[inline] +    fn sections<'data, R: ReadRef<'data>>( +        &self, +        data: R, +        offset: u64, +    ) -> read::Result<SectionTable<'data>> { +        SectionTable::parse(self.file_header(), data, offset) +    } + +    /// Read the COFF symbol table and string table. +    /// +    /// `data` must be the entire file data. +    #[inline] +    fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<SymbolTable<'data, R>> { +        SymbolTable::parse(self.file_header(), data) +    } +} + +/// A trait for generic access to [`pe::ImageOptionalHeader32`] and [`pe::ImageOptionalHeader64`]. +#[allow(missing_docs)] +pub trait ImageOptionalHeader: Debug + Pod { +    // Standard fields. +    fn magic(&self) -> u16; +    fn major_linker_version(&self) -> u8; +    fn minor_linker_version(&self) -> u8; +    fn size_of_code(&self) -> u32; +    fn size_of_initialized_data(&self) -> u32; +    fn size_of_uninitialized_data(&self) -> u32; +    fn address_of_entry_point(&self) -> u32; +    fn base_of_code(&self) -> u32; +    fn base_of_data(&self) -> Option<u32>; + +    // NT additional fields. +    fn image_base(&self) -> u64; +    fn section_alignment(&self) -> u32; +    fn file_alignment(&self) -> u32; +    fn major_operating_system_version(&self) -> u16; +    fn minor_operating_system_version(&self) -> u16; +    fn major_image_version(&self) -> u16; +    fn minor_image_version(&self) -> u16; +    fn major_subsystem_version(&self) -> u16; +    fn minor_subsystem_version(&self) -> u16; +    fn win32_version_value(&self) -> u32; +    fn size_of_image(&self) -> u32; +    fn size_of_headers(&self) -> u32; +    fn check_sum(&self) -> u32; +    fn subsystem(&self) -> u16; +    fn dll_characteristics(&self) -> u16; +    fn size_of_stack_reserve(&self) -> u64; +    fn size_of_stack_commit(&self) -> u64; +    fn size_of_heap_reserve(&self) -> u64; +    fn size_of_heap_commit(&self) -> u64; +    fn loader_flags(&self) -> u32; +    fn number_of_rva_and_sizes(&self) -> u32; +} + +impl ImageNtHeaders for pe::ImageNtHeaders32 { +    type ImageOptionalHeader = pe::ImageOptionalHeader32; +    type ImageThunkData = pe::ImageThunkData32; + +    #[inline] +    fn is_type_64(&self) -> bool { +        false +    } + +    #[inline] +    fn is_valid_optional_magic(&self) -> bool { +        self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC +    } + +    #[inline] +    fn signature(&self) -> u32 { +        self.signature.get(LE) +    } + +    #[inline] +    fn file_header(&self) -> &pe::ImageFileHeader { +        &self.file_header +    } + +    #[inline] +    fn optional_header(&self) -> &Self::ImageOptionalHeader { +        &self.optional_header +    } +} + +impl ImageOptionalHeader for pe::ImageOptionalHeader32 { +    #[inline] +    fn magic(&self) -> u16 { +        self.magic.get(LE) +    } + +    #[inline] +    fn major_linker_version(&self) -> u8 { +        self.major_linker_version +    } + +    #[inline] +    fn minor_linker_version(&self) -> u8 { +        self.minor_linker_version +    } + +    #[inline] +    fn size_of_code(&self) -> u32 { +        self.size_of_code.get(LE) +    } + +    #[inline] +    fn size_of_initialized_data(&self) -> u32 { +        self.size_of_initialized_data.get(LE) +    } + +    #[inline] +    fn size_of_uninitialized_data(&self) -> u32 { +        self.size_of_uninitialized_data.get(LE) +    } + +    #[inline] +    fn address_of_entry_point(&self) -> u32 { +        self.address_of_entry_point.get(LE) +    } + +    #[inline] +    fn base_of_code(&self) -> u32 { +        self.base_of_code.get(LE) +    } + +    #[inline] +    fn base_of_data(&self) -> Option<u32> { +        Some(self.base_of_data.get(LE)) +    } + +    #[inline] +    fn image_base(&self) -> u64 { +        self.image_base.get(LE).into() +    } + +    #[inline] +    fn section_alignment(&self) -> u32 { +        self.section_alignment.get(LE) +    } + +    #[inline] +    fn file_alignment(&self) -> u32 { +        self.file_alignment.get(LE) +    } + +    #[inline] +    fn major_operating_system_version(&self) -> u16 { +        self.major_operating_system_version.get(LE) +    } + +    #[inline] +    fn minor_operating_system_version(&self) -> u16 { +        self.minor_operating_system_version.get(LE) +    } + +    #[inline] +    fn major_image_version(&self) -> u16 { +        self.major_image_version.get(LE) +    } + +    #[inline] +    fn minor_image_version(&self) -> u16 { +        self.minor_image_version.get(LE) +    } + +    #[inline] +    fn major_subsystem_version(&self) -> u16 { +        self.major_subsystem_version.get(LE) +    } + +    #[inline] +    fn minor_subsystem_version(&self) -> u16 { +        self.minor_subsystem_version.get(LE) +    } + +    #[inline] +    fn win32_version_value(&self) -> u32 { +        self.win32_version_value.get(LE) +    } + +    #[inline] +    fn size_of_image(&self) -> u32 { +        self.size_of_image.get(LE) +    } + +    #[inline] +    fn size_of_headers(&self) -> u32 { +        self.size_of_headers.get(LE) +    } + +    #[inline] +    fn check_sum(&self) -> u32 { +        self.check_sum.get(LE) +    } + +    #[inline] +    fn subsystem(&self) -> u16 { +        self.subsystem.get(LE) +    } + +    #[inline] +    fn dll_characteristics(&self) -> u16 { +        self.dll_characteristics.get(LE) +    } + +    #[inline] +    fn size_of_stack_reserve(&self) -> u64 { +        self.size_of_stack_reserve.get(LE).into() +    } + +    #[inline] +    fn size_of_stack_commit(&self) -> u64 { +        self.size_of_stack_commit.get(LE).into() +    } + +    #[inline] +    fn size_of_heap_reserve(&self) -> u64 { +        self.size_of_heap_reserve.get(LE).into() +    } + +    #[inline] +    fn size_of_heap_commit(&self) -> u64 { +        self.size_of_heap_commit.get(LE).into() +    } + +    #[inline] +    fn loader_flags(&self) -> u32 { +        self.loader_flags.get(LE) +    } + +    #[inline] +    fn number_of_rva_and_sizes(&self) -> u32 { +        self.number_of_rva_and_sizes.get(LE) +    } +} + +impl ImageNtHeaders for pe::ImageNtHeaders64 { +    type ImageOptionalHeader = pe::ImageOptionalHeader64; +    type ImageThunkData = pe::ImageThunkData64; + +    #[inline] +    fn is_type_64(&self) -> bool { +        true +    } + +    #[inline] +    fn is_valid_optional_magic(&self) -> bool { +        self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC +    } + +    #[inline] +    fn signature(&self) -> u32 { +        self.signature.get(LE) +    } + +    #[inline] +    fn file_header(&self) -> &pe::ImageFileHeader { +        &self.file_header +    } + +    #[inline] +    fn optional_header(&self) -> &Self::ImageOptionalHeader { +        &self.optional_header +    } +} + +impl ImageOptionalHeader for pe::ImageOptionalHeader64 { +    #[inline] +    fn magic(&self) -> u16 { +        self.magic.get(LE) +    } + +    #[inline] +    fn major_linker_version(&self) -> u8 { +        self.major_linker_version +    } + +    #[inline] +    fn minor_linker_version(&self) -> u8 { +        self.minor_linker_version +    } + +    #[inline] +    fn size_of_code(&self) -> u32 { +        self.size_of_code.get(LE) +    } + +    #[inline] +    fn size_of_initialized_data(&self) -> u32 { +        self.size_of_initialized_data.get(LE) +    } + +    #[inline] +    fn size_of_uninitialized_data(&self) -> u32 { +        self.size_of_uninitialized_data.get(LE) +    } + +    #[inline] +    fn address_of_entry_point(&self) -> u32 { +        self.address_of_entry_point.get(LE) +    } + +    #[inline] +    fn base_of_code(&self) -> u32 { +        self.base_of_code.get(LE) +    } + +    #[inline] +    fn base_of_data(&self) -> Option<u32> { +        None +    } + +    #[inline] +    fn image_base(&self) -> u64 { +        self.image_base.get(LE) +    } + +    #[inline] +    fn section_alignment(&self) -> u32 { +        self.section_alignment.get(LE) +    } + +    #[inline] +    fn file_alignment(&self) -> u32 { +        self.file_alignment.get(LE) +    } + +    #[inline] +    fn major_operating_system_version(&self) -> u16 { +        self.major_operating_system_version.get(LE) +    } + +    #[inline] +    fn minor_operating_system_version(&self) -> u16 { +        self.minor_operating_system_version.get(LE) +    } + +    #[inline] +    fn major_image_version(&self) -> u16 { +        self.major_image_version.get(LE) +    } + +    #[inline] +    fn minor_image_version(&self) -> u16 { +        self.minor_image_version.get(LE) +    } + +    #[inline] +    fn major_subsystem_version(&self) -> u16 { +        self.major_subsystem_version.get(LE) +    } + +    #[inline] +    fn minor_subsystem_version(&self) -> u16 { +        self.minor_subsystem_version.get(LE) +    } + +    #[inline] +    fn win32_version_value(&self) -> u32 { +        self.win32_version_value.get(LE) +    } + +    #[inline] +    fn size_of_image(&self) -> u32 { +        self.size_of_image.get(LE) +    } + +    #[inline] +    fn size_of_headers(&self) -> u32 { +        self.size_of_headers.get(LE) +    } + +    #[inline] +    fn check_sum(&self) -> u32 { +        self.check_sum.get(LE) +    } + +    #[inline] +    fn subsystem(&self) -> u16 { +        self.subsystem.get(LE) +    } + +    #[inline] +    fn dll_characteristics(&self) -> u16 { +        self.dll_characteristics.get(LE) +    } + +    #[inline] +    fn size_of_stack_reserve(&self) -> u64 { +        self.size_of_stack_reserve.get(LE) +    } + +    #[inline] +    fn size_of_stack_commit(&self) -> u64 { +        self.size_of_stack_commit.get(LE) +    } + +    #[inline] +    fn size_of_heap_reserve(&self) -> u64 { +        self.size_of_heap_reserve.get(LE) +    } + +    #[inline] +    fn size_of_heap_commit(&self) -> u64 { +        self.size_of_heap_commit.get(LE) +    } + +    #[inline] +    fn loader_flags(&self) -> u32 { +        self.loader_flags.get(LE) +    } + +    #[inline] +    fn number_of_rva_and_sizes(&self) -> u32 { +        self.number_of_rva_and_sizes.get(LE) +    } +} diff --git a/vendor/object/src/read/pe/import.rs b/vendor/object/src/read/pe/import.rs new file mode 100644 index 0000000..8e9a9a9 --- /dev/null +++ b/vendor/object/src/read/pe/import.rs @@ -0,0 +1,337 @@ +use core::fmt::Debug; +use core::mem; + +use crate::read::{Bytes, ReadError, Result}; +use crate::{pe, LittleEndian as LE, Pod, U16Bytes}; + +use super::ImageNtHeaders; + +/// Information for parsing a PE import table. +/// +/// Returned by [`DataDirectories::import_table`](super::DataDirectories::import_table). +#[derive(Debug, Clone)] +pub struct ImportTable<'data> { +    section_data: Bytes<'data>, +    section_address: u32, +    import_address: u32, +} + +impl<'data> ImportTable<'data> { +    /// Create a new import table parser. +    /// +    /// The import descriptors start at `import_address`. +    /// The size declared in the `IMAGE_DIRECTORY_ENTRY_IMPORT` data directory is +    /// ignored by the Windows loader, and so 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 { +        ImportTable { +            section_data: Bytes(section_data), +            section_address, +            import_address, +        } +    } + +    /// Return an iterator for the import descriptors. +    pub fn descriptors(&self) -> Result<ImportDescriptorIterator<'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 import descriptor address")?; +        Ok(ImportDescriptorIterator { data }) +    } + +    /// Return a library name given its address. +    /// +    /// This address may be from [`pe::ImageImportDescriptor::name`]. +    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 [`pe::ImageImportDescriptor::original_first_thunk`] +    /// or [`pe::ImageImportDescriptor::first_thunk`]. +    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 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 import thunk address")?; +        let hint = data +            .read::<U16Bytes<LE>>() +            .read_error("Missing PE import thunk hint")? +            .get(LE); +        let name = data +            .read_string() +            .read_error("Missing PE import thunk name")?; +        Ok((hint, name)) +    } +} + +/// A fallible iterator for the descriptors in the import data directory. +#[derive(Debug, Clone)] +pub struct ImportDescriptorIterator<'data> { +    data: Bytes<'data>, +} + +impl<'data> ImportDescriptorIterator<'data> { +    /// Return the next descriptor. +    /// +    /// Returns `Ok(None)` when a null descriptor is found. +    pub fn next(&mut self) -> Result<Option<&'data pe::ImageImportDescriptor>> { +        let import_desc = self +            .data +            .read::<pe::ImageImportDescriptor>() +            .read_error("Missing PE null import descriptor")?; +        if import_desc.is_null() { +            Ok(None) +        } else { +            Ok(Some(import_desc)) +        } +    } +} + +/// A list of import thunks. +/// +/// These may be in the import lookup table, or the import address table. +#[derive(Debug, Clone)] +pub struct ImportThunkList<'data> { +    data: Bytes<'data>, +} + +impl<'data> ImportThunkList<'data> { +    /// Get the thunk at the given index. +    pub fn get<Pe: ImageNtHeaders>(&self, index: usize) -> Result<Pe::ImageThunkData> { +        let thunk = self +            .data +            .read_at(index * mem::size_of::<Pe::ImageThunkData>()) +            .read_error("Invalid PE import thunk index")?; +        Ok(*thunk) +    } + +    /// Return the first thunk in the list, and update `self` to point after it. +    /// +    /// Returns `Ok(None)` when a null thunk is found. +    pub fn next<Pe: ImageNtHeaders>(&mut self) -> Result<Option<Pe::ImageThunkData>> { +        let thunk = self +            .data +            .read::<Pe::ImageThunkData>() +            .read_error("Missing PE null import thunk")?; +        if thunk.address() == 0 { +            Ok(None) +        } else { +            Ok(Some(*thunk)) +        } +    } +} + +/// A parsed import thunk. +#[derive(Debug, Clone, Copy)] +pub enum Import<'data> { +    /// Import by ordinal. +    Ordinal(u16), +    /// Import by name. +    /// +    /// Includes a hint for the index into the export name pointer table in the target library. +    Name(u16, &'data [u8]), +} + +/// A trait for generic access to [`pe::ImageThunkData32`] and [`pe::ImageThunkData64`]. +#[allow(missing_docs)] +pub trait ImageThunkData: Debug + Pod { +    /// Return the raw thunk value. +    fn raw(self) -> u64; + +    /// Returns true if the ordinal flag is set. +    fn is_ordinal(self) -> bool; + +    /// Return the ordinal portion of the thunk. +    /// +    /// Does not check the ordinal flag. +    fn ordinal(self) -> u16; + +    /// Return the RVA portion of the thunk. +    /// +    /// Does not check the ordinal flag. +    fn address(self) -> u32; +} + +impl ImageThunkData for pe::ImageThunkData64 { +    fn raw(self) -> u64 { +        self.0.get(LE) +    } + +    fn is_ordinal(self) -> bool { +        self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG64 != 0 +    } + +    fn ordinal(self) -> u16 { +        self.0.get(LE) as u16 +    } + +    fn address(self) -> u32 { +        self.0.get(LE) as u32 & 0x7fff_ffff +    } +} + +impl ImageThunkData for pe::ImageThunkData32 { +    fn raw(self) -> u64 { +        self.0.get(LE).into() +    } + +    fn is_ordinal(self) -> bool { +        self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG32 != 0 +    } + +    fn ordinal(self) -> u16 { +        self.0.get(LE) as u16 +    } + +    fn address(self) -> u32 { +        self.0.get(LE) & 0x7fff_ffff +    } +} + +/// Information for parsing a PE delay-load import table. +/// +/// Returned by +/// [`DataDirectories::delay_load_import_table`](super::DataDirectories::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)) +        } +    } +} diff --git a/vendor/object/src/read/pe/mod.rs b/vendor/object/src/read/pe/mod.rs new file mode 100644 index 0000000..ab6011c --- /dev/null +++ b/vendor/object/src/read/pe/mod.rs @@ -0,0 +1,68 @@ +//! Support for reading PE files. +//! +//! Traits are used to abstract over the difference between PE32 and PE32+. +//! The primary trait for this is [`ImageNtHeaders`]. +//! +//! ## High level API +//! +//! [`PeFile`] implements the [`Object`](crate::read::Object) trait for +//! PE files. [`PeFile`] is parameterised by [`ImageNtHeaders`] to allow +//! reading both PE32 and PE32+. There are type aliases for these parameters +//! ([`PeFile32`] and [`PeFile64`]). +//! +//! ## Low level API +//! +//! The [`ImageNtHeaders`] trait can be directly used to parse both +//! [`pe::ImageNtHeaders32`] and [`pe::ImageNtHeaders64`]. +//! +//! ### Example for low level API +//!  ```no_run +//! use object::pe; +//! use object::read::pe::ImageNtHeaders; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the name of each section. +//! fn main() -> Result<(), Box<dyn Error>> { +//! #   #[cfg(feature = "std")] { +//!     let data = fs::read("path/to/binary")?; +//!     let dos_header = pe::ImageDosHeader::parse(&*data)?; +//!     let mut offset = dos_header.nt_headers_offset().into(); +//!     let (nt_headers, data_directories) = pe::ImageNtHeaders64::parse(&*data, &mut offset)?; +//!     let sections = nt_headers.sections(&*data, offset)?; +//!     let symbols = nt_headers.symbols(&*data)?; +//!     for section in sections.iter() { +//!         println!("{}", String::from_utf8_lossy(section.name(symbols.strings())?)); +//!     } +//! #   } +//!     Ok(()) +//! } +//! ``` +#[cfg(doc)] +use crate::pe; + +mod file; +pub use file::*; + +mod section; +pub use section::*; + +mod data_directory; +pub use data_directory::*; + +mod export; +pub use export::*; + +mod import; +pub use import::*; + +mod relocation; +pub use relocation::*; + +mod resource; +pub use resource::*; + +mod rich; +pub use rich::*; + +pub use super::coff::{SectionTable, SymbolTable}; diff --git a/vendor/object/src/read/pe/relocation.rs b/vendor/object/src/read/pe/relocation.rs new file mode 100644 index 0000000..77421b7 --- /dev/null +++ b/vendor/object/src/read/pe/relocation.rs @@ -0,0 +1,92 @@ +use core::slice; + +use crate::endian::{LittleEndian as LE, U16}; +use crate::pe; +use crate::read::{Bytes, Error, ReadError, Result}; + +/// An iterator over the relocation blocks in the `.reloc` section of a PE file. +/// +/// Returned by [`DataDirectories::relocation_blocks`](super::DataDirectories::relocation_blocks). +#[derive(Debug, Default, Clone, Copy)] +pub struct RelocationBlockIterator<'data> { +    data: Bytes<'data>, +} + +impl<'data> RelocationBlockIterator<'data> { +    /// Construct a new iterator from the data of the `.reloc` section. +    pub fn new(data: &'data [u8]) -> Self { +        RelocationBlockIterator { data: Bytes(data) } +    } + +    /// Read the next relocation page. +    pub fn next(&mut self) -> Result<Option<RelocationIterator<'data>>> { +        if self.data.is_empty() { +            return Ok(None); +        } +        let header = self +            .data +            .read::<pe::ImageBaseRelocation>() +            .read_error("Invalid PE reloc section size")?; +        let virtual_address = header.virtual_address.get(LE); +        let size = header.size_of_block.get(LE); +        if size <= 8 || size & 3 != 0 { +            return Err(Error("Invalid PE reloc block size")); +        } +        let count = (size - 8) / 2; +        let relocs = self +            .data +            .read_slice::<U16<LE>>(count as usize) +            .read_error("Invalid PE reloc block size")? +            .iter(); +        Ok(Some(RelocationIterator { +            virtual_address, +            size, +            relocs, +        })) +    } +} + +/// An iterator of the relocations in a block in the `.reloc` section of a PE file. +#[derive(Debug, Clone)] +pub struct RelocationIterator<'data> { +    virtual_address: u32, +    size: u32, +    relocs: slice::Iter<'data, U16<LE>>, +} + +impl<'data> RelocationIterator<'data> { +    /// Return the virtual address of the page that this block of relocations applies to. +    pub fn virtual_address(&self) -> u32 { +        self.virtual_address +    } + +    /// Return the size in bytes of this block of relocations. +    pub fn size(&self) -> u32 { +        self.size +    } +} + +impl<'data> Iterator for RelocationIterator<'data> { +    type Item = Relocation; + +    fn next(&mut self) -> Option<Relocation> { +        loop { +            let reloc = self.relocs.next()?.get(LE); +            if reloc != 0 { +                return Some(Relocation { +                    virtual_address: self.virtual_address.wrapping_add((reloc & 0xfff) as u32), +                    typ: reloc >> 12, +                }); +            } +        } +    } +} + +/// A relocation in the `.reloc` section of a PE file. +#[derive(Debug, Default, Clone, Copy)] +pub struct Relocation { +    /// The virtual address of the relocation. +    pub virtual_address: u32, +    /// One of the `pe::IMAGE_REL_BASED_*` constants. +    pub typ: u16, +} diff --git a/vendor/object/src/read/pe/resource.rs b/vendor/object/src/read/pe/resource.rs new file mode 100644 index 0000000..331da3f --- /dev/null +++ b/vendor/object/src/read/pe/resource.rs @@ -0,0 +1,209 @@ +use alloc::string::String; +use core::char; + +use crate::read::{ReadError, ReadRef, Result}; +use crate::{pe, LittleEndian as LE, U16Bytes}; + +/// The `.rsrc` section of a PE file. +/// +/// Returned by [`DataDirectories::resource_directory`](super::DataDirectories::resource_directory). +#[derive(Debug, Clone, Copy)] +pub struct ResourceDirectory<'data> { +    data: &'data [u8], +} + +impl<'data> ResourceDirectory<'data> { +    /// Construct from the data of the `.rsrc` section. +    pub fn new(data: &'data [u8]) -> Self { +        ResourceDirectory { data } +    } + +    /// Parses the root resource directory. +    pub fn root(&self) -> Result<ResourceDirectoryTable<'data>> { +        ResourceDirectoryTable::parse(self.data, 0) +    } +} + +/// A table of resource entries. +#[derive(Debug, Clone)] +pub struct ResourceDirectoryTable<'data> { +    /// The table header. +    pub header: &'data pe::ImageResourceDirectory, +    /// The table entries. +    pub entries: &'data [pe::ImageResourceDirectoryEntry], +} + +impl<'data> ResourceDirectoryTable<'data> { +    fn parse(data: &'data [u8], offset: u32) -> Result<Self> { +        let mut offset = u64::from(offset); +        let header = data +            .read::<pe::ImageResourceDirectory>(&mut offset) +            .read_error("Invalid resource table header")?; +        let entries_count = header.number_of_id_entries.get(LE) as usize +            + header.number_of_named_entries.get(LE) as usize; +        let entries = data +            .read_slice::<pe::ImageResourceDirectoryEntry>(&mut offset, entries_count) +            .read_error("Invalid resource table entries")?; +        Ok(Self { header, entries }) +    } +} + +impl pe::ImageResourceDirectoryEntry { +    /// Returns true if the entry has a name, rather than an ID. +    pub fn has_name(&self) -> bool { +        self.name_or_id.get(LE) & pe::IMAGE_RESOURCE_NAME_IS_STRING != 0 +    } + +    /// Returns the section offset of the name. +    /// +    /// Valid if `has_name()` returns true. +    fn name(&self) -> ResourceName { +        let offset = self.name_or_id.get(LE) & !pe::IMAGE_RESOURCE_NAME_IS_STRING; +        ResourceName { offset } +    } + +    /// Returns the ID. +    /// +    /// Valid if `has_string_name()` returns false. +    fn id(&self) -> u16 { +        (self.name_or_id.get(LE) & 0x0000_FFFF) as u16 +    } + +    /// Returns the entry name +    pub fn name_or_id(&self) -> ResourceNameOrId { +        if self.has_name() { +            ResourceNameOrId::Name(self.name()) +        } else { +            ResourceNameOrId::Id(self.id()) +        } +    } + +    /// Returns true if the entry is a subtable. +    pub fn is_table(&self) -> bool { +        self.offset_to_data_or_directory.get(LE) & pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0 +    } + +    /// Returns the section offset of the associated table or data. +    pub fn data_offset(&self) -> u32 { +        self.offset_to_data_or_directory.get(LE) & !pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY +    } + +    /// Returns the data associated to this directory entry. +    pub fn data<'data>( +        &self, +        section: ResourceDirectory<'data>, +    ) -> Result<ResourceDirectoryEntryData<'data>> { +        if self.is_table() { +            ResourceDirectoryTable::parse(section.data, self.data_offset()) +                .map(ResourceDirectoryEntryData::Table) +        } else { +            section +                .data +                .read_at::<pe::ImageResourceDataEntry>(self.data_offset().into()) +                .read_error("Invalid resource entry") +                .map(ResourceDirectoryEntryData::Data) +        } +    } +} + +/// Data associated with a resource directory entry. +#[derive(Debug, Clone)] +pub enum ResourceDirectoryEntryData<'data> { +    /// A subtable entry. +    Table(ResourceDirectoryTable<'data>), +    /// A resource data entry. +    Data(&'data pe::ImageResourceDataEntry), +} + +impl<'data> ResourceDirectoryEntryData<'data> { +    /// Converts to an option of table. +    /// +    /// Helper for iterator filtering. +    pub fn table(self) -> Option<ResourceDirectoryTable<'data>> { +        match self { +            Self::Table(dir) => Some(dir), +            _ => None, +        } +    } + +    /// Converts to an option of data entry. +    /// +    /// Helper for iterator filtering. +    pub fn data(self) -> Option<&'data pe::ImageResourceDataEntry> { +        match self { +            Self::Data(rsc) => Some(rsc), +            _ => None, +        } +    } +} + +/// A resource name. +#[derive(Debug, Clone, Copy)] +pub struct ResourceName { +    offset: u32, +} + +impl ResourceName { +    /// Converts to a `String`. +    pub fn to_string_lossy(&self, directory: ResourceDirectory<'_>) -> Result<String> { +        let d = self.data(directory)?.iter().map(|c| c.get(LE)); + +        Ok(char::decode_utf16(d) +            .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) +            .collect::<String>()) +    } + +    /// Returns the string unicode buffer. +    pub fn data<'data>( +        &self, +        directory: ResourceDirectory<'data>, +    ) -> Result<&'data [U16Bytes<LE>]> { +        let mut offset = u64::from(self.offset); +        let len = directory +            .data +            .read::<U16Bytes<LE>>(&mut offset) +            .read_error("Invalid resource name offset")?; +        directory +            .data +            .read_slice::<U16Bytes<LE>>(&mut offset, len.get(LE).into()) +            .read_error("Invalid resource name length") +    } + +    /// Returns the string buffer as raw bytes. +    pub fn raw_data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u8]> { +        self.data(directory).map(crate::pod::bytes_of_slice) +    } +} + +/// A resource name or ID. +/// +/// Can be either a string or a numeric ID. +#[derive(Debug)] +pub enum ResourceNameOrId { +    /// A resource name. +    Name(ResourceName), +    /// A resource ID. +    Id(u16), +} + +impl ResourceNameOrId { +    /// Converts to an option of name. +    /// +    /// Helper for iterator filtering. +    pub fn name(self) -> Option<ResourceName> { +        match self { +            Self::Name(name) => Some(name), +            _ => None, +        } +    } + +    /// Converts to an option of ID. +    /// +    /// Helper for iterator filtering. +    pub fn id(self) -> Option<u16> { +        match self { +            Self::Id(id) => Some(id), +            _ => None, +        } +    } +} diff --git a/vendor/object/src/read/pe/rich.rs b/vendor/object/src/read/pe/rich.rs new file mode 100644 index 0000000..33dd039 --- /dev/null +++ b/vendor/object/src/read/pe/rich.rs @@ -0,0 +1,91 @@ +//! PE rich header handling + +use core::mem; + +use crate::pod::bytes_of_slice; +use crate::read::Bytes; +use crate::{pe, LittleEndian as LE, ReadRef, U32}; + +/// Parsed information about a Rich Header. +#[derive(Debug, Clone, Copy)] +pub struct RichHeaderInfo<'data> { +    /// The offset at which the rich header starts. +    pub offset: usize, +    /// The length (in bytes) of the rich header. +    /// +    /// This includes the payload, but also the 16-byte start sequence and the +    /// 8-byte final "Rich" and XOR key. +    pub length: usize, +    /// The XOR key used to mask the rich header. +    /// +    /// Unless the file has been tampered with, it should be equal to a checksum +    /// of the file header. +    pub xor_key: u32, +    masked_entries: &'data [pe::MaskedRichHeaderEntry], +} + +/// A PE rich header entry after it has been unmasked. +/// +/// See [`pe::MaskedRichHeaderEntry`]. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct RichHeaderEntry { +    /// ID of the component. +    pub comp_id: u32, +    /// Number of times this component has been used when building this PE. +    pub count: u32, +} + +impl<'data> RichHeaderInfo<'data> { +    /// Try to locate a rich header and its entries in the current PE file. +    pub fn parse<R: ReadRef<'data>>(data: R, nt_header_offset: u64) -> Option<Self> { +        // Locate the rich header, if any. +        // It ends with the "Rich" string and an XOR key, before the NT header. +        let data = data.read_bytes_at(0, nt_header_offset).map(Bytes).ok()?; +        let end_marker_offset = memmem(data.0, b"Rich", 4)?; +        let xor_key = *data.read_at::<U32<LE>>(end_marker_offset + 4).ok()?; + +        // It starts at the masked "DanS" string and 3 masked zeroes. +        let masked_start_marker = U32::new(LE, 0x536e_6144 ^ xor_key.get(LE)); +        let start_header = [masked_start_marker, xor_key, xor_key, xor_key]; +        let start_sequence = bytes_of_slice(&start_header); +        let start_marker_offset = memmem(&data.0[..end_marker_offset], start_sequence, 4)?; + +        // Extract the items between the markers. +        let items_offset = start_marker_offset + start_sequence.len(); +        let items_len = end_marker_offset - items_offset; +        let item_count = items_len / mem::size_of::<pe::MaskedRichHeaderEntry>(); +        let items = data.read_slice_at(items_offset, item_count).ok()?; +        Some(RichHeaderInfo { +            offset: start_marker_offset, +            // Includes "Rich" marker and the XOR key. +            length: end_marker_offset - start_marker_offset + 8, +            xor_key: xor_key.get(LE), +            masked_entries: items, +        }) +    } + +    /// Returns an iterator over the unmasked entries. +    pub fn unmasked_entries(&self) -> impl Iterator<Item = RichHeaderEntry> + 'data { +        let xor_key = self.xor_key; +        self.masked_entries +            .iter() +            .map(move |entry| RichHeaderEntry { +                comp_id: entry.masked_comp_id.get(LE) ^ xor_key, +                count: entry.masked_count.get(LE) ^ xor_key, +            }) +    } +} + +/// Find the offset of the first occurrence of needle in the data. +/// +/// The offset must have the given alignment. +fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option<usize> { +    let mut offset = 0; +    loop { +        if data.get(offset..)?.get(..needle.len())? == needle { +            return Some(offset); +        } +        offset += align; +    } +} diff --git a/vendor/object/src/read/pe/section.rs b/vendor/object/src/read/pe/section.rs new file mode 100644 index 0000000..74c9d7f --- /dev/null +++ b/vendor/object/src/read/pe/section.rs @@ -0,0 +1,440 @@ +use core::marker::PhantomData; +use core::{cmp, iter, slice, str}; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::pe::ImageSectionHeader; +use crate::read::{ +    self, CompressedData, CompressedFileRange, ObjectSection, ObjectSegment, ReadError, ReadRef, +    Relocation, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, +}; + +use super::{ImageNtHeaders, PeFile, SectionTable}; + +/// An iterator for the loadable sections in a [`PeFile32`](super::PeFile32). +pub type PeSegmentIterator32<'data, 'file, R = &'data [u8]> = +    PeSegmentIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the loadable sections in a [`PeFile64`](super::PeFile64). +pub type PeSegmentIterator64<'data, 'file, R = &'data [u8]> = +    PeSegmentIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the loadable sections in a [`PeFile`]. +#[derive(Debug)] +pub struct PeSegmentIterator<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    pub(super) file: &'file PeFile<'data, Pe, R>, +    pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>, +} + +impl<'data, 'file, Pe, R> Iterator for PeSegmentIterator<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    type Item = PeSegment<'data, 'file, Pe, R>; + +    fn next(&mut self) -> Option<Self::Item> { +        self.iter.next().map(|section| PeSegment { +            file: self.file, +            section, +        }) +    } +} + +/// A loadable section in a [`PeFile32`](super::PeFile32). +pub type PeSegment32<'data, 'file, R = &'data [u8]> = +    PeSegment<'data, 'file, pe::ImageNtHeaders32, R>; +/// A loadable section in a [`PeFile64`](super::PeFile64). +pub type PeSegment64<'data, 'file, R = &'data [u8]> = +    PeSegment<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A loadable section in a [`PeFile`]. +/// +/// Most functionality is provided by the [`ObjectSegment`] trait implementation. +#[derive(Debug)] +pub struct PeSegment<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    file: &'file PeFile<'data, Pe, R>, +    section: &'data pe::ImageSectionHeader, +} + +impl<'data, 'file, Pe, R> read::private::Sealed for PeSegment<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> ObjectSegment<'data> for PeSegment<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    #[inline] +    fn address(&self) -> u64 { +        u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base) +    } + +    #[inline] +    fn size(&self) -> u64 { +        u64::from(self.section.virtual_size.get(LE)) +    } + +    #[inline] +    fn align(&self) -> u64 { +        self.file.section_alignment() +    } + +    #[inline] +    fn file_range(&self) -> (u64, u64) { +        let (offset, size) = self.section.pe_file_range(); +        (u64::from(offset), u64::from(size)) +    } + +    fn data(&self) -> Result<&'data [u8]> { +        self.section.pe_data(self.file.data) +    } + +    fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { +        Ok(read::util::data_range( +            self.data()?, +            self.address(), +            address, +            size, +        )) +    } + +    #[inline] +    fn name_bytes(&self) -> Result<Option<&[u8]>> { +        self.section +            .name(self.file.common.symbols.strings()) +            .map(Some) +    } + +    #[inline] +    fn name(&self) -> Result<Option<&str>> { +        let name = self.section.name(self.file.common.symbols.strings())?; +        Ok(Some( +            str::from_utf8(name) +                .ok() +                .read_error("Non UTF-8 PE section name")?, +        )) +    } + +    #[inline] +    fn flags(&self) -> SegmentFlags { +        let characteristics = self.section.characteristics.get(LE); +        SegmentFlags::Coff { characteristics } +    } +} + +/// An iterator for the sections in a [`PeFile32`](super::PeFile32). +pub type PeSectionIterator32<'data, 'file, R = &'data [u8]> = +    PeSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator for the sections in a [`PeFile64`](super::PeFile64). +pub type PeSectionIterator64<'data, 'file, R = &'data [u8]> = +    PeSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator for the sections in a [`PeFile`]. +#[derive(Debug)] +pub struct PeSectionIterator<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    pub(super) file: &'file PeFile<'data, Pe, R>, +    pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>, +} + +impl<'data, 'file, Pe, R> Iterator for PeSectionIterator<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    type Item = PeSection<'data, 'file, Pe, R>; + +    fn next(&mut self) -> Option<Self::Item> { +        self.iter.next().map(|(index, section)| PeSection { +            file: self.file, +            index: SectionIndex(index + 1), +            section, +        }) +    } +} + +/// A section in a [`PeFile32`](super::PeFile32). +pub type PeSection32<'data, 'file, R = &'data [u8]> = +    PeSection<'data, 'file, pe::ImageNtHeaders32, R>; +/// A section in a [`PeFile64`](super::PeFile64). +pub type PeSection64<'data, 'file, R = &'data [u8]> = +    PeSection<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A section in a [`PeFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct PeSection<'data, 'file, Pe, R = &'data [u8]> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    pub(super) file: &'file PeFile<'data, Pe, R>, +    pub(super) index: SectionIndex, +    pub(super) section: &'data pe::ImageSectionHeader, +} + +impl<'data, 'file, Pe, R> read::private::Sealed for PeSection<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Pe, R> ObjectSection<'data> for PeSection<'data, 'file, Pe, R> +where +    Pe: ImageNtHeaders, +    R: ReadRef<'data>, +{ +    type RelocationIterator = PeRelocationIterator<'data, 'file, R>; + +    #[inline] +    fn index(&self) -> SectionIndex { +        self.index +    } + +    #[inline] +    fn address(&self) -> u64 { +        u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base) +    } + +    #[inline] +    fn size(&self) -> u64 { +        u64::from(self.section.virtual_size.get(LE)) +    } + +    #[inline] +    fn align(&self) -> u64 { +        self.file.section_alignment() +    } + +    #[inline] +    fn file_range(&self) -> Option<(u64, u64)> { +        let (offset, size) = self.section.pe_file_range(); +        if size == 0 { +            None +        } else { +            Some((u64::from(offset), u64::from(size))) +        } +    } + +    fn data(&self) -> Result<&'data [u8]> { +        self.section.pe_data(self.file.data) +    } + +    fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { +        Ok(read::util::data_range( +            self.data()?, +            self.address(), +            address, +            size, +        )) +    } + +    #[inline] +    fn compressed_file_range(&self) -> Result<CompressedFileRange> { +        Ok(CompressedFileRange::none(self.file_range())) +    } + +    #[inline] +    fn compressed_data(&self) -> Result<CompressedData<'data>> { +        self.data().map(CompressedData::none) +    } + +    #[inline] +    fn name_bytes(&self) -> Result<&[u8]> { +        self.section.name(self.file.common.symbols.strings()) +    } + +    #[inline] +    fn name(&self) -> Result<&str> { +        let name = self.name_bytes()?; +        str::from_utf8(name) +            .ok() +            .read_error("Non UTF-8 PE section name") +    } + +    #[inline] +    fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { +        Ok(None) +    } + +    #[inline] +    fn segment_name(&self) -> Result<Option<&str>> { +        Ok(None) +    } + +    #[inline] +    fn kind(&self) -> SectionKind { +        self.section.kind() +    } + +    fn relocations(&self) -> PeRelocationIterator<'data, 'file, R> { +        PeRelocationIterator(PhantomData) +    } + +    fn flags(&self) -> SectionFlags { +        SectionFlags::Coff { +            characteristics: self.section.characteristics.get(LE), +        } +    } +} + +impl<'data> SectionTable<'data> { +    /// Return the file 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)) +    } + +    /// Return the data starting at the given virtual address, up to the end of the +    /// section containing it. +    /// +    /// Ignores sections with invalid 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)) +    } + +    /// Return the data of the section that contains the given virtual address in a PE file. +    /// +    /// Also returns the virtual address of that section. +    /// +    /// Ignores sections with invalid data. +    pub fn pe_data_containing<R: ReadRef<'data>>( +        &self, +        data: R, +        va: u32, +    ) -> Option<(&'data [u8], u32)> { +        self.iter() +            .find_map(|section| section.pe_data_containing(data, va)) +    } + +    /// Return the section that contains a given virtual address. +    pub fn section_containing(&self, va: u32) -> Option<&'data ImageSectionHeader> { +        self.iter().find(|section| section.contains_rva(va)) +    } +} + +impl pe::ImageSectionHeader { +    /// Return the offset and size of the section in a PE file. +    /// +    /// The size of the range will be the minimum of the file size and virtual size. +    pub fn pe_file_range(&self) -> (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 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 +    /// 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)> { +        let section_va = self.virtual_address.get(LE); +        let offset = va.checked_sub(section_va)?; +        let (section_offset, section_size) = self.pe_file_range(); +        // Address must be within section (and not at its end). +        if offset < section_size { +            Some((section_offset.checked_add(offset)?, section_size - offset)) +        } else { +            None +        } +    } + +    /// 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(); +        data.read_bytes_at(offset.into(), size.into()) +            .read_error("Invalid PE section offset or size") +    } + +    /// Return the data starting at the given virtual address, up to the end of the +    /// section. +    /// +    /// 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)?; +        data.read_bytes_at(offset.into(), size.into()).ok() +    } + +    /// Tests whether a given RVA is part of this section +    pub fn contains_rva(&self, va: u32) -> bool { +        let section_va = self.virtual_address.get(LE); +        match va.checked_sub(section_va) { +            None => false, +            Some(offset) => { +                // Address must be within section (and not at its end). +                offset < self.virtual_size.get(LE) +            } +        } +    } + +    /// Return the section data if it contains the given virtual address. +    /// +    /// Also returns the virtual address of that section. +    /// +    /// Ignores sections with invalid data. +    pub fn pe_data_containing<'data, R: ReadRef<'data>>( +        &self, +        data: R, +        va: u32, +    ) -> 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(); +        // Address must be within section (and not at its end). +        if offset < section_size { +            let section_data = data +                .read_bytes_at(section_offset.into(), section_size.into()) +                .ok()?; +            Some((section_data, section_va)) +        } else { +            None +        } +    } +} + +/// An iterator for the relocations in an [`PeSection`]. +/// +/// This is a stub that doesn't implement any functionality. +#[derive(Debug)] +pub struct PeRelocationIterator<'data, 'file, R = &'data [u8]>( +    PhantomData<(&'data (), &'file (), R)>, +); + +impl<'data, 'file, R> Iterator for PeRelocationIterator<'data, 'file, R> { +    type Item = (u64, Relocation); + +    fn next(&mut self) -> Option<Self::Item> { +        None +    } +}  | 
