Skip to content

Commit

Permalink
Better pe::Export struct
Browse files Browse the repository at this point in the history
  • Loading branch information
daladim committed Aug 11, 2021
1 parent 4c414ad commit c619281
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 130 deletions.
165 changes: 40 additions & 125 deletions src/read/pe/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,110 +7,50 @@ use crate::read::ReadError;
use crate::read::Result;
use crate::{pe, Bytes, LittleEndian as LE, U16Bytes, U32Bytes};

/// Possible exports from a PE file
/// Where an export is pointing to
#[derive(Clone)]
pub enum Export<'data> {
/// A named exported symbol from this PE file
Regular {
/// The ordinal of this export
ordinal: u32,
/// The name of this export
name: &'data [u8],
/// The virtual address pointed to by this export
address: u64,
},

/// An export that only has an ordinal, but no name
ByOrdinal {
/// The ordinal of this export
ordinal: u32,
/// The virtual address pointed to by this export
address: u64,
},

/// A forwarded export (i.e. a symbol that is contained in some other DLL).
/// This concept is [PE-specific](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table)
Forwarded {
/// The ordinal of this export
ordinal: u32,
/// The name of this export
name: &'data [u8],
/// The name of the actual symbol, in some other lib.
/// for example, "MYDLL.expfunc" or "MYDLL.#27"
forwarded_to: &'data [u8],
},

/// A forwarded export (i.e. a symbol that is contained in some other DLL) that has no name.
/// This concept is [PE-specific](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table)
ForwardedByOrdinal {
/// The ordinal of this export
ordinal: u32,
/// The name of the actual symbol, in some other lib.
/// for example, "MYDLL.expfunc" or "MYDLL.#27"
forwarded_to: &'data [u8],
},
pub enum Target<'data> {
/// The export points at a RVA in the file
Local(u64),
/// The export is "forwarded" to another DLL
///
/// for example, "MYDLL.expfunc" or "MYDLL.#27"
Forwarded(&'data [u8]),
}

impl<'a> Export<'a> {
/// Returns the ordinal of this export
pub fn ordinal(&self) -> u32 {
match &self {
&Export::Regular { ordinal, .. }
| &Export::Forwarded { ordinal, .. }
| &Export::ByOrdinal { ordinal, .. }
| &Export::ForwardedByOrdinal { ordinal, .. } => *ordinal,
}
}
/// An export from a PE file
///
/// There are multiple kinds of PE exports (with or without a name, and local or exported)
#[derive(Clone)]
pub struct Export<'data> {
/// The ordinal of the export
pub ordinal: u32,
/// The name of the export, if ever the PE file has named it
pub name: Option<&'data [u8]>,
/// The target of this export
pub target: Target<'data>,
}

/// Whether this export has a name
pub fn has_name(&self) -> bool {
match &self {
&Export::Regular { .. } | &Export::Forwarded { .. } => true,
&Export::ByOrdinal { .. } | &Export::ForwardedByOrdinal { .. } => false,
}
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(|n| ByteString(n)))
.field("target", &self.target)
.finish()
}
}

impl<'a> Debug for Export<'a> {
impl<'a> Debug for Target<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
match &self {
Export::Regular {
ordinal,
name,
address,
} => f
.debug_struct("Regular")
.field("ordinal", &ordinal)
.field("data", &ByteString(name))
.field("address", &address)
.finish(),
Export::ByOrdinal { ordinal, address } => f
.debug_struct("ByOrdinal")
.field("ordinal", &ordinal)
.field("address", &address)
.finish(),
Export::Forwarded {
ordinal,
name,
forwarded_to,
} => f
.debug_struct("Forwarded")
.field("ordinal", &ordinal)
.field("name", &ByteString(name))
.field("forwarded_to", &ByteString(forwarded_to))
.finish(),
Export::ForwardedByOrdinal {
ordinal,
forwarded_to,
} => f
.debug_struct("ForwardedByOrdinal")
.field("ordinal", &ordinal)
.field("forwarded_to", &ByteString(forwarded_to))
.finish(),
match self {
Target::Local(addr) => f.write_fmt(format_args!("Local({:#x})", addr)),
Target::Forwarded(forward) => f.write_fmt(format_args!("Forwarded({:?})", ByteString(forward))),
}
}
}


impl<'data, Pe, R> super::PeFile<'data, Pe, R>
where
Pe: super::ImageNtHeaders,
Expand Down Expand Up @@ -171,17 +111,19 @@ where

// is it a regular or forwarded export?
if address < export_va || (address - export_va) >= export_size {
exports.push(Export::ByOrdinal {
exports.push(Export {
ordinal: ordinal,
address: self.common.image_base.wrapping_add(address as u64),
target: Target::Local(self.common.image_base.wrapping_add(address as u64)),
name: None, // might be populated later
});
} else {
let forwarded_to = export_data
.read_string_at(address.wrapping_sub(export_va) as usize)
.read_error("Invalid target for PE forwarded export")?;
exports.push(Export::ForwardedByOrdinal {
exports.push(Export {
ordinal: ordinal,
forwarded_to: forwarded_to,
target: Target::Forwarded(forwarded_to),
name: None, // might be populated later
});
}
}
Expand All @@ -196,35 +138,8 @@ where
.read_string_at(name_ptr.get(LE).wrapping_sub(export_va) as usize)
.read_error("Invalid PE export name entry")?;

let unnamed_equivalent = exports.get(ordinal_index as usize).cloned();
match unnamed_equivalent {
Some(Export::ByOrdinal { ordinal, address }) => {
let _ = core::mem::replace(
&mut exports[ordinal_index as usize],
Export::Regular {
name,
address,
ordinal,
},
);
}

Some(Export::ForwardedByOrdinal {
ordinal,
forwarded_to,
}) => {
let _ = core::mem::replace(
&mut exports[ordinal_index as usize],
Export::Forwarded {
name,
ordinal,
forwarded_to,
},
);
}

_ => continue, // unless ordinals are not unique in the ordinals array, this should not happen
}
exports.get_mut(ordinal_index as usize)
.map(|export| export.name = Some(name));
}

Ok(exports)
Expand Down
13 changes: 8 additions & 5 deletions src/read/pe/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,14 @@ where
self.export_table().map(|pe_exports| {
let mut exports = Vec::new();
for pe_export in pe_exports {
if let PeExport::Regular { name, address, .. } = pe_export {
exports.push(Export {
name: ByteString(name),
address,
})
match (pe_export.name, pe_export.target) {
(Some(name), super::export::Target::Local(address)) => {
exports.push(Export {
name: ByteString(name),
address,
})
},
_ => continue,
}
}
exports
Expand Down

0 comments on commit c619281

Please sign in to comment.