use super::{ParseSectionData, Sections};
use crate::{
containers::Table,
error::{PewterError, Result},
io::{ReadData, Reader, WriteData},
pe::{
coff::CoffFileHeader,
optional_header::{OptionalHeader, OptionalHeaderMagic},
},
};
use crate::{string::String, vec::Vec};
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct ImportTableDataDirectory {
pub entries: Table<ImportTableDataDirectoryEntry>,
}
impl ParseSectionData for ImportTableDataDirectory {
fn parse(
section_data: &[u8],
sections: &super::Sections,
optional_header: &OptionalHeader,
_: &CoffFileHeader,
) -> Result<Self> {
let entries = {
let mut import_lookup_table_ptr = section_data.as_ref();
let mut import_directory_tables = Vec::with_capacity(5);
loop {
let dir: ImportDirectoryTable = import_lookup_table_ptr.read()?;
if dir.is_null() {
break;
}
import_directory_tables.push(ImportTableDataDirectoryEntry::parse(
dir,
sections,
optional_header.standard_fields.magic,
)?);
}
Table(import_directory_tables)
};
Ok(Self { entries })
}
}
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct ImportTableDataDirectoryEntry {
pub import_directory_table: ImportDirectoryTable,
pub dll_name: String,
pub import_lookup_table: Table<ImportTableRow>,
}
impl ImportTableDataDirectoryEntry {
pub fn parse(
import_directory_table: ImportDirectoryTable,
sections: &Sections,
magic: OptionalHeaderMagic,
) -> Result<Self> {
let import_lookup_table_data = sections
.find_rva_data(import_directory_table.import_lookup_table_rva as usize)
.ok_or_else(|| {
PewterError::invalid_image_format(
"Failed to map import_lookup_table_rva inside image",
)
})?;
let import_lookup_table = {
let mut lookup_table_data_ptr = import_lookup_table_data;
let mut lookup_items = Vec::new();
loop {
let lookup_entry = match magic {
OptionalHeaderMagic::PE32 => {
ImportTableRow::from_u32(sections, u32::read(&mut lookup_table_data_ptr)?)?
}
OptionalHeaderMagic::PE32Plus => {
ImportTableRow::from_u64(sections, u64::read(&mut lookup_table_data_ptr)?)?
}
};
if let Some(lookup_entry) = lookup_entry {
lookup_items.push(lookup_entry);
} else {
break;
}
}
Table(lookup_items)
};
let dll_name = {
let dll_name_data = sections
.find_rva_data(import_directory_table.name_rva as usize)
.ok_or_else(|| {
PewterError::invalid_image_format(
"Failed to map import_directory_table.name_rva inside image",
)
})?;
let null_terminator = dll_name_data.iter().position(|c| *c == 0).unwrap_or(0);
String::from_utf8_lossy(&dll_name_data[..null_terminator]).into()
};
Ok(Self {
import_directory_table,
dll_name,
import_lookup_table,
})
}
}
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct ImportDirectoryTable {
pub import_lookup_table_rva: u32,
pub time_date_stamp: u32,
pub fowarder_chain: u32,
pub name_rva: u32,
pub import_address_table_rva: u32,
}
impl ImportDirectoryTable {
pub fn is_null(&self) -> bool {
self == &ImportDirectoryTable::default()
}
}
impl ReadData for ImportDirectoryTable {
fn read(reader: &mut impl crate::io::Reader) -> crate::error::Result<Self> {
Ok(Self {
import_lookup_table_rva: reader.read()?,
time_date_stamp: reader.read()?,
fowarder_chain: reader.read()?,
name_rva: reader.read()?,
import_address_table_rva: reader.read()?,
})
}
}
impl WriteData for &ImportDirectoryTable {
fn write_to(self, writer: &mut impl crate::io::Writer) -> crate::error::Result<()> {
writer.write(self.import_lookup_table_rva)?;
writer.write(self.time_date_stamp)?;
writer.write(self.fowarder_chain)?;
writer.write(self.name_rva)?;
writer.write(self.import_address_table_rva)?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ImportTableRow {
Ordinal(u16),
HintName {
hint: u16,
name_rva: u32,
name: String,
},
}
impl ImportTableRow {
pub fn is_null(&self) -> bool {
matches!(self, Self::Ordinal(0))
}
fn from_lower_bits(
sections: &Sections,
import_by_ordinal: bool,
rest_of_fields: u32,
) -> Result<Self> {
if import_by_ordinal {
return Ok(ImportTableRow::Ordinal((rest_of_fields & 0xFF_FF) as u16));
}
let name_rva = (rest_of_fields & 0x7F_FF_FF_FF) as u32;
let mut hint_rva_location = sections.find_rva_data(name_rva as usize).ok_or_else(|| {
PewterError::invalid_image_format("Failed to map \"Hint/Name Table RVA\" inside image")
})?;
let hint = u16::read(&mut hint_rva_location)?;
let str_pos = hint_rva_location.iter().position(|c| *c == 0).unwrap_or(0);
Ok(ImportTableRow::HintName {
hint,
name_rva,
name: String::from_utf8_lossy(&hint_rva_location[..str_pos]).into(),
})
}
pub fn from_u32(sections: &Sections, val: u32) -> Result<Option<Self>> {
const PE32_LOOKUP_TABLE_ORDINAL_MASK: u32 = 0x80000000;
if val == 0 {
return Ok(None);
}
let import_by_ordinal = (val & PE32_LOOKUP_TABLE_ORDINAL_MASK) != 0;
let rest_of_fields = (val & !PE32_LOOKUP_TABLE_ORDINAL_MASK) as u32;
Self::from_lower_bits(sections, import_by_ordinal, rest_of_fields).map(Some)
}
pub fn from_u64(sections: &Sections, val: u64) -> Result<Option<Self>> {
const PE32_PLUS_LOOKUP_TABLE_ORDINAL_MASK: u64 = 0x8000000000000000;
if val == 0 {
return Ok(None);
}
let import_by_ordinal = (val & PE32_PLUS_LOOKUP_TABLE_ORDINAL_MASK) != 0;
let rest_of_fields = (val & !PE32_PLUS_LOOKUP_TABLE_ORDINAL_MASK) as u32;
Self::from_lower_bits(sections, import_by_ordinal, rest_of_fields).map(Some)
}
}
impl Default for ImportTableRow {
fn default() -> Self {
Self::Ordinal(0)
}
}