Source code

Revision control

Copy as Markdown

Other Tools

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.
#[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)
}