Source code

Revision control

Copy as Markdown

Other Tools

use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt::{Debug, LowerHex};
use crate::error;
use scroll::ctx::TryFromCtx;
use scroll::{Pread, Pwrite, SizeWith};
use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;
use log::{debug, warn};
pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff;
pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff;
pub trait Bitfield<'a>:
Into<u64>
+ PartialEq
+ Eq
+ LowerHex
+ Debug
+ TryFromCtx<'a, scroll::Endian, Error = scroll::Error>
{
fn is_ordinal(&self) -> bool;
fn to_ordinal(&self) -> u16;
fn to_rva(&self) -> u32;
fn size_of() -> usize;
fn is_zero(&self) -> bool;
}
impl<'a> Bitfield<'a> for u64 {
fn is_ordinal(&self) -> bool {
self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64
}
fn to_ordinal(&self) -> u16 {
(0xffff & self) as u16
}
fn to_rva(&self) -> u32 {
(self & IMPORT_RVA_MASK_64) as u32
}
fn size_of() -> usize {
8
}
fn is_zero(&self) -> bool {
*self == 0
}
}
impl<'a> Bitfield<'a> for u32 {
fn is_ordinal(&self) -> bool {
self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32
}
fn to_ordinal(&self) -> u16 {
(0xffff & self) as u16
}
fn to_rva(&self) -> u32 {
(self & IMPORT_RVA_MASK_32) as u32
}
fn size_of() -> usize {
4
}
fn is_zero(&self) -> bool {
*self == 0
}
}
#[derive(Debug, Clone)]
pub struct HintNameTableEntry<'a> {
pub hint: u16,
pub name: &'a str,
}
impl<'a> HintNameTableEntry<'a> {
fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
let offset = &mut offset;
let hint = bytes.gread_with(offset, scroll::LE)?;
let name = bytes.pread::<&'a str>(*offset)?;
Ok(HintNameTableEntry { hint, name })
}
}
#[derive(Debug, Clone)]
pub enum SyntheticImportLookupTableEntry<'a> {
OrdinalNumber(u16),
HintNameTableRVA((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/
}
pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;
impl<'a> SyntheticImportLookupTableEntry<'a> {
pub fn parse<T: Bitfield<'a>>(
bytes: &'a [u8],
offset: usize,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<ImportLookupTable<'a>> {
Self::parse_with_opts::<T>(
bytes,
offset,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}
pub fn parse_with_opts<T: Bitfield<'a>>(
bytes: &'a [u8],
mut offset: usize,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<ImportLookupTable<'a>> {
let le = scroll::LE;
let offset = &mut offset;
let mut table = Vec::new();
loop {
let bitfield: T = bytes.gread_with(offset, le)?;
if bitfield.is_zero() {
debug!("imports done");
break;
} else {
let entry = {
debug!("bitfield {:#x}", bitfield);
use self::SyntheticImportLookupTableEntry::*;
if bitfield.is_ordinal() {
let ordinal = bitfield.to_ordinal();
debug!("importing by ordinal {:#x}", ordinal);
OrdinalNumber(ordinal)
} else {
let rva = bitfield.to_rva();
let hentry = {
debug!("searching for RVA {:#x}", rva);
if let Some(offset) =
utils::find_offset(rva as usize, sections, file_alignment, opts)
{
debug!("offset {:#x}", offset);
HintNameTableEntry::parse(bytes, offset)?
} else {
warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
continue;
}
};
HintNameTableRVA((rva, hentry))
}
};
table.push(entry);
}
}
Ok(table)
}
}
// get until entry is 0
pub type ImportAddressTable = Vec<u64>;
#[repr(C)]
#[derive(Debug, Pread, Pwrite, SizeWith)]
pub struct ImportDirectoryEntry {
pub import_lookup_table_rva: u32,
pub time_date_stamp: u32,
pub forwarder_chain: u32,
pub name_rva: u32,
pub import_address_table_rva: u32,
}
pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;
impl ImportDirectoryEntry {
pub fn is_null(&self) -> bool {
(self.import_lookup_table_rva == 0)
&& (self.time_date_stamp == 0)
&& (self.forwarder_chain == 0)
&& (self.name_rva == 0)
&& (self.import_address_table_rva == 0)
}
}
#[derive(Debug)]
pub struct SyntheticImportDirectoryEntry<'a> {
pub import_directory_entry: ImportDirectoryEntry,
/// Computed
pub name: &'a str,
/// The import lookup table is a vector of either ordinals, or RVAs + import names
pub import_lookup_table: Option<ImportLookupTable<'a>>,
/// Computed
pub import_address_table: ImportAddressTable,
}
impl<'a> SyntheticImportDirectoryEntry<'a> {
pub fn parse<T: Bitfield<'a>>(
bytes: &'a [u8],
import_directory_entry: ImportDirectoryEntry,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
Self::parse_with_opts::<T>(
bytes,
import_directory_entry,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}
pub fn parse_with_opts<T: Bitfield<'a>>(
bytes: &'a [u8],
import_directory_entry: ImportDirectoryEntry,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
const LE: scroll::Endian = scroll::LE;
let name_rva = import_directory_entry.name_rva;
let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment, opts)?;
let import_lookup_table = {
let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
let import_address_table_rva = import_directory_entry.import_address_table_rva;
if let Some(import_lookup_table_offset) = utils::find_offset(
import_lookup_table_rva as usize,
sections,
file_alignment,
opts,
) {
debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
let import_lookup_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>(
bytes,
import_lookup_table_offset,
sections,
file_alignment,
opts,
)?;
debug!(
"Successfully synthesized import lookup table entry from lookup table: {:#?}",
import_lookup_table
);
Some(import_lookup_table)
} else if let Some(import_address_table_offset) = utils::find_offset(
import_address_table_rva as usize,
sections,
file_alignment,
opts,
) {
debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva);
let import_address_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>(
bytes,
import_address_table_offset,
sections,
file_alignment,
opts,
)?;
debug!(
"Successfully synthesized import lookup table entry from IAT: {:#?}",
import_address_table
);
Some(import_address_table)
} else {
None
}
};
let import_address_table_offset = &mut utils::find_offset(
import_directory_entry.import_address_table_rva as usize,
sections,
file_alignment,
opts,
)
.ok_or_else(|| {
error::Error::Malformed(format!(
"Cannot map import_address_table_rva {:#x} into offset for {}",
import_directory_entry.import_address_table_rva, name
))
})?;
let mut import_address_table = Vec::new();
loop {
let import_address = bytes
.gread_with::<T>(import_address_table_offset, LE)?
.into();
if import_address == 0 {
break;
} else {
import_address_table.push(import_address);
}
}
Ok(SyntheticImportDirectoryEntry {
import_directory_entry,
name,
import_lookup_table,
import_address_table,
})
}
}
#[derive(Debug)]
/// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from
pub struct ImportData<'a> {
pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
}
impl<'a> ImportData<'a> {
pub fn parse<T: Bitfield<'a>>(
bytes: &'a [u8],
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<ImportData<'a>> {
Self::parse_with_opts::<T>(
bytes,
dd,
sections,
file_alignment,
&options::ParseOptions::default(),
)
}
pub fn parse_with_opts<T: Bitfield<'a>>(
bytes: &'a [u8],
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<ImportData<'a>> {
let import_directory_table_rva = dd.virtual_address as usize;
debug!(
"import_directory_table_rva {:#x}",
import_directory_table_rva
);
let offset =
&mut utils::find_offset(import_directory_table_rva, sections, file_alignment, opts)
.ok_or_else(|| {
error::Error::Malformed(format!(
"Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset",
import_directory_table_rva
))
})?;
debug!("import data offset {:#x}", offset);
let mut import_data = Vec::new();
loop {
let import_directory_entry: ImportDirectoryEntry =
bytes.gread_with(offset, scroll::LE)?;
debug!("{:#?}", import_directory_entry);
if import_directory_entry.is_null() {
break;
} else {
let entry = SyntheticImportDirectoryEntry::parse_with_opts::<T>(
bytes,
import_directory_entry,
sections,
file_alignment,
opts,
)?;
debug!("entry {:#?}", entry);
import_data.push(entry);
}
}
debug!("finished ImportData");
Ok(ImportData { import_data })
}
}
#[derive(Debug)]
/// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to
pub struct Import<'a> {
pub name: Cow<'a, str>,
pub dll: &'a str,
pub ordinal: u16,
pub offset: usize,
pub rva: usize,
pub size: usize,
}
impl<'a> Import<'a> {
pub fn parse<T: Bitfield<'a>>(
_bytes: &'a [u8],
import_data: &ImportData<'a>,
_sections: &[section_table::SectionTable],
) -> error::Result<Vec<Import<'a>>> {
let mut imports = Vec::new();
for data in &import_data.import_data {
if let Some(ref import_lookup_table) = data.import_lookup_table {
let dll = data.name;
let import_base = data.import_directory_entry.import_address_table_rva as usize;
debug!("Getting imports from {}", &dll);
for (i, entry) in import_lookup_table.iter().enumerate() {
let offset = import_base + (i * T::size_of());
use self::SyntheticImportLookupTableEntry::*;
let (rva, name, ordinal) = match *entry {
HintNameTableRVA((rva, ref hint_entry)) => {
// if hint_entry.name = "" && hint_entry.hint = 0 {
// println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva);
// }
(rva, Cow::Borrowed(hint_entry.name), hint_entry.hint)
}
OrdinalNumber(ordinal) => {
let name = format!("ORDINAL {}", ordinal);
(0x0, Cow::Owned(name), ordinal)
}
};
let import = Import {
name,
ordinal,
dll,
size: T::size_of(),
offset,
rva: rva as usize,
};
imports.push(import);
}
}
}
Ok(imports)
}
}