use core::fmt;
use core::marker::PhantomData;
use core::mem;
use crate::endian::{LittleEndian as LE, U16};
use crate::pe;
use crate::pod::Pod;
use crate::read::{self, Bytes, Error, ReadError, ReadRef, Result};
use super::{ImageNtHeaders, PeFile, SectionTable};
#[derive(Debug, Clone)]
pub struct ImportTable<'data> {
import_address: u32,
import_section_data: Bytes<'data>,
import_section_address: u32,
name_section_data: Bytes<'data>,
name_section_address: u32,
}
impl<'data> ImportTable<'data> {
pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self {
ImportTable {
import_address,
import_section_data: Bytes(section_data),
import_section_address: section_address,
name_section_data: Bytes(section_data),
name_section_address: section_address,
}
}
pub fn from_sections<R: ReadRef<'data>>(
data: R,
sections: &SectionTable<'data>,
import_address: u32,
) -> Result<Self> {
let (section_data, section_address) = sections
.pe_data_containing(data, import_address)
.read_error("Invalid import data dir virtual address")?;
let mut imports = Self::new(section_data, section_address, import_address);
if let Ok(mut descriptors) = imports.descriptors() {
if let Ok(Some(descriptor)) = descriptors.next() {
if let Some((name_section_data, name_section_address)) =
sections.pe_data_containing(data, descriptor.name.get(LE))
{
imports.name_section_data = Bytes(name_section_data);
imports.name_section_address = name_section_address;
}
}
}
Ok(imports)
}
pub fn descriptors(&self) -> Result<ImportDescriptorIterator<'data>> {
let offset = self
.import_address
.wrapping_sub(self.import_section_address);
let mut data = self.import_section_data;
data.skip(offset as usize)
.read_error("Invalid PE import descriptor address")?;
Ok(ImportDescriptorIterator { data, null: false })
}
pub fn name(&self, address: u32) -> Result<&'data [u8]> {
self.name_section_data
.read_string_at(address.wrapping_sub(self.name_section_address) as usize)
.read_error("Invalid PE import descriptor name")
}
pub fn thunks(&self, address: u32) -> Result<ImportThunkList<'data>> {
let offset = address.wrapping_sub(self.import_section_address);
let mut data = self.import_section_data;
data.skip(offset as usize)
.read_error("Invalid PE import thunk table address")?;
Ok(ImportThunkList { data, null: false })
}
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))
}
}
pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> {
let offset = address.wrapping_sub(self.name_section_address);
let mut data = self.name_section_data;
data.skip(offset as usize)
.read_error("Invalid PE import thunk address")?;
let hint = data
.read::<U16<LE>>()
.read_error("Missing PE import thunk hint")?
.get(LE);
let name = data
.read_string()
.ok()
.filter(|s| !s.is_empty())
.read_error("Missing PE import thunk name")?;
Ok((hint, name))
}
}
#[derive(Debug, Clone)]
pub struct ImportDescriptorIterator<'data> {
data: Bytes<'data>,
null: bool,
}
impl<'data> ImportDescriptorIterator<'data> {
pub fn next(&mut self) -> Result<Option<&'data pe::ImageImportDescriptor>> {
if self.null {
return Ok(None);
}
let Ok(import_desc) = self.data.read::<pe::ImageImportDescriptor>() else {
self.null = true;
return Err(Error("Missing PE null import descriptor"));
};
if import_desc.is_null() {
self.null = true;
Ok(None)
} else {
Ok(Some(import_desc))
}
}
}
impl<'data> Iterator for ImportDescriptorIterator<'data> {
type Item = Result<&'data pe::ImageImportDescriptor>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
#[derive(Debug, Clone)]
pub struct ImportThunkList<'data> {
data: Bytes<'data>,
null: bool,
}
impl<'data> ImportThunkList<'data> {
pub fn get<Pe: ImageNtHeaders>(&self, index: usize) -> Result<Pe::ImageThunkData> {
let thunk = index
.checked_mul(mem::size_of::<Pe::ImageThunkData>())
.and_then(|offset| self.data.read_at(offset).ok())
.read_error("Invalid PE import thunk index")?;
Ok(*thunk)
}
pub fn next<Pe: ImageNtHeaders>(&mut self) -> Result<Option<Pe::ImageThunkData>> {
if self.null {
return Ok(None);
}
let Ok(thunk) = self.data.read::<Pe::ImageThunkData>() else {
self.null = true;
return Err(Error("Missing PE null import thunk"));
};
if thunk.address() == 0 {
self.null = true;
Ok(None)
} else {
Ok(Some(*thunk))
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Import<'data> {
Ordinal(u16),
Name(u16, &'data [u8]),
}
#[allow(missing_docs)]
pub trait ImageThunkData: fmt::Debug + Pod + read::private::Sealed {
fn raw(self) -> u64;
fn is_ordinal(self) -> bool;
fn ordinal(self) -> u16;
fn address(self) -> u32;
}
impl read::private::Sealed for pe::ImageThunkData64 {}
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 read::private::Sealed for pe::ImageThunkData32 {}
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
}
}
#[derive(Debug, Clone)]
pub struct DelayLoadImportTable<'data> {
section_data: Bytes<'data>,
section_address: u32,
import_address: u32,
}
impl<'data> DelayLoadImportTable<'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,
}
}
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, null: false })
}
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")
}
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, null: false })
}
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))
}
}
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::<U16<LE>>()
.read_error("Missing PE delay load import thunk hint")?
.get(LE);
let name = data
.read_string()
.ok()
.filter(|s| !s.is_empty())
.read_error("Missing PE delay load import thunk name")?;
Ok((hint, name))
}
}
#[derive(Debug, Clone)]
pub struct DelayLoadDescriptorIterator<'data> {
data: Bytes<'data>,
null: bool,
}
impl<'data> DelayLoadDescriptorIterator<'data> {
pub fn next(&mut self) -> Result<Option<&'data pe::ImageDelayloadDescriptor>> {
if self.null {
return Ok(None);
}
let Ok(import_desc) = self.data.read::<pe::ImageDelayloadDescriptor>() else {
self.null = true;
return Err(Error("Missing PE null delay-load import descriptor"));
};
if import_desc.is_null() {
self.null = true;
Ok(None)
} else {
Ok(Some(import_desc))
}
}
}
impl<'data> Iterator for DelayLoadDescriptorIterator<'data> {
type Item = Result<&'data pe::ImageDelayloadDescriptor>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
pub type PeImportLibraryIterator32<'data, 'file, R = &'data [u8]> =
PeImportLibraryIterator<'data, 'file, pe::ImageNtHeaders32, R>;
pub type PeImportLibraryIterator64<'data, 'file, R = &'data [u8]> =
PeImportLibraryIterator<'data, 'file, pe::ImageNtHeaders64, R>;
pub struct PeImportLibraryIterator<'data, 'file, Pe, R = &'data [u8]>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
table: Option<ImportTable<'data>>,
descs: Option<ImportDescriptorIterator<'data>>,
delay_table: Option<DelayLoadImportTable<'data>>,
delay_descs: Option<DelayLoadDescriptorIterator<'data>>,
marker: PhantomData<(&'file (), Pe, R)>,
}
impl<'data, 'file, Pe, R> PeImportLibraryIterator<'data, 'file, Pe, R>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
pub(super) fn new(file: &'file PeFile<'data, Pe, R>) -> Result<Self> {
let table = file.import_table()?;
let descs = table
.as_ref()
.map(|table| table.descriptors())
.transpose()?;
let delay_table = file.delay_load_import_table()?;
let delay_descs = delay_table
.as_ref()
.map(|table| table.descriptors())
.transpose()?;
Ok(PeImportLibraryIterator {
table,
descs,
delay_table,
delay_descs,
marker: PhantomData,
})
}
fn next(&mut self) -> read::Result<Option<read::ImportLibrary<'data>>> {
if let Some(table) = self.table.as_ref() {
if let Some(descs) = self.descs.as_mut() {
if let Some(desc) = descs.next()? {
return Ok(Some(read::ImportLibrary {
name: table.name(desc.name.get(LE))?,
flags: read::ImportLibraryFlags::Pe { delay: false },
}));
}
self.descs = None;
}
self.table = None;
}
if let Some(table) = self.delay_table.as_ref() {
if let Some(descs) = self.delay_descs.as_mut() {
if let Some(desc) = descs.next()? {
if desc.attributes.get(LE) & pe::IMAGE_DELAYLOAD_RVA_BASED == 0 {
return Err(Error("Unsupported PE delay-load non-RVA based descriptor"));
}
return Ok(Some(read::ImportLibrary {
name: table.name(desc.dll_name_rva.get(LE))?,
flags: read::ImportLibraryFlags::Pe { delay: true },
}));
}
self.delay_descs = None;
}
self.delay_table = None;
}
Ok(None)
}
}
impl<'data, 'file, Pe, R> fmt::Debug for PeImportLibraryIterator<'data, 'file, Pe, R>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PeImportLibraryIterator").finish()
}
}
impl<'data, 'file, Pe, R> Iterator for PeImportLibraryIterator<'data, 'file, Pe, R>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
type Item = Result<read::ImportLibrary<'data>>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
pub type PeImportIterator32<'data, 'file, R = &'data [u8]> =
PeImportIterator<'data, 'file, pe::ImageNtHeaders32, R>;
pub type PeImportIterator64<'data, 'file, R = &'data [u8]> =
PeImportIterator<'data, 'file, pe::ImageNtHeaders64, R>;
pub struct PeImportIterator<'data, 'file, Pe, R = &'data [u8]>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
table: Option<ImportTable<'data>>,
descs: Option<ImportDescriptorIterator<'data>>,
delay_table: Option<DelayLoadImportTable<'data>>,
delay_descs: Option<DelayLoadDescriptorIterator<'data>>,
thunks: Option<ImportThunkList<'data>>,
library: &'data [u8],
marker: PhantomData<(&'file (), Pe, R)>,
}
impl<'data, 'file, Pe, R> PeImportIterator<'data, 'file, Pe, R>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
pub(super) fn new(file: &'file PeFile<'data, Pe, R>) -> Result<Self> {
let table = file.import_table()?;
let descs = table
.as_ref()
.map(|table| table.descriptors())
.transpose()?;
let delay_table = file.delay_load_import_table()?;
let delay_descs = delay_table
.as_ref()
.map(|table| table.descriptors())
.transpose()?;
Ok(PeImportIterator {
table,
descs,
delay_table,
delay_descs,
thunks: None,
library: &[],
marker: PhantomData,
})
}
fn next(&mut self) -> read::Result<Option<read::Import<'data>>> {
while let Some(table) = self.table.as_ref() {
if let Some(thunks) = self.thunks.as_mut() {
if let Some(thunk) = thunks.next::<Pe>()? {
return Ok(Some(self.import(table.import::<Pe>(thunk)?, false)));
}
self.thunks = None;
}
if let Some(descs) = self.descs.as_mut() {
if let Some(desc) = descs.next()? {
self.library = table.name(desc.name.get(LE))?;
let mut first_thunk = desc.original_first_thunk.get(LE);
if first_thunk == 0 {
first_thunk = desc.first_thunk.get(LE);
}
self.thunks = Some(table.thunks(first_thunk)?);
continue;
}
self.descs = None;
}
self.table = None;
}
while let Some(table) = self.delay_table.as_ref() {
if let Some(thunks) = self.thunks.as_mut() {
if let Some(thunk) = thunks.next::<Pe>()? {
return Ok(Some(self.import(table.import::<Pe>(thunk)?, true)));
}
self.thunks = None;
}
if let Some(descs) = self.delay_descs.as_mut() {
if let Some(desc) = descs.next()? {
if desc.attributes.get(LE) & pe::IMAGE_DELAYLOAD_RVA_BASED == 0 {
return Err(Error("Unsupported PE delay-load non-RVA based descriptor"));
}
self.library = table.name(desc.dll_name_rva.get(LE))?;
self.thunks = Some(table.thunks(desc.import_name_table_rva.get(LE))?);
continue;
}
self.delay_descs = None;
}
self.delay_table = None;
}
Ok(None)
}
fn import(&self, import: Import<'data>, delay: bool) -> read::Import<'data> {
let name = match import {
Import::Ordinal(ordinal) => read::NameOrOrdinal::Ordinal(ordinal),
Import::Name(_hint, name) => read::NameOrOrdinal::Name(name),
};
read::Import {
library: self.library,
name,
weak: false,
flags: read::ImportFlags::Pe { delay },
}
}
}
impl<'data, 'file, Pe, R> fmt::Debug for PeImportIterator<'data, 'file, Pe, R>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PeImportIterator").finish()
}
}
impl<'data, 'file, Pe, R> Iterator for PeImportIterator<'data, 'file, Pe, R>
where
Pe: ImageNtHeaders,
R: ReadRef<'data>,
{
type Item = Result<read::Import<'data>>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}