diff --git a/src/pe/debug.rs b/src/pe/debug.rs index cae77b4b..ebf05d19 100644 --- a/src/pe/debug.rs +++ b/src/pe/debug.rs @@ -1,4 +1,5 @@ use crate::error; +use alloc::vec::Vec; use scroll::{Pread, Pwrite, SizeWith}; use crate::pe::data_directories; @@ -6,10 +7,11 @@ use crate::pe::options; use crate::pe::section_table; use crate::pe::utils; -#[derive(Debug, PartialEq, Copy, Clone, Default)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct DebugData<'a> { - pub image_debug_directory: ImageDebugDirectory, + pub image_debug_directories: Vec, pub codeview_pdb70_debug_info: Option>, + pub vcfeature_info: Option, } impl<'a> DebugData<'a> { @@ -35,14 +37,16 @@ impl<'a> DebugData<'a> { file_alignment: u32, opts: &options::ParseOptions, ) -> error::Result { - let image_debug_directory = + let image_debug_directories = ImageDebugDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?; let codeview_pdb70_debug_info = - CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directory, opts)?; + CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directories, opts)?; + let vcfeature_info = VCFeatureInfo::parse_with_opts(bytes, &image_debug_directories, opts)?; Ok(DebugData { - image_debug_directory, + image_debug_directories, codeview_pdb70_debug_info, + vcfeature_info, }) } @@ -50,6 +54,13 @@ impl<'a> DebugData<'a> { pub fn guid(&self) -> Option<[u8; 16]> { self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature) } + + /// Find a specific debug type in the debug data. + pub fn find_type(&self, data_type: u32) -> Option<&ImageDebugDirectory> { + self.image_debug_directories + .iter() + .find(|idd| idd.data_type == data_type) + } } // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680307(v=vs.85).aspx @@ -74,6 +85,7 @@ pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4; pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5; pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6; pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9; +pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12; impl ImageDebugDirectory { #[allow(unused)] @@ -82,7 +94,7 @@ impl ImageDebugDirectory { dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, - ) -> error::Result { + ) -> error::Result> { Self::parse_with_opts( bytes, dd, @@ -98,16 +110,22 @@ impl ImageDebugDirectory { sections: &[section_table::SectionTable], file_alignment: u32, opts: &options::ParseOptions, - ) -> error::Result { + ) -> error::Result> { let rva = dd.virtual_address as usize; + let entries = dd.size as usize / core::mem::size_of::(); let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| { error::Error::Malformed(format!( "Cannot map ImageDebugDirectory rva {:#x} into offset", rva )) })?; - let idd: Self = bytes.pread_with(offset, scroll::LE)?; - Ok(idd) + let idd_list = (0..entries) + .map(|i| { + let entry = offset + i * core::mem::size_of::(); + bytes.pread_with(entry, scroll::LE) + }) + .collect::, scroll::Error>>()?; + Ok(idd_list) } } @@ -127,60 +145,119 @@ pub struct CodeviewPDB70DebugInfo<'a> { } impl<'a> CodeviewPDB70DebugInfo<'a> { - pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result> { + pub fn parse(bytes: &'a [u8], idd: &Vec) -> error::Result> { Self::parse_with_opts(bytes, idd, &options::ParseOptions::default()) } pub fn parse_with_opts( bytes: &'a [u8], - idd: &ImageDebugDirectory, + idd: &Vec, opts: &options::ParseOptions, ) -> error::Result> { - if idd.data_type != IMAGE_DEBUG_TYPE_CODEVIEW { - // not a codeview debug directory - // that's not an error, but it's not a CodeviewPDB70DebugInfo either - return Ok(None); - } + let idd = idd + .iter() + .find(|idd| idd.data_type == IMAGE_DEBUG_TYPE_CODEVIEW); - // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly - let mut offset: usize = match opts.resolve_rva { - true => idd.pointer_to_raw_data as usize, - false => idd.address_of_raw_data as usize, - }; - - // calculate how long the eventual filename will be, which doubles as a check of the record size - let filename_length = idd.size_of_data as isize - 24; - if filename_length < 0 { - // the record is too short to be plausible - return Err(error::Error::Malformed(format!( - "ImageDebugDirectory size of data seems wrong: {:?}", - idd.size_of_data - ))); - } - let filename_length = filename_length as usize; + if let Some(idd) = idd { + // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly + let mut offset: usize = match opts.resolve_rva { + true => idd.pointer_to_raw_data as usize, + false => idd.address_of_raw_data as usize, + }; - // check the codeview signature - let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?; - if codeview_signature != CODEVIEW_PDB70_MAGIC { - return Ok(None); + // calculate how long the eventual filename will be, which doubles as a check of the record size + let filename_length = idd.size_of_data as isize - 24; + if filename_length < 0 { + // the record is too short to be plausible + return Err(error::Error::Malformed(format!( + "ImageDebugDirectory size of data seems wrong: {:?}", + idd.size_of_data + ))); + } + let filename_length = filename_length as usize; + + // check the codeview signature + let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + if codeview_signature != CODEVIEW_PDB70_MAGIC { + return Ok(None); + } + + // read the rest + let mut signature: [u8; 16] = [0; 16]; + signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?); + let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + if let Some(filename) = bytes.get(offset..offset + filename_length) { + Ok(Some(CodeviewPDB70DebugInfo { + codeview_signature, + signature, + age, + filename, + })) + } else { + Err(error::Error::Malformed(format!( + "ImageDebugDirectory seems corrupted: {:?}", + idd + ))) + } + } else { + // CodeView debug info not found + Ok(None) } + } +} - // read the rest - let mut signature: [u8; 16] = [0; 16]; - signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?); - let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?; - if let Some(filename) = bytes.get(offset..offset + filename_length) { - Ok(Some(CodeviewPDB70DebugInfo { - codeview_signature, - signature, - age, - filename, +/// Represents the `IMAGE_DEBUG_VC_FEATURE_ENTRY` structure +#[repr(C)] +#[derive(Debug, PartialEq, Copy, Clone, Default)] +pub struct VCFeatureInfo { + /// The count of pre-VC++ + pub pre_vc_plusplus_count: u32, + /// The count of C and C++ + pub c_and_cplusplus_count: u32, + /// The count of guard stack + pub guard_stack_count: u32, + /// The count of SDL + pub sdl_count: u32, + /// The count of guard + pub guard_count: u32, +} + +impl<'a> VCFeatureInfo { + pub fn parse(bytes: &'a [u8], idd: &Vec) -> error::Result> { + Self::parse_with_opts(bytes, idd, &options::ParseOptions::default()) + } + + pub fn parse_with_opts( + bytes: &'a [u8], + idd: &Vec, + opts: &options::ParseOptions, + ) -> error::Result> { + let idd = idd + .iter() + .find(|idd| idd.data_type == IMAGE_DEBUG_TYPE_VC_FEATURE); + + if let Some(idd) = idd { + let mut offset: usize = match opts.resolve_rva { + true => idd.pointer_to_raw_data as usize, + false => idd.address_of_raw_data as usize, + }; + + let pre_vc_plusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + let c_and_cplusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + let guard_stack_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + let sdl_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + let guard_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + + Ok(Some(VCFeatureInfo { + pre_vc_plusplus_count, + c_and_cplusplus_count, + guard_stack_count, + sdl_count, + guard_count, })) } else { - Err(error::Error::Malformed(format!( - "ImageDebugDirectory seems corrupted: {:?}", - idd - ))) + // VC Feature info not found + return Ok(None); } } } diff --git a/src/pe/mod.rs b/src/pe/mod.rs index 1f2bac7a..1bfa7c11 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -509,7 +509,7 @@ impl<'a> TE<'a> { // Parse the debug data. Must adjust offsets before parsing the image_debug_directory let mut debug_data = debug::DebugData::default(); - debug_data.image_debug_directory = debug::ImageDebugDirectory::parse_with_opts( + debug_data.image_debug_directories = debug::ImageDebugDirectory::parse_with_opts( bytes, header.debug_dir, §ions, @@ -519,7 +519,7 @@ impl<'a> TE<'a> { TE::fixup_debug_data(&mut debug_data, rva_offset as u32); debug_data.codeview_pdb70_debug_info = debug::CodeviewPDB70DebugInfo::parse_with_opts( bytes, - &debug_data.image_debug_directory, + &debug_data.image_debug_directories, opts, )?; @@ -533,29 +533,21 @@ impl<'a> TE<'a> { /// Adjust all addresses in the TE binary debug data. fn fixup_debug_data(dd: &mut debug::DebugData, rva_offset: u32) { - debug!( - "ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}", - dd.image_debug_directory.address_of_raw_data, - dd.image_debug_directory - .address_of_raw_data - .wrapping_sub(rva_offset), - ); - dd.image_debug_directory.address_of_raw_data = dd - .image_debug_directory - .address_of_raw_data - .wrapping_sub(rva_offset); - - debug!( - "ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}", - dd.image_debug_directory.pointer_to_raw_data, - dd.image_debug_directory - .pointer_to_raw_data - .wrapping_sub(rva_offset), - ); - dd.image_debug_directory.pointer_to_raw_data = dd - .image_debug_directory - .pointer_to_raw_data - .wrapping_sub(rva_offset); + dd.image_debug_directories.iter_mut().for_each(|idd| { + debug!( + "ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}", + idd.address_of_raw_data, + idd.address_of_raw_data.wrapping_sub(rva_offset), + ); + idd.address_of_raw_data = idd.address_of_raw_data.wrapping_sub(rva_offset); + + debug!( + "ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}", + idd.pointer_to_raw_data, + idd.pointer_to_raw_data.wrapping_sub(rva_offset), + ); + idd.pointer_to_raw_data = idd.pointer_to_raw_data.wrapping_sub(rva_offset); + }); } } diff --git a/tests/te.rs b/tests/te.rs index 347131f2..8e8ad5d0 100644 --- a/tests/te.rs +++ b/tests/te.rs @@ -54,13 +54,13 @@ mod te_tests { assert_eq!(te.sections[4].pointer_to_relocations, 0); // Verify the debug directory is correct - assert_eq!(te.debug_data.image_debug_directory.size_of_data, 0xab); + assert_eq!(te.debug_data.image_debug_directories[0].size_of_data, 0xab); assert_eq!( - te.debug_data.image_debug_directory.address_of_raw_data, + te.debug_data.image_debug_directories[0].address_of_raw_data, 0x3b54 ); assert_eq!( - te.debug_data.image_debug_directory.pointer_to_raw_data, + te.debug_data.image_debug_directories[0].pointer_to_raw_data, 0x3b54 ); let debug_info = te.debug_data.codeview_pdb70_debug_info.unwrap();