diff options
Diffstat (limited to 'third_party/rust/goblin/src/pe/import.rs')
-rw-r--r-- | third_party/rust/goblin/src/pe/import.rs | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/pe/import.rs b/third_party/rust/goblin/src/pe/import.rs new file mode 100644 index 0000000000..0284fc3276 --- /dev/null +++ b/third_party/rust/goblin/src/pe/import.rs @@ -0,0 +1,417 @@ +use alloc::borrow::Cow; +use alloc::vec::Vec; +use core::fmt::{Debug, LowerHex}; + +use crate::error; +use scroll::ctx::TryFromCtx; +use scroll::{Pread, Pwrite, SizeWith}; + +use crate::pe::data_directories; +use crate::pe::options; +use crate::pe::section_table; +use crate::pe::utils; + +use log::{debug, warn}; + +pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000; +pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000; +pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff; +pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff; + +pub trait Bitfield<'a>: + Into<u64> + + PartialEq + + Eq + + LowerHex + + Debug + + TryFromCtx<'a, scroll::Endian, Error = scroll::Error> +{ + fn is_ordinal(&self) -> bool; + fn to_ordinal(&self) -> u16; + fn to_rva(&self) -> u32; + fn size_of() -> usize; + fn is_zero(&self) -> bool; +} + +impl<'a> Bitfield<'a> for u64 { + fn is_ordinal(&self) -> bool { + self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64 + } + fn to_ordinal(&self) -> u16 { + (0xffff & self) as u16 + } + fn to_rva(&self) -> u32 { + (self & IMPORT_RVA_MASK_64) as u32 + } + fn size_of() -> usize { + 8 + } + fn is_zero(&self) -> bool { + *self == 0 + } +} + +impl<'a> Bitfield<'a> for u32 { + fn is_ordinal(&self) -> bool { + self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32 + } + fn to_ordinal(&self) -> u16 { + (0xffff & self) as u16 + } + fn to_rva(&self) -> u32 { + (self & IMPORT_RVA_MASK_32) as u32 + } + fn size_of() -> usize { + 4 + } + fn is_zero(&self) -> bool { + *self == 0 + } +} + +#[derive(Debug, Clone)] +pub struct HintNameTableEntry<'a> { + pub hint: u16, + pub name: &'a str, +} + +impl<'a> HintNameTableEntry<'a> { + fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> { + let offset = &mut offset; + let hint = bytes.gread_with(offset, scroll::LE)?; + let name = bytes.pread::<&'a str>(*offset)?; + Ok(HintNameTableEntry { hint, name }) + } +} + +#[derive(Debug, Clone)] +pub enum SyntheticImportLookupTableEntry<'a> { + OrdinalNumber(u16), + HintNameTableRVA((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/ +} + +pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>; + +impl<'a> SyntheticImportLookupTableEntry<'a> { + pub fn parse<T: Bitfield<'a>>( + bytes: &'a [u8], + offset: usize, + sections: &[section_table::SectionTable], + file_alignment: u32, + ) -> error::Result<ImportLookupTable<'a>> { + Self::parse_with_opts::<T>( + bytes, + offset, + sections, + file_alignment, + &options::ParseOptions::default(), + ) + } + + pub fn parse_with_opts<T: Bitfield<'a>>( + bytes: &'a [u8], + mut offset: usize, + sections: &[section_table::SectionTable], + file_alignment: u32, + opts: &options::ParseOptions, + ) -> error::Result<ImportLookupTable<'a>> { + let le = scroll::LE; + let offset = &mut offset; + let mut table = Vec::new(); + loop { + let bitfield: T = bytes.gread_with(offset, le)?; + if bitfield.is_zero() { + debug!("imports done"); + break; + } else { + let entry = { + debug!("bitfield {:#x}", bitfield); + use self::SyntheticImportLookupTableEntry::*; + if bitfield.is_ordinal() { + let ordinal = bitfield.to_ordinal(); + debug!("importing by ordinal {:#x}", ordinal); + OrdinalNumber(ordinal) + } else { + let rva = bitfield.to_rva(); + let hentry = { + debug!("searching for RVA {:#x}", rva); + if let Some(offset) = + utils::find_offset(rva as usize, sections, file_alignment, opts) + { + debug!("offset {:#x}", offset); + HintNameTableEntry::parse(bytes, offset)? + } else { + warn!("Entry {} has bad RVA: {:#x}", table.len(), rva); + continue; + } + }; + HintNameTableRVA((rva, hentry)) + } + }; + table.push(entry); + } + } + Ok(table) + } +} + +// get until entry is 0 +pub type ImportAddressTable = Vec<u64>; + +#[repr(C)] +#[derive(Debug, Pread, Pwrite, SizeWith)] +pub struct ImportDirectoryEntry { + pub import_lookup_table_rva: u32, + pub time_date_stamp: u32, + pub forwarder_chain: u32, + pub name_rva: u32, + pub import_address_table_rva: u32, +} + +pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20; + +impl ImportDirectoryEntry { + pub fn is_null(&self) -> bool { + (self.import_lookup_table_rva == 0) + && (self.time_date_stamp == 0) + && (self.forwarder_chain == 0) + && (self.name_rva == 0) + && (self.import_address_table_rva == 0) + } +} + +#[derive(Debug)] +pub struct SyntheticImportDirectoryEntry<'a> { + pub import_directory_entry: ImportDirectoryEntry, + /// Computed + pub name: &'a str, + /// The import lookup table is a vector of either ordinals, or RVAs + import names + pub import_lookup_table: Option<ImportLookupTable<'a>>, + /// Computed + pub import_address_table: ImportAddressTable, +} + +impl<'a> SyntheticImportDirectoryEntry<'a> { + pub fn parse<T: Bitfield<'a>>( + bytes: &'a [u8], + import_directory_entry: ImportDirectoryEntry, + sections: &[section_table::SectionTable], + file_alignment: u32, + ) -> error::Result<SyntheticImportDirectoryEntry<'a>> { + Self::parse_with_opts::<T>( + bytes, + import_directory_entry, + sections, + file_alignment, + &options::ParseOptions::default(), + ) + } + + pub fn parse_with_opts<T: Bitfield<'a>>( + bytes: &'a [u8], + import_directory_entry: ImportDirectoryEntry, + sections: &[section_table::SectionTable], + file_alignment: u32, + opts: &options::ParseOptions, + ) -> error::Result<SyntheticImportDirectoryEntry<'a>> { + const LE: scroll::Endian = scroll::LE; + let name_rva = import_directory_entry.name_rva; + let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment, opts)?; + let import_lookup_table = { + let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva; + let import_address_table_rva = import_directory_entry.import_address_table_rva; + if let Some(import_lookup_table_offset) = utils::find_offset( + import_lookup_table_rva as usize, + sections, + file_alignment, + opts, + ) { + debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva); + let import_lookup_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>( + bytes, + import_lookup_table_offset, + sections, + file_alignment, + opts, + )?; + debug!( + "Successfully synthesized import lookup table entry from lookup table: {:#?}", + import_lookup_table + ); + Some(import_lookup_table) + } else if let Some(import_address_table_offset) = utils::find_offset( + import_address_table_rva as usize, + sections, + file_alignment, + opts, + ) { + debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva); + let import_address_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>( + bytes, + import_address_table_offset, + sections, + file_alignment, + opts, + )?; + debug!( + "Successfully synthesized import lookup table entry from IAT: {:#?}", + import_address_table + ); + Some(import_address_table) + } else { + None + } + }; + + let import_address_table_offset = &mut utils::find_offset( + import_directory_entry.import_address_table_rva as usize, + sections, + file_alignment, + opts, + ) + .ok_or_else(|| { + error::Error::Malformed(format!( + "Cannot map import_address_table_rva {:#x} into offset for {}", + import_directory_entry.import_address_table_rva, name + )) + })?; + let mut import_address_table = Vec::new(); + loop { + let import_address = bytes + .gread_with::<T>(import_address_table_offset, LE)? + .into(); + if import_address == 0 { + break; + } else { + import_address_table.push(import_address); + } + } + Ok(SyntheticImportDirectoryEntry { + import_directory_entry, + name, + import_lookup_table, + import_address_table, + }) + } +} + +#[derive(Debug)] +/// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from +pub struct ImportData<'a> { + pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>, +} + +impl<'a> ImportData<'a> { + pub fn parse<T: Bitfield<'a>>( + bytes: &'a [u8], + dd: data_directories::DataDirectory, + sections: &[section_table::SectionTable], + file_alignment: u32, + ) -> error::Result<ImportData<'a>> { + Self::parse_with_opts::<T>( + bytes, + dd, + sections, + file_alignment, + &options::ParseOptions::default(), + ) + } + + pub fn parse_with_opts<T: Bitfield<'a>>( + bytes: &'a [u8], + dd: data_directories::DataDirectory, + sections: &[section_table::SectionTable], + file_alignment: u32, + opts: &options::ParseOptions, + ) -> error::Result<ImportData<'a>> { + let import_directory_table_rva = dd.virtual_address as usize; + debug!( + "import_directory_table_rva {:#x}", + import_directory_table_rva + ); + let offset = + &mut utils::find_offset(import_directory_table_rva, sections, file_alignment, opts) + .ok_or_else(|| { + error::Error::Malformed(format!( + "Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", + import_directory_table_rva + )) + })?; + debug!("import data offset {:#x}", offset); + let mut import_data = Vec::new(); + loop { + let import_directory_entry: ImportDirectoryEntry = + bytes.gread_with(offset, scroll::LE)?; + debug!("{:#?}", import_directory_entry); + if import_directory_entry.is_null() { + break; + } else { + let entry = SyntheticImportDirectoryEntry::parse_with_opts::<T>( + bytes, + import_directory_entry, + sections, + file_alignment, + opts, + )?; + debug!("entry {:#?}", entry); + import_data.push(entry); + } + } + debug!("finished ImportData"); + Ok(ImportData { import_data }) + } +} + +#[derive(Debug)] +/// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to +pub struct Import<'a> { + pub name: Cow<'a, str>, + pub dll: &'a str, + pub ordinal: u16, + pub offset: usize, + pub rva: usize, + pub size: usize, +} + +impl<'a> Import<'a> { + pub fn parse<T: Bitfield<'a>>( + _bytes: &'a [u8], + import_data: &ImportData<'a>, + _sections: &[section_table::SectionTable], + ) -> error::Result<Vec<Import<'a>>> { + let mut imports = Vec::new(); + for data in &import_data.import_data { + if let Some(ref import_lookup_table) = data.import_lookup_table { + let dll = data.name; + let import_base = data.import_directory_entry.import_address_table_rva as usize; + debug!("Getting imports from {}", &dll); + for (i, entry) in import_lookup_table.iter().enumerate() { + let offset = import_base + (i * T::size_of()); + use self::SyntheticImportLookupTableEntry::*; + let (rva, name, ordinal) = match *entry { + HintNameTableRVA((rva, ref hint_entry)) => { + // if hint_entry.name = "" && hint_entry.hint = 0 { + // println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva); + // } + (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint) + } + OrdinalNumber(ordinal) => { + let name = format!("ORDINAL {}", ordinal); + (0x0, Cow::Owned(name), ordinal) + } + }; + let import = Import { + name, + ordinal, + dll, + size: T::size_of(), + offset, + rva: rva as usize, + }; + imports.push(import); + } + } + } + Ok(imports) + } +} |