From aae203584aa4dd30c0705502463a1e3d83642569 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Mon, 20 Sep 2021 15:55:16 +1000 Subject: [PATCH] read/pe: add support for base relocations --- crates/examples/src/readobj/pe.rs | 94 ++++++--- .../testfiles/pe/base-gnu.exe.readobj | 196 ++++++++++++++++++ .../{base.exe.readobj => base.exe.readobj.0} | 0 .../testfiles/pe/base.exe.readobj.reloc | 8 + src/pe.rs | 5 +- src/read/pe/data_directory.rs | 34 ++- src/read/pe/mod.rs | 3 + src/read/pe/relocation.rs | 90 ++++++++ 8 files changed, 401 insertions(+), 29 deletions(-) rename crates/examples/testfiles/pe/{base.exe.readobj => base.exe.readobj.0} (100%) create mode 100644 crates/examples/testfiles/pe/base.exe.readobj.reloc create mode 100644 src/read/pe/relocation.rs diff --git a/crates/examples/src/readobj/pe.rs b/crates/examples/src/readobj/pe.rs index d9613cba..18db8454 100644 --- a/crates/examples/src/readobj/pe.rs +++ b/crates/examples/src/readobj/pe.rs @@ -56,6 +56,7 @@ fn print_pe(p: &mut Printer<'_>, data: &[u8]) { p.field_hex("Signature", nt_headers.signature()); }); let header = nt_headers.file_header(); + let machine = header.machine.get(LE); let sections = header.sections(data, offset).print_err(p); let symbols = header.symbols(data).print_err(p); print_file(p, header); @@ -68,27 +69,15 @@ fn print_pe(p: &mut Printer<'_>, data: &[u8]) { }); } if let Some(ref sections) = sections { - print_sections(p, data, header.machine.get(LE), symbols.as_ref(), sections); + print_sections(p, data, machine, 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() { - if dir.virtual_address.get(LE) == 0 { - continue; - } - match index { - IMAGE_DIRECTORY_ENTRY_EXPORT => { - print_export_dir(p, data, §ions, dir); - } - IMAGE_DIRECTORY_ENTRY_IMPORT => { - print_import_dir::(p, data, §ions, dir); - } - // TODO - _ => {} - } - } + print_export_dir(p, data, §ions, &data_directories); + print_import_dir::(p, data, §ions, &data_directories); + print_reloc_dir(p, data, machine, §ions, &data_directories); } } } @@ -164,10 +153,11 @@ fn print_export_dir( p: &mut Printer<'_>, data: &[u8], sections: &SectionTable, - dir: &ImageDataDirectory, + data_directories: &DataDirectories, ) -> Option<()> { - let dir_data = dir.data(data, sections).print_err(p)?; - let export_dir = ExportTable::parse_directory(data).print_err(p)?; + let export_dir = data_directories + .export_directory(data, sections) + .print_err(p)??; p.group("ImageExportDirectory", |p| { p.field_hex("Characteristics", export_dir.characteristics.get(LE)); p.field_hex("TimeDateStamp", export_dir.time_date_stamp.get(LE)); @@ -186,8 +176,7 @@ fn print_export_dir( "AddressOfNameOrdinals", export_dir.address_of_name_ordinals.get(LE), ); - if let Some(export_table) = - ExportTable::parse(dir_data, dir.virtual_address.get(LE)).print_err(p) + if let Some(Some(export_table)) = data_directories.export_table(data, sections).print_err(p) { // TODO: the order of the name pointers might be interesting? let mut names = vec![None; export_table.addresses().len()]; @@ -240,11 +229,11 @@ fn print_import_dir( p: &mut Printer<'_>, data: &[u8], sections: &SectionTable, - dir: &ImageDataDirectory, + data_directories: &DataDirectories, ) -> 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 import_table = data_directories + .import_table(data, sections) + .print_err(p)??; let mut import_descs = import_table.descriptors().print_err(p)?; p.group("ImageImportDirectory", |p| { while let Some(Some(import_desc)) = import_descs.next().print_err(p) { @@ -454,6 +443,38 @@ fn print_symbols(p: &mut Printer<'_>, sections: Option<&SectionTable>, symbols: } } +fn print_reloc_dir( + p: &mut Printer<'_>, + data: &[u8], + machine: u16, + sections: &SectionTable, + data_directories: &DataDirectories, +) -> Option<()> { + let proc = match machine { + IMAGE_FILE_MACHINE_IA64 => FLAGS_IMAGE_REL_IA64_BASED, + IMAGE_FILE_MACHINE_MIPS16 | IMAGE_FILE_MACHINE_MIPSFPU | IMAGE_FILE_MACHINE_MIPSFPU16 => { + FLAGS_IMAGE_REL_MIPS_BASED + } + IMAGE_FILE_MACHINE_ARM => FLAGS_IMAGE_REL_ARM_BASED, + IMAGE_FILE_MACHINE_RISCV32 | IMAGE_FILE_MACHINE_RISCV64 | IMAGE_FILE_MACHINE_RISCV128 => { + FLAGS_IMAGE_REL_RISCV_BASED + } + _ => &[], + }; + let mut blocks = data_directories + .relocation_blocks(data, sections) + .print_err(p)??; + while let Some(block) = blocks.next().print_err(p)? { + for reloc in block { + p.group("ImageBaseRelocation", |p| { + p.field_hex("VirtualAddress", reloc.virtual_address); + p.field_enums("Type", reloc.typ, &[proc, FLAGS_IMAGE_REL_BASED]); + }); + } + } + Some(()) +} + static FLAGS_IMAGE_FILE: &[Flag] = &flags!( IMAGE_FILE_RELOCS_STRIPPED, IMAGE_FILE_EXECUTABLE_IMAGE, @@ -930,3 +951,26 @@ static FLAGS_IMAGE_DIRECTORY_ENTRY: &[Flag] = &flags!( IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, ); +static FLAGS_IMAGE_REL_BASED: &[Flag] = &flags!( + IMAGE_REL_BASED_ABSOLUTE, + IMAGE_REL_BASED_HIGH, + IMAGE_REL_BASED_LOW, + IMAGE_REL_BASED_HIGHLOW, + IMAGE_REL_BASED_HIGHADJ, + IMAGE_REL_BASED_MACHINE_SPECIFIC_5, + IMAGE_REL_BASED_RESERVED, + IMAGE_REL_BASED_MACHINE_SPECIFIC_7, + IMAGE_REL_BASED_MACHINE_SPECIFIC_8, + IMAGE_REL_BASED_MACHINE_SPECIFIC_9, + IMAGE_REL_BASED_DIR64, +); +static FLAGS_IMAGE_REL_IA64_BASED: &[Flag] = &flags!(IMAGE_REL_BASED_IA64_IMM64,); +static FLAGS_IMAGE_REL_MIPS_BASED: &[Flag] = + &flags!(IMAGE_REL_BASED_MIPS_JMPADDR, IMAGE_REL_BASED_MIPS_JMPADDR16,); +static FLAGS_IMAGE_REL_ARM_BASED: &[Flag] = + &flags!(IMAGE_REL_BASED_ARM_MOV32, IMAGE_REL_BASED_THUMB_MOV32,); +static FLAGS_IMAGE_REL_RISCV_BASED: &[Flag] = &flags!( + IMAGE_REL_BASED_RISCV_HIGH20, + IMAGE_REL_BASED_RISCV_LOW12I, + IMAGE_REL_BASED_RISCV_LOW12S, +); diff --git a/crates/examples/testfiles/pe/base-gnu.exe.readobj b/crates/examples/testfiles/pe/base-gnu.exe.readobj index 5a8b182a..f34e3501 100644 --- a/crates/examples/testfiles/pe/base-gnu.exe.readobj +++ b/crates/examples/testfiles/pe/base-gnu.exe.readobj @@ -15441,3 +15441,199 @@ ImageImportDirectory { } } } +ImageBaseRelocation { + VirtualAddress: 0x7BD8 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x8010 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x8080 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x8090 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80A0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80A8 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80B0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80B8 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80C0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80D0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x80E0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9020 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9040 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9048 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9050 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9058 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9680 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9690 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x96A0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x96B0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x96C0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x96D0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x96E0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x96F0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9700 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9710 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9720 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9730 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9740 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9750 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9760 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9770 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9780 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9790 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x97A0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x97B0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x97C0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x97D0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x97E0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x97F0 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9800 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9810 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9820 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9830 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0x9840 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0xE008 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0xE020 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0xE038 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} +ImageBaseRelocation { + VirtualAddress: 0xE040 + Type: IMAGE_REL_BASED_DIR64 (0xA) +} diff --git a/crates/examples/testfiles/pe/base.exe.readobj b/crates/examples/testfiles/pe/base.exe.readobj.0 similarity index 100% rename from crates/examples/testfiles/pe/base.exe.readobj rename to crates/examples/testfiles/pe/base.exe.readobj.0 diff --git a/crates/examples/testfiles/pe/base.exe.readobj.reloc b/crates/examples/testfiles/pe/base.exe.readobj.reloc new file mode 100644 index 00000000..f1501ea3 --- /dev/null +++ b/crates/examples/testfiles/pe/base.exe.readobj.reloc @@ -0,0 +1,8 @@ +ImageBaseRelocation { + VirtualAddress: 0x1004 + Type: IMAGE_REL_BASED_HIGHLOW (0x3) +} +ImageBaseRelocation { + VirtualAddress: 0x1024 + Type: IMAGE_REL_BASED_HIGHLOW (0x3) +} diff --git a/src/pe.rs b/src/pe.rs index e68e1ae5..c075a6d6 100644 --- a/src/pe.rs +++ b/src/pe.rs @@ -1715,12 +1715,11 @@ pub struct ImageLinenumber { // Based relocation format. // -// This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageBaseRelocation { - pub virtual_address: U32Bytes, - pub size_of_block: U32Bytes, + pub virtual_address: U32, + pub size_of_block: U32, // pub type_offset[1]: U16, } diff --git a/src/read/pe/data_directory.rs b/src/read/pe/data_directory.rs index a236eb44..b7d134a3 100644 --- a/src/read/pe/data_directory.rs +++ b/src/read/pe/data_directory.rs @@ -3,7 +3,7 @@ use core::slice; use crate::read::{ReadError, ReadRef, Result}; use crate::{pe, LittleEndian as LE}; -use super::{ExportTable, ImportTable, SectionTable}; +use super::{ExportTable, ImportTable, RelocationBlockIterator, SectionTable}; /// The table of data directories in a PE file. #[derive(Debug, Clone, Copy)] @@ -42,6 +42,22 @@ impl<'data> DataDirectories<'data> { .filter(|d| d.virtual_address.get(LE) != 0) } + /// Returns the unparsed export directory. + /// + /// `data` must be the entire file data. + pub fn export_directory>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let export_data = data_dir.data(data, sections)?; + ExportTable::parse_directory(export_data).map(Some) + } + /// Returns the partially parsed export directory. /// /// `data` must be the entire file data. @@ -77,6 +93,22 @@ impl<'data> DataDirectories<'data> { .read_error("Invalid import data dir virtual address")?; Ok(Some(ImportTable::new(section_data, section_va, import_va))) } + + /// Returns the blocks in the base relocation directory. + /// + /// `data` must be the entire file data. + pub fn relocation_blocks>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let reloc_data = data_dir.data(data, sections)?; + Ok(Some(RelocationBlockIterator::new(reloc_data))) + } } impl pe::ImageDataDirectory { diff --git a/src/read/pe/mod.rs b/src/read/pe/mod.rs index 97c0901a..36718492 100644 --- a/src/read/pe/mod.rs +++ b/src/read/pe/mod.rs @@ -22,6 +22,9 @@ pub use export::*; mod import; pub use import::*; +mod relocation; +pub use relocation::*; + mod rich; pub use rich::*; diff --git a/src/read/pe/relocation.rs b/src/read/pe/relocation.rs new file mode 100644 index 00000000..06215bd1 --- /dev/null +++ b/src/read/pe/relocation.rs @@ -0,0 +1,90 @@ +use core::slice; + +use crate::endian::{LittleEndian as LE, U16}; +use crate::pe; +use crate::read::{Bytes, Error, ReadError, Result}; + +/// An iterator over the relocation blocks in the `.reloc` section of a PE file. +#[derive(Debug, Default, Clone, Copy)] +pub struct RelocationBlockIterator<'data> { + data: Bytes<'data>, +} + +impl<'data> RelocationBlockIterator<'data> { + /// Construct a new iterator from the data of the `.reloc` section. + pub fn new(data: &'data [u8]) -> Self { + RelocationBlockIterator { data: Bytes(data) } + } + + /// Read the next relocation page. + pub fn next(&mut self) -> Result>> { + if self.data.is_empty() { + return Ok(None); + } + let header = self + .data + .read::() + .read_error("Invalid PE reloc section size")?; + let virtual_address = header.virtual_address.get(LE); + let size = header.size_of_block.get(LE); + if size <= 8 || size & 3 != 0 { + return Err(Error("Invalid PE reloc block size")); + } + let count = (size - 8) / 2; + let relocs = self + .data + .read_slice::>(count as usize) + .read_error("Invalid PE reloc block size")? + .iter(); + Ok(Some(RelocationIterator { + virtual_address, + size, + relocs, + })) + } +} + +/// An iterator of the relocations in a block in the `.reloc` section of a PE file. +#[derive(Debug, Clone)] +pub struct RelocationIterator<'data> { + virtual_address: u32, + size: u32, + relocs: slice::Iter<'data, U16>, +} + +impl<'data> RelocationIterator<'data> { + /// Return the virtual address of the page that this block of relocations applies to. + pub fn virtual_address(&self) -> u32 { + self.virtual_address + } + + /// Return the size in bytes of this block of relocations. + pub fn size(&self) -> u32 { + self.size + } +} + +impl<'data> Iterator for RelocationIterator<'data> { + type Item = Relocation; + + fn next(&mut self) -> Option { + loop { + let reloc = self.relocs.next()?.get(LE); + if reloc != 0 { + return Some(Relocation { + virtual_address: self.virtual_address.wrapping_add((reloc & 0xfff) as u32), + typ: reloc >> 12, + }); + } + } + } +} + +/// A relocation in the `.reloc` section of a PE file. +#[derive(Debug, Default, Clone, Copy)] +pub struct Relocation { + /// The virtual address of the relocation. + pub virtual_address: u32, + /// One of the `pe::IMAGE_REL_BASED_*` constants. + pub typ: u16, +}