diff --git a/src/pe/debug.rs b/src/pe/debug.rs index 3455f57e8..d4830c23c 100644 --- a/src/pe/debug.rs +++ b/src/pe/debug.rs @@ -12,8 +12,8 @@ pub struct DebugData<'a> { } impl<'a> DebugData<'a> { - pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result { - let image_debug_directory = ImageDebugDirectory::parse(bytes, dd, sections)?; + pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result { + let image_debug_directory = ImageDebugDirectory::parse(bytes, dd, sections, file_alignment)?; let codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse(bytes, &image_debug_directory)?; Ok(DebugData{ @@ -54,9 +54,9 @@ pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6; pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9; impl ImageDebugDirectory { - fn parse(bytes: &[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result { + fn parse(bytes: &[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result { let rva = dd.virtual_address as usize; - let offset = utils::find_offset(rva, sections).ok_or(error::Error::Malformed(format!("Cannot map ImageDebugDirectory rva {:#x} into offset", rva)))?;; + let offset = utils::find_offset(rva, sections, file_alignment).ok_or(error::Error::Malformed(format!("Cannot map ImageDebugDirectory rva {:#x} into offset", rva)))?;; let idd: Self = bytes.pread_with(offset, scroll::LE)?; Ok (idd) } diff --git a/src/pe/export.rs b/src/pe/export.rs index d62dcf18c..06c0adecf 100644 --- a/src/pe/export.rs +++ b/src/pe/export.rs @@ -64,17 +64,17 @@ pub struct ExportData<'a> { } impl<'a> ExportData<'a> { - pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result> { + pub fn parse(bytes: &'a [u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { let export_rva = dd.virtual_address as usize; let size = dd.size as usize; debug!("export_rva {:#x} size {:#}", export_rva, size); - let export_offset = utils::find_offset_or(export_rva, sections, &format!("cannot map export_rva ({:#x}) into offset", export_rva))?; + let export_offset = utils::find_offset_or(export_rva, sections, file_alignment, &format!("cannot map export_rva ({:#x}) into offset", export_rva))?; let export_directory_table = ExportDirectoryTable::parse(bytes, export_offset) .map_err(|_| error::Error::Malformed(format!("cannot parse export_directory_table (offset {:#x})", export_offset)))?; let number_of_name_pointers = export_directory_table.number_of_name_pointers as usize; let address_table_entries = export_directory_table.address_table_entries as usize; - let export_name_pointer_table = utils::find_offset(export_directory_table.name_pointer_rva as usize, sections).map_or(vec![], |table_offset| { + let export_name_pointer_table = utils::find_offset(export_directory_table.name_pointer_rva as usize, sections, file_alignment).map_or(vec![], |table_offset| { let mut offset = table_offset; let mut table: ExportNamePointerTable = Vec::with_capacity(number_of_name_pointers); @@ -89,7 +89,7 @@ impl<'a> ExportData<'a> { table }); - let export_ordinal_table = utils::find_offset(export_directory_table.ordinal_table_rva as usize, sections).map_or(vec![], |table_offset| { + let export_ordinal_table = utils::find_offset(export_directory_table.ordinal_table_rva as usize, sections, file_alignment).map_or(vec![], |table_offset| { let mut offset = table_offset; let mut table: ExportOrdinalTable = Vec::with_capacity(number_of_name_pointers); @@ -104,7 +104,7 @@ impl<'a> ExportData<'a> { table }); - let export_address_table = utils::find_offset(export_directory_table.export_address_table_rva as usize, sections).map_or(vec![], |table_offset| { + let export_address_table = utils::find_offset(export_directory_table.export_address_table_rva as usize, sections, file_alignment).map_or(vec![], |table_offset| { let mut offset = table_offset; let mut table: ExportAddressTable = Vec::with_capacity(address_table_entries); let export_end = export_rva + size; @@ -124,7 +124,7 @@ impl<'a> ExportData<'a> { table }); - let name = utils::find_offset(export_directory_table.name_rva as usize, sections).and_then(|offset| bytes.pread(offset).ok()); + let name = utils::find_offset(export_directory_table.name_rva as usize, sections, file_alignment).and_then(|offset| bytes.pread(offset).ok()); Ok(ExportData { name: name, @@ -209,6 +209,7 @@ struct ExportCtx<'a> { pub ptr: u32, pub idx: usize, pub sections: &'a [section_table::SectionTable], + pub file_alignment: u32, pub addresses: &'a ExportAddressTable, pub ordinals: &'a ExportOrdinalTable, } @@ -217,23 +218,23 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> { type Error = error::Error; type Size = usize; #[inline] - fn try_from_ctx(bytes: &'a [u8], ExportCtx { ptr, idx, sections, addresses, ordinals }: ExportCtx<'b>) -> Result<(Self, Self::Size), Self::Error> { + fn try_from_ctx(bytes: &'a [u8], ExportCtx { ptr, idx, sections, file_alignment, addresses, ordinals }: ExportCtx<'b>) -> Result<(Self, Self::Size), Self::Error> { use self::ExportAddressTableEntry::*; - let name = utils::find_offset(ptr as usize, sections).map_or(None, |offset| bytes.pread::<&str>(offset).ok()); + let name = utils::find_offset(ptr as usize, sections, file_alignment).map_or(None, |offset| bytes.pread::<&str>(offset).ok()); if let Some(ordinal) = ordinals.get(idx) { if let Some(rva) = addresses.get(*ordinal as usize) { match *rva { ExportRVA(rva) => { let rva = rva as usize; - let offset = utils::find_offset_or(rva, sections, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?; + let offset = utils::find_offset_or(rva, sections, file_alignment, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?; Ok((Export { name, offset, rva, reexport: None, size: 0 }, 0)) }, ForwarderRVA(rva) => { let rva = rva as usize; - let offset = utils::find_offset_or(rva, sections, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?; + let offset = utils::find_offset_or(rva, sections, file_alignment, &format!("cannot map RVA ({:#x}) of export ordinal {} into offset", rva, ordinal))?; let reexport = Reexport::parse(bytes, offset)?; Ok((Export { name, offset, rva, reexport: Some(reexport), size: 0 }, 0)) } @@ -248,14 +249,14 @@ impl<'a, 'b> scroll::ctx::TryFromCtx<'a, ExportCtx<'b>> for Export<'a> { } impl<'a> Export<'a> { - pub fn parse(bytes: &'a [u8], export_data: &ExportData, sections: &[section_table::SectionTable]) -> error::Result>> { + pub fn parse(bytes: &'a [u8], export_data: &ExportData, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result>> { let pointers = &export_data.export_name_pointer_table; let addresses = &export_data.export_address_table; let ordinals = &export_data.export_ordinal_table; let mut exports = Vec::with_capacity(pointers.len()); for (idx, &ptr) in pointers.iter().enumerate() { - if let Ok(export) = bytes.pread_with(0, ExportCtx { ptr, idx, sections, addresses, ordinals }) { + if let Ok(export) = bytes.pread_with(0, ExportCtx { ptr, idx, sections, file_alignment, addresses, ordinals }) { exports.push(export); } } diff --git a/src/pe/import.rs b/src/pe/import.rs index 82cdef8e2..afee0dc3b 100644 --- a/src/pe/import.rs +++ b/src/pe/import.rs @@ -64,8 +64,7 @@ pub enum SyntheticImportLookupTableEntry<'a> { pub type ImportLookupTable<'a> = Vec>; impl<'a> SyntheticImportLookupTableEntry<'a> { - pub fn parse>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable]) - -> error::Result> { + pub fn parse>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { let le = scroll::LE; let offset = &mut offset; let mut table = Vec::new(); @@ -86,7 +85,7 @@ impl<'a> SyntheticImportLookupTableEntry<'a> { let rva = bitfield.to_rva(); let hentry = { debug!("searching for RVA {:#x}", rva); - if let Some(offset) = utils::find_offset(rva as usize, sections) { + if let Some(offset) = utils::find_offset(rva as usize, sections, file_alignment) { debug!("offset {:#x}", offset); HintNameTableEntry::parse(bytes, offset)? } else { @@ -142,15 +141,15 @@ pub struct SyntheticImportDirectoryEntry<'a> { } impl<'a> SyntheticImportDirectoryEntry<'a> { - pub fn parse>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable]) -> error::Result> { + pub fn parse>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { 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)?; + let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment)?; let import_lookup_table = { let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva; debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva); - if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections) { - let import_lookup_table = SyntheticImportLookupTableEntry::parse::(bytes, import_lookup_table_offset, sections)?; + if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections, file_alignment) { + let import_lookup_table = SyntheticImportLookupTableEntry::parse::(bytes, import_lookup_table_offset, sections, file_alignment)?; debug!("Successfully synthesized import lookup table entry: {:#?}", import_lookup_table); Some(import_lookup_table) } else { @@ -158,7 +157,7 @@ impl<'a> SyntheticImportDirectoryEntry<'a> { } }; - let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections).ok_or(error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?; + let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections, file_alignment).ok_or(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::(import_address_table_offset, LE)?.into(); @@ -180,10 +179,10 @@ pub struct ImportData<'a> { } impl<'a> ImportData<'a> { - pub fn parse>(bytes: &'a[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result> { + pub fn parse>(bytes: &'a[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { 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).ok_or(error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;; + let offset = &mut utils::find_offset(import_directory_table_rva, sections, file_alignment).ok_or(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 { @@ -192,7 +191,7 @@ impl<'a> ImportData<'a> { if import_directory_entry.is_null() { break; } else { - let entry = SyntheticImportDirectoryEntry::parse::(bytes, import_directory_entry, sections)?; + let entry = SyntheticImportDirectoryEntry::parse::(bytes, import_directory_entry, sections, file_alignment)?; debug!("entry {:#?}", entry); import_data.push(entry); } diff --git a/src/pe/mod.rs b/src/pe/mod.rs index b218f45fc..c2019e7ba 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -80,10 +80,11 @@ impl<'a> PE<'a> { image_base = optional_header.windows_fields.image_base as usize; is_64 = optional_header.container()? == container::Container::Big; debug!("entry {:#x} image_base {:#x} is_64: {}", entry, image_base, is_64); + let file_alignment = optional_header.windows_fields.file_alignment; if let &Some(export_table) = optional_header.data_directories.get_export_table() { - if let Ok(ed) = export::ExportData::parse(bytes, &export_table, §ions) { + if let Ok(ed) = export::ExportData::parse(bytes, &export_table, §ions, file_alignment) { debug!("export data {:#?}", ed); - exports = export::Export::parse(bytes, &ed, §ions)?; + exports = export::Export::parse(bytes, &ed, §ions, file_alignment)?; name = ed.name; debug!("name: {:#?}", name); export_data = Some(ed); @@ -92,9 +93,9 @@ impl<'a> PE<'a> { debug!("exports: {:#?}", exports); if let &Some(import_table) = optional_header.data_directories.get_import_table() { let id = if is_64 { - import::ImportData::parse::(bytes, &import_table, §ions)? + import::ImportData::parse::(bytes, &import_table, §ions, file_alignment)? } else { - import::ImportData::parse::(bytes, &import_table, §ions)? + import::ImportData::parse::(bytes, &import_table, §ions, file_alignment)? }; debug!("import data {:#?}", id); if is_64 { @@ -109,7 +110,7 @@ impl<'a> PE<'a> { } debug!("imports: {:#?}", imports); if let &Some(debug_table) = optional_header.data_directories.get_debug_table() { - debug_data = Some(debug::DebugData::parse(bytes, &debug_table, §ions)?); + debug_data = Some(debug::DebugData::parse(bytes, &debug_table, §ions, file_alignment)?); } } Ok( PE { diff --git a/src/pe/utils.rs b/src/pe/utils.rs index 78ebba41a..bedf988f9 100644 --- a/src/pe/utils.rs +++ b/src/pe/utils.rs @@ -8,18 +8,60 @@ pub fn is_in_range (rva: usize, r1: usize, r2: usize) -> bool { r1 <= rva && rva < r2 } +// reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR +#[inline] +fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize { + const PHYSICAL_ALIGN: usize = 0x1ff; + pointer_to_raw_data & !PHYSICAL_ALIGN +} + +#[inline] +fn section_read_size(section: §ion_table::SectionTable, file_alignment: u32) -> usize { + fn round_size(size: usize) -> usize { + const PAGE_MASK: usize = 0xfff; + (size + PAGE_MASK) & !PAGE_MASK + } + + let file_alignment = file_alignment as usize; + let size_of_raw_data = section.size_of_raw_data as usize; + let virtual_size = section.virtual_size as usize; + let read_size = { + let read_size = (section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1) & !(file_alignment - 1); + let raw_data_round_size = round_size(size_of_raw_data); + if read_size < raw_data_round_size { + read_size + } else { + raw_data_round_size + } + }; + + if virtual_size == 0 { + read_size + } else { + let virtual_size_round = round_size(virtual_size); + if read_size < virtual_size_round { + read_size + } else { + virtual_size_round + } + } +} + fn rva2offset (rva: usize, section: §ion_table::SectionTable) -> usize { - (rva - section.virtual_address as usize) + section.pointer_to_raw_data as usize + (rva - section.virtual_address as usize) + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize) } -fn is_in_section (rva: usize, section: §ion_table::SectionTable) -> bool { - section.virtual_address as usize <= rva && rva < (section.virtual_address + section.virtual_size) as usize +fn is_in_section (rva: usize, section: §ion_table::SectionTable, file_alignment: u32) -> bool { + let section_rva = section.virtual_address as usize; + is_in_range(rva, section_rva, section_rva + section_read_size(section, file_alignment)) + + // section.virtual_address as usize <= rva && rva < (section.virtual_address + section.virtual_size) as usize } -pub fn find_offset (rva: usize, sections: &[section_table::SectionTable]) -> Option { +pub fn find_offset (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> Option { for (i, section) in sections.iter().enumerate() { debug!("Checking {} for {:#x} ∈ {:#x}..{:#x}", section.name().unwrap_or(""), rva, section.virtual_address, section.virtual_address + section.virtual_size); - if is_in_section(rva, §ion) { + if is_in_section(rva, §ion, file_alignment) { let offset = rva2offset(rva, §ion); debug!("Found in section {}({}), remapped into offset {:#x}", section.name().unwrap_or(""), i, offset); return Some(offset) @@ -28,12 +70,12 @@ pub fn find_offset (rva: usize, sections: &[section_table::SectionTable]) -> Opt None } -pub fn find_offset_or (rva: usize, sections: &[section_table::SectionTable], msg: &str) -> error::Result { - find_offset(rva, sections).ok_or(error::Error::Malformed(msg.to_string())) +pub fn find_offset_or (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, msg: &str) -> error::Result { + find_offset(rva, sections, file_alignment).ok_or(error::Error::Malformed(msg.to_string())) } -pub fn try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable]) -> error::Result<&'a str> { - match find_offset(rva, sections) { +pub fn try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<&'a str> { + match find_offset(rva, sections, file_alignment) { Some(offset) => { Ok(bytes.pread::<&str>(offset)?) },