Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

read/pe: add DataDirectories and ImportTable #357

Merged
merged 1 commit into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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")
}
}