From 6126dddc00ccdc080305d033baade624fd22f493 Mon Sep 17 00:00:00 2001 From: John Schock Date: Mon, 6 May 2024 16:47:16 -0700 Subject: [PATCH] Add support for Codeview PDB 2.0 debug directory format --- src/pe/debug.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/pe/debug.rs b/src/pe/debug.rs index cae77b4b..89e6d21a 100644 --- a/src/pe/debug.rs +++ b/src/pe/debug.rs @@ -10,6 +10,7 @@ use crate::pe::utils; pub struct DebugData<'a> { pub image_debug_directory: ImageDebugDirectory, pub codeview_pdb70_debug_info: Option>, + pub codeview_pdb20_debug_info: Option>, } impl<'a> DebugData<'a> { @@ -39,10 +40,13 @@ impl<'a> DebugData<'a> { ImageDebugDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?; let codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directory, opts)?; + let codeview_pdb20_debug_info = + CodeviewPDB20DebugInfo::parse_with_opts(bytes, &image_debug_directory, opts)?; Ok(DebugData { image_debug_directory, codeview_pdb70_debug_info, + codeview_pdb20_debug_info, }) } @@ -184,3 +188,74 @@ impl<'a> CodeviewPDB70DebugInfo<'a> { } } } + +// http://llvm.org/doxygen/CVDebugRecord_8h_source.html +#[repr(C)] +#[derive(Debug, PartialEq, Copy, Clone, Default)] +pub struct CodeviewPDB20DebugInfo<'a> { + pub codeview_signature: u32, + pub codeview_offset: u32, + pub signature: u32, + pub age: u32, + pub filename: &'a [u8], +} + +impl<'a> CodeviewPDB20DebugInfo<'a> { + pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result> { + Self::parse_with_opts(bytes, idd, &options::ParseOptions::default()) + } + + pub fn parse_with_opts( + bytes: &'a [u8], + idd: &ImageDebugDirectory, + 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 CodeviewPDB20DebugInfo either + return Ok(None); + } + + // 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 - 16; + 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_PDB20_MAGIC { + return Ok(None); + } + let codeview_offset: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + + // read the rest + let signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?; + if let Some(filename) = bytes.get(offset..offset + filename_length) { + Ok(Some(CodeviewPDB20DebugInfo { + codeview_signature, + codeview_offset, + signature, + age, + filename, + })) + } else { + Err(error::Error::Malformed(format!( + "ImageDebugDirectory seems corrupted: {:?}", + idd + ))) + } + } +}