Skip to content

Commit

Permalink
read/pe: add DataDirectories and ImportTable
Browse files Browse the repository at this point in the history
  • Loading branch information
philipc committed Aug 19, 2021
1 parent dd3a938 commit 0661c3d
Show file tree
Hide file tree
Showing 9 changed files with 557 additions and 193 deletions.
201 changes: 131 additions & 70 deletions examples/readobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4579,24 +4579,28 @@ mod pe {
p.field_enum("Index", index, FLAGS_IMAGE_DIRECTORY_ENTRY);
p.field_hex("VirtualAddress", dir.virtual_address.get(LE));
p.field_hex("Size", dir.size.get(LE));
if let Some(dir_data) = sections
.as_ref()
.and_then(|sections| dir.data(data, sections).ok())
{
match index {
IMAGE_DIRECTORY_ENTRY_EXPORT => print_export_dir(p, dir, dir_data),
// TODO
_ => {}
}
}
});
}
if let Some(ref sections) = sections {
print_sections(p, data, header.machine.get(LE), symbols.as_ref(), &sections);
print_sections(p, data, header.machine.get(LE), symbols.as_ref(), sections);
}
if let Some(ref symbols) = symbols {
print_symbols(p, sections.as_ref(), &symbols);
}
if let Some(ref sections) = sections {
for (index, dir) in data_directories.iter().enumerate() {
match index {
IMAGE_DIRECTORY_ENTRY_EXPORT => {
print_export_dir(p, data, &sections, dir);
}
IMAGE_DIRECTORY_ENTRY_IMPORT => {
print_import_dir::<Pe, _>(p, data, &sections, dir);
}
// TODO
_ => {}
}
}
}
}
}
}
Expand Down Expand Up @@ -4667,70 +4671,127 @@ mod pe {
});
}

