use std::{fmt, ops};
use crate::{Error, Result};
use crate::util::CStr;
use super::image::*;
use super::imports::Import;
use super::Pe;
pub use crate::wrap::exports::Export;
#[derive(Copy, Clone)]
pub struct Exports<'a, P> {
pe: P,
datadir: &'a IMAGE_DATA_DIRECTORY,
image: &'a IMAGE_EXPORT_DIRECTORY,
}
impl<'a, P: Pe<'a>> Exports<'a, P> {
pub(crate) fn try_from(pe: P) -> Result<Exports<'a, P>> {
let datadir = pe.data_directory().get(IMAGE_DIRECTORY_ENTRY_EXPORT).ok_or(Error::Bounds)?;
let image = pe.derva(datadir.VirtualAddress)?;
Ok(Exports { pe, datadir, image })
}
pub fn pe(&self) -> P {
self.pe
}
pub fn image(&self) -> &'a IMAGE_EXPORT_DIRECTORY {
self.image
}
pub fn dll_name(&self) -> Result<&'a CStr> {
self.pe.derva_c_str(self.image.Name)
}
pub fn ordinal_base(&self) -> Ordinal {
self.image.Base as Ordinal
}
pub fn functions(&self) -> Result<&'a [Rva]> {
self.pe.derva_slice(self.image.AddressOfFunctions, self.image.NumberOfFunctions as usize)
}
pub fn names(&self) -> Result<&'a [Rva]> {
self.pe.derva_slice(self.image.AddressOfNames, self.image.NumberOfNames as usize)
}
pub fn name_indices(&self) -> Result<&'a [u16]> {
self.pe.derva_slice(self.image.AddressOfNameOrdinals, self.image.NumberOfNames as usize)
}
pub fn by(&self) -> Result<By<'a, P>> {
let functions = match self.functions() {
Ok(functions) => functions,
Err(Error::Null) => &[],
Err(e) => return Err(e),
};
let names = match self.names() {
Ok(names) => names,
Err(Error::Null) => &[],
Err(e) => return Err(e),
};
let name_indices = match self.name_indices() {
Ok(name_indices) => name_indices,
Err(Error::Null) => &[],
Err(e) => return Err(e),
};
Ok(By { exp: *self, functions, names, name_indices })
}
fn is_forwarded(&self, rva: Rva) -> bool {
rva >= self.datadir.VirtualAddress && rva < self.datadir.VirtualAddress + self.datadir.Size
}
pub(crate) fn symbol_from_rva(&self, rva: &'a Rva) -> Result<Export<'a>> {
if *rva == 0 {
Err(Error::Null)
}
else if self.is_forwarded(*rva) {
let fwd = self.pe.derva_c_str(*rva)?;
Ok(Export::Forward(fwd))
}
else {
Ok(Export::Symbol(rva))
}
}
}
impl<'a, P: Pe<'a>> fmt::Debug for Exports<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.by() {
Ok(by) => by.fmt(f),
Err(err) => err.fmt(f),
}
}
}
#[derive(Copy, Clone)]
pub struct By<'a, P> {
exp: Exports<'a, P>,
functions: &'a [Rva],
names: &'a [Rva],
name_indices: &'a [u16],
}
impl<'a, P: Pe<'a>> ops::Deref for By<'a, P> {
type Target = Exports<'a, P>;
fn deref(&self) -> &Exports<'a, P> {
&self.exp
}
}
impl<'a, P: Pe<'a>> By<'a, P> {
pub fn functions(&self) -> &'a [Rva] {
self.functions
}
pub fn names(&self) -> &'a [Rva] {
self.names
}
pub fn name_indices(&self) -> &'a [u16] {
self.name_indices
}
pub fn check_sorted(&self) -> Result<bool> {
let mut last = CStr::empty();
for (name, _export) in self.iter_names() {
let name = name?;
if last > name {
return Ok(false);
}
last = name;
}
Ok(true)
}
pub fn ordinal(&self, ordinal: Ordinal) -> Result<Export<'a>> {
let base = self.exp.image.Base;
if (ordinal as u32) < base {
Err(Error::Bounds)
}
else {
let index = (ordinal as u32 - base) as usize;
self.index(index)
}
}
pub fn name_linear<S: AsRef<[u8]> + ?Sized>(&self, name: &S) -> Result<Export<'a>> {
self.name_linear_(name.as_ref())
}
fn name_linear_(&self, name: &[u8]) -> Result<Export<'a>> {
for hint in 0..self.names.len() {
match self.name_of_hint(hint) {
Ok(name_it) if name_it == name => return self.hint(hint),
_ => (),
}
}
Err(Error::Null)
}
pub fn name<S: AsRef<[u8]> + ?Sized>(&self, name: &S) -> Result<Export<'a>> {
self.name_(name.as_ref())
}
fn name_(&self, name: &[u8]) -> Result<Export<'a>> {
let mut lower_bound = 0;
let mut upper_bound = self.names.len();
while lower_bound != upper_bound {
let i = lower_bound + (upper_bound - lower_bound) / 2;
let name_rva = self.names[i];
let name_it = self.exp.pe.derva_c_str(name_rva)?.as_ref();
use std::cmp::Ordering::*;
match name.cmp(name_it) {
Less => upper_bound = i,
Greater => lower_bound = i + 1,
Equal => {
let &index = self.name_indices.get(i).ok_or(Error::Bounds)?;
return self.index(index as usize);
},
};
}
Err(Error::Null)
}
pub fn import(&self, import: Import) -> Result<Export<'a>> {
match import {
Import::ByName { hint, name } => {
self.hint_name_(hint, name)
},
Import::ByOrdinal { ord } => {
self.ordinal(ord)
}
}
}
pub fn index(&self, index: usize) -> Result<Export<'a>> {
let rva = self.functions.get(index).ok_or(Error::Bounds)?;
self.exp.symbol_from_rva(rva)
}
pub fn hint(&self, hint: usize) -> Result<Export<'a>> {
let &index = self.name_indices.get(hint).ok_or(Error::Bounds)?;
self.index(index as usize)
}
pub fn hint_name<S: AsRef<[u8]> + ?Sized>(&self, hint: usize, name: &S) -> Result<Export<'a>> {
self.hint_name_(hint, name.as_ref())
}
fn hint_name_(&self, hint: usize, name: &[u8]) -> Result<Export<'a>> {
if let Ok(export) = self.hint(hint) {
if let Ok(export_name) = self.name_of_hint(hint) {
if export_name == name {
return Ok(export);
}
}
}
self.name(name)
}
pub fn name_of_hint(&self, hint: usize) -> Result<&'a CStr> {
let &name_rva = self.names.get(hint).ok_or(Error::Bounds)?;
self.exp.pe.derva_c_str(name_rva)
}
pub fn name_lookup(&self, index: usize) -> Result<Import<'a>> {
match self.name_indices.iter().position(|&i| i as usize == index) {
Some(hint) => {
let name_rva = self.names[hint];
let name = self.exp.pe.derva_c_str(name_rva)?;
Ok(Import::ByName { hint, name })
},
None => {
let ord = (index as u32 + self.exp.image.Base) as Ordinal;
Ok(Import::ByOrdinal { ord })
},
}
}
pub fn iter<'s>(&'s self) -> impl 's + Clone + Iterator<Item = Result<Export<'a>>> {
self.functions.iter().map(move |rva| self.symbol_from_rva(rva))
}
pub fn iter_names<'s>(&'s self) -> impl 's + Clone + Iterator<Item = (Result<&'a CStr>, Result<Export<'a>>)> {
(0..self.names().len() as u32)
.map(move |hint| (
self.name_of_hint(hint as usize),
self.hint(hint as usize),
))
}
pub fn iter_name_indices<'s>(&'s self) -> impl 's + Clone + Iterator<Item = (Result<&'a CStr>, usize)> {
(0..self.names().len() as u32)
.map(move |hint| (
self.name_of_hint(hint as usize),
self.name_indices[hint as usize] as usize,
))
}
}
impl<'a, P: Pe<'a>> fmt::Debug for By<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Exports")
.field("dll_name", &format_args!("{:?}", self.dll_name()))
.field("time_date_stamp", &self.image.TimeDateStamp)
.field("version", &self.image.Version)
.field("ordinal_base", &self.ordinal_base())
.field("functions.len", &self.functions().len())
.field("names.len", &self.names.len())
.finish()
}
}
pub trait GetProcAddress<'a, T>: Pe<'a> {
fn get_export(self, name: T) -> Result<Export<'a>>;
#[inline(never)]
fn get_proc_address(self, name: T) -> Result<Va> {
self.rva_to_va(self.get_export(name)?.symbol().ok_or(Error::Null)?)
}
}
impl<'a, P: Pe<'a>> GetProcAddress<'a, Ordinal> for P {
fn get_export(self, name: Ordinal) -> Result<Export<'a>> {
self.exports()?.by()?.ordinal(name)
}
}
impl<'b, 'a, P: Pe<'a>> GetProcAddress<'a, Import<'b>> for P {
fn get_export(self, name: Import<'b>) -> Result<Export<'a>> {
self.exports()?.by()?.import(name)
}
}
impl<'b, 'a, P: Pe<'a>, S: AsRef<[u8]> + ?Sized> GetProcAddress<'a, &'b S> for P {
fn get_export(self, name: &'b S) -> Result<Export<'a>> {
self.exports()?.by()?.name(name)
}
}
#[cfg(feature = "serde")]
mod serde {
use crate::util::serde_helper::*;
use super::{Pe, Exports, By};
impl<'a, P: Pe<'a>> Serialize for Exports<'a, P> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.by().ok().serialize(serializer)
}
}
impl<'a, P: Pe<'a>> Serialize for By<'a, P> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut state = serializer.serialize_struct("Exports", 6)?;
state.serialize_field("dll_name", &self.dll_name().ok())?;
state.serialize_field("time_date_stamp", &self.image.TimeDateStamp)?;
state.serialize_field("version", &self.image.Version)?;
state.serialize_field("ordinal_base", &self.ordinal_base())?;
state.serialize_field("functions", &self.functions())?;
let names = self.iter_name_indices().filter_map(|(name, index)| {
name.ok().and_then(|name| name.to_str().ok()).map(|name| (name, index))
});
state.serialize_field("names", &SerdeKV(names))?;
state.end()
}
}
}
#[cfg(test)]
pub(crate) fn test<'a, P: Pe<'a>>(pe: P) -> Result<()> {
let by = pe.exports()?.by()?;
let _ = format!("{:?}", by);
let _dll_name = by.dll_name();
let _ordinal_base = by.ordinal_base();
let sorted = by.check_sorted()?;
use std::collections::HashMap;
let mut occurances = HashMap::<_, i32>::new();
for (name, _) in by.iter_names() {
if let Ok(name) = name {
*occurances.entry(name).or_default() += 1;
}
}
for (hint, (name, export)) in by.iter_names().enumerate() {
assert_eq!(name, by.name_of_hint(hint));
assert_eq!(export, by.hint(hint));
if let Ok(name) = name {
let unique = occurances[name] == 1;
if unique {
assert_eq!(export, by.name_linear(name));
if sorted {
assert_eq!(export, by.name(name));
}
}
assert_eq!(export, by.hint_name(hint, name));
assert_eq!(export, by.import(Import::ByName { hint, name }));
if sorted && unique {
assert_eq!(export, by.hint_name(0, name));
assert_eq!(export, by.import(Import::ByName { hint: 0, name }));
}
}
}
Ok(())
}