summaryrefslogtreecommitdiffstats
path: root/third_party/rust/goblin/src/pe/import.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/goblin/src/pe/import.rs')
-rw-r--r--third_party/rust/goblin/src/pe/import.rs417
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)
+ }
+}