fn print_export_dir(p: &mut Printer<impl Write>, dir: &ImageDataDirectory, dir_data: &[u8]) {
if let Ok((export_dir, _)) = object::from_bytes::<pe::ImageExportDirectory>(dir_data) {
p.group("ImageExportDirectory", |p| {
p.field_hex("Characteristics", export_dir.characteristics.get(LE));
p.field_hex("TimeDateStamp", export_dir.time_date_stamp.get(LE));
p.field("MajorVersion", export_dir.major_version.get(LE));
p.field("MinorVersion", export_dir.minor_version.get(LE));
p.field_hex("Name", export_dir.name.get(LE));
p.field("Base", export_dir.base.get(LE));
p.field("NumberOfFunctions", export_dir.number_of_functions.get(LE));
p.field("NumberOfNames", export_dir.number_of_names.get(LE));
p.field_hex(
"AddressOfFunctions",
export_dir.address_of_functions.get(LE),
);
p.field_hex("AddressOfNames", export_dir.address_of_names.get(LE));
p.field_hex(
"AddressOfNameOrdinals",
export_dir.address_of_name_ordinals.get(LE),
);
if let Ok(export_table) = ExportTable::parse(dir_data, dir.virtual_address.get(LE))
{
// TODO: the order of the name pointers might be interesting?
let mut names = vec![None; export_table.addresses().len()];
for (name_pointer, ordinal) in export_table.name_iter() {
if let Some(name) = names.get_mut(ordinal as usize) {
*name = Some(name_pointer);
}
fn print_export_dir(
p: &mut Printer<impl Write>,
data: &[u8],
sections: &SectionTable,
dir: &ImageDataDirectory,
) -> Option<()> {
let dir_data = dir.data(data, sections).ok()?;
let export_dir = object::from_bytes::<pe::ImageExportDirectory>(dir_data)
.ok()?
.0;
p.group("ImageExportDirectory", |p| {
p.field_hex("Characteristics", export_dir.characteristics.get(LE));
p.field_hex("TimeDateStamp", export_dir.time_date_stamp.get(LE));
p.field("MajorVersion", export_dir.major_version.get(LE));
p.field("MinorVersion", export_dir.minor_version.get(LE));
p.field_hex("Name", export_dir.name.get(LE));
p.field("Base", export_dir.base.get(LE));
p.field("NumberOfFunctions", export_dir.number_of_functions.get(LE));
p.field("NumberOfNames", export_dir.number_of_names.get(LE));
p.field_hex(
"AddressOfFunctions",
export_dir.address_of_functions.get(LE),
);
p.field_hex("AddressOfNames", export_dir.address_of_names.get(LE));
p.field_hex(
"AddressOfNameOrdinals",
export_dir.address_of_name_ordinals.get(LE),
);
if let Ok(export_table) = ExportTable::parse(dir_data, dir.virtual_address.get(LE)) {
// TODO: the order of the name pointers might be interesting?
let mut names = vec![None; export_table.addresses().len()];
for (name_pointer, ordinal) in export_table.name_iter() {
if let Some(name) = names.get_mut(ordinal as usize) {
*name = Some(name_pointer);
}
}

let ordinal_base = export_table.ordinal_base();
for (ordinal, address) in export_table.addresses().iter().enumerate() {
p.group("Export", |p| {
p.field("Ordinal", ordinal_base.wrapping_add(ordinal as u32));
if let Some(name_pointer) = names[ordinal] {
p.field_string(
"Name",
name_pointer,
export_table.name_from_pointer(name_pointer).ok(),
);
}
p.field_hex("Address", address.get(LE));
if let Ok(target) = export_table.target_from_address(address.get(LE)) {
match target {
ExportTarget::Address(_) => {}
ExportTarget::ForwardByOrdinal(library, ordinal) => {
p.field_inline_string("ForwardLibrary", library);
p.field("ForwardOrdinal", ordinal);
}
ExportTarget::ForwardByName(library, name) => {
p.field_inline_string("ForwardLibrary", library);
p.field_inline_string("ForwardName", name);
}
let ordinal_base = export_table.ordinal_base();
for (ordinal, address) in export_table.addresses().iter().enumerate() {
p.group("Export", |p| {
p.field("Ordinal", ordinal_base.wrapping_add(ordinal as u32));
if let Some(name_pointer) = names[ordinal] {
p.field_string(
"Name",
name_pointer,
export_table.name_from_pointer(name_pointer).ok(),
);
}
p.field_hex("Address", address.get(LE));
if let Ok(target) = export_table.target_from_address(address.get(LE)) {
match target {
ExportTarget::Address(_) => {}
ExportTarget::ForwardByOrdinal(library, ordinal) => {
p.field_inline_string("ForwardLibrary", library);
p.field("ForwardOrdinal", ordinal);
}
ExportTarget::ForwardByName(library, name) => {
p.field_inline_string("ForwardLibrary", library);
p.field_inline_string("ForwardName", name);
}
} else if let Ok(Some(forward)) =
export_table.forward_string(address.get(LE))
{
p.field_inline_string("Forward", forward);
}
});
}
} else if let Ok(Some(forward)) =
export_table.forward_string(address.get(LE))
{
p.field_inline_string("Forward", forward);
}
});
}
});
}
}
});
Some(())
}

fn print_import_dir<Pe: ImageNtHeaders, W: Write>(
p: &mut Printer<W>,
data: &[u8],
sections: &SectionTable,
dir: &ImageDataDirectory,
) -> Option<()> {
let import_address = dir.virtual_address.get(LE);
let (section_data, section_address) = sections.pe_data_containing(data, import_address)?;
let import_table = ImportTable::new(section_data, section_address, import_address);
let mut import_descs = import_table.descriptors().ok()?;
p.group("ImageImportDirectory", |p| {
while let Ok(Some(import_desc)) = import_descs.next() {
p.group("ImageImportDescriptor", |p| {
p.field_hex("LookupTable", import_desc.original_first_thunk.get(LE));
p.field_hex("TimeDataStamp", import_desc.time_date_stamp.get(LE));
p.field_hex("ForwarderChain", import_desc.forwarder_chain.get(LE));
let name = import_desc.name.get(LE);
p.field_string("Name", name, import_table.name(name).ok());
p.field_hex("AddressTable", import_desc.first_thunk.get(LE));
let mut address_thunks =
import_table.thunks(import_desc.first_thunk.get(LE)).ok();
if let Ok(mut lookup_thunks) =
import_table.thunks(import_desc.original_first_thunk.get(LE))
{
while let Ok(Some(thunk)) = lookup_thunks.next::<Pe>() {
p.group("Thunk", |p| {
p.field_hex("Lookup", thunk.raw());
if let Some(Some(thunk)) = address_thunks
.as_mut()
.and_then(|thunks| thunks.next::<Pe>().ok())
{
p.field_hex("Address", thunk.raw());
}
if thunk.is_ordinal() {
p.field("Ordinal", thunk.ordinal());
} else if let Ok((hint, name)) =
import_table.hint_name(thunk.address())
{
p.field("Hint", hint);
p.field_inline_string("Name", name);
}
});
}
}
});
}
});
Some(())
}

fn print_sections(
Expand Down
2 changes: 1 addition & 1 deletion src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2576,7 +2576,7 @@ pub const OHW_R4KEOP: u32 = 0x1;
pub const OHW_R8KPFETCH: u32 = 0x2;
/// R5000 end-of-page patch.
pub const OHW_R5KEOP: u32 = 0x4;
/// R5000 cvt.[ds].l bug. clean=1.
/// R5000 cvt.\[ds\].l bug. clean=1.
pub const OHW_R5KCVTL: u32 = 0x8;

#[allow(missing_docs)]
Expand Down
15 changes: 7 additions & 8 deletions src/pe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1813,12 +1813,10 @@ pub struct ImageImportByName {
//pub name: [i8; 1],
}

/*
// TODO? unions
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct ImageThunkData64 {
pub struct ImageThunkData64(pub U64<LE>);
/*
union {
/// PBYTE
pub forwarder_string: U64<LE>,
Expand All @@ -1828,11 +1826,12 @@ pub struct ImageThunkData64 {
/// PIMAGE_IMPORT_BY_NAME
pub address_of_data: U64<LE>,
} u1;
}
*/

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct ImageThunkData32 {
pub struct ImageThunkData32(pub U32<LE>);
/*
union {
/// PBYTE
pub forwarder_string: U32<LE>,
Expand Down Expand Up @@ -2905,8 +2904,8 @@ unsafe_impl_pod!(
ImageArchiveMemberHeader,
ImageExportDirectory,
ImageImportByName,
//ImageThunkData64,
//ImageThunkData32,
ImageThunkData64,
ImageThunkData32,
ImageTlsDirectory64,
ImageTlsDirectory32,
ImageImportDescriptor,
Expand Down
2 changes: 1 addition & 1 deletion src/read/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,9 @@ impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
/// An imported symbol.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Import<'data> {
library: ByteString<'data>,
// TODO: or ordinal
name: ByteString<'data>,
library: ByteString<'data>,
}

impl<'data> Import<'data> {
Expand Down
102 changes: 102 additions & 0 deletions src/read/pe/data_directory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use core::slice;

use crate::read::{ReadError, ReadRef, Result};
use crate::{pe, LittleEndian as LE};

use super::{ExportTable, ImportTable, SectionTable};

/// The table of data directories in a PE file.
#[derive(Debug, Clone, Copy)]
pub struct DataDirectories<'data> {
entries: &'data [pe::ImageDataDirectory],
}

impl<'data> DataDirectories<'data> {
/// Parse the data directory table.
///
/// `data` must be the remaining optional data following the
/// [optional header](pe::ImageOptionalHeader64). `number` must be from the
/// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes)
/// field of the optional header.
pub fn parse(data: &'data [u8], number: u32) -> Result<Self> {
let entries = data
.read_slice_at(0, number as usize)
.read_error("Invalid PE number of RVA and sizes")?;
Ok(DataDirectories { entries })
}

/// Iterator over the data directories.
pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> {
self.entries.iter()
}

/// Returns the data directory at the given index.
///
/// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants.
///
/// Returns `None` if the index is larger than the table size,
/// or if the entry at the index has a zero virtual address.
pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> {
self.entries
.get(index)
.filter(|d| d.virtual_address.get(LE) != 0)
}

/// Returns the partially parsed export directory.
///
/// `data` must be the entire file data.
pub fn export_table<R: ReadRef<'data>>(
&self,
data: R,
sections: &SectionTable<'data>,
) -> Result<Option<ExportTable<'data>>> {
let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) {
Some(data_dir) => data_dir,
None => return Ok(None),
};
let export_va = data_dir.virtual_address.get(LE);
let export_data = data_dir.data(data, sections)?;
ExportTable::parse(export_data, export_va).map(Some)
}

/// Returns the partially parsed import directory.
///
/// `data` must be the entire file data.
pub fn import_table<R: ReadRef<'data>>(
&self,
data: R,
sections: &SectionTable<'data>,
) -> Result<Option<ImportTable<'data>>> {
let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT) {
Some(data_dir) => data_dir,
None => return Ok(None),
};
let import_va = data_dir.virtual_address.get(LE);
let (section_data, section_va) = sections
.pe_data_containing(data, import_va)
.read_error("Invalid import data dir virtual address")?;
Ok(Some(ImportTable::new(section_data, section_va, import_va)))
}
}

impl pe::ImageDataDirectory {
/// Get the data referenced by this directory entry.
///
/// This function has some limitations:
/// - It requires that the data is contained in a single section.
/// - It uses the size field of the directory entry, which is
/// not desirable for all data directories.
/// - It uses the `virtual_address` of the directory entry as an address,
/// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`.
pub fn data<'data, R: ReadRef<'data>>(
&self,
data: R,
sections: &SectionTable<'data>,
) -> Result<&'data [u8]> {
sections
.pe_data_at(data, self.virtual_address.get(LE))
.read_error("Invalid data dir virtual address")?
.get(..self.size.get(LE) as usize)
.read_error("Invalid data dir size")
}
}

0 comments on commit 0661c3d

Please sign in to comment.