1 use crate::error; 2 use scroll::{Pread, Pwrite, SizeWith}; 3 4 use crate::pe::data_directories; 5 use crate::pe::options; 6 use crate::pe::section_table; 7 use crate::pe::utils; 8 9 #[derive(Debug, PartialEq, Copy, Clone, Default)] 10 pub struct DebugData<'a> { 11 pub image_debug_directory: ImageDebugDirectory, 12 pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>, 13 } 14 15 impl<'a> DebugData<'a> { parse( bytes: &'a [u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, ) -> error::Result<Self>16 pub fn parse( 17 bytes: &'a [u8], 18 dd: data_directories::DataDirectory, 19 sections: &[section_table::SectionTable], 20 file_alignment: u32, 21 ) -> error::Result<Self> { 22 Self::parse_with_opts( 23 bytes, 24 dd, 25 sections, 26 file_alignment, 27 &options::ParseOptions::default(), 28 ) 29 } 30 parse_with_opts( bytes: &'a [u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, opts: &options::ParseOptions, ) -> error::Result<Self>31 pub fn parse_with_opts( 32 bytes: &'a [u8], 33 dd: data_directories::DataDirectory, 34 sections: &[section_table::SectionTable], 35 file_alignment: u32, 36 opts: &options::ParseOptions, 37 ) -> error::Result<Self> { 38 let image_debug_directory = 39 ImageDebugDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?; 40 let codeview_pdb70_debug_info = 41 CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directory, opts)?; 42 43 Ok(DebugData { 44 image_debug_directory, 45 codeview_pdb70_debug_info, 46 }) 47 } 48 49 /// Return this executable's debugging GUID, suitable for matching against a PDB file. guid(&self) -> Option<[u8; 16]>50 pub fn guid(&self) -> Option<[u8; 16]> { 51 self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature) 52 } 53 } 54 55 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680307(v=vs.85).aspx 56 #[repr(C)] 57 #[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)] 58 pub struct ImageDebugDirectory { 59 pub characteristics: u32, 60 pub time_date_stamp: u32, 61 pub major_version: u16, 62 pub minor_version: u16, 63 pub data_type: u32, 64 pub size_of_data: u32, 65 pub address_of_raw_data: u32, 66 pub pointer_to_raw_data: u32, 67 } 68 69 pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0; 70 pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1; 71 pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2; 72 pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3; 73 pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4; 74 pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5; 75 pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6; 76 pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9; 77 78 impl ImageDebugDirectory { 79 #[allow(unused)] parse( bytes: &[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, ) -> error::Result<Self>80 fn parse( 81 bytes: &[u8], 82 dd: data_directories::DataDirectory, 83 sections: &[section_table::SectionTable], 84 file_alignment: u32, 85 ) -> error::Result<Self> { 86 Self::parse_with_opts( 87 bytes, 88 dd, 89 sections, 90 file_alignment, 91 &options::ParseOptions::default(), 92 ) 93 } 94 parse_with_opts( bytes: &[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32, opts: &options::ParseOptions, ) -> error::Result<Self>95 fn parse_with_opts( 96 bytes: &[u8], 97 dd: data_directories::DataDirectory, 98 sections: &[section_table::SectionTable], 99 file_alignment: u32, 100 opts: &options::ParseOptions, 101 ) -> error::Result<Self> { 102 let rva = dd.virtual_address as usize; 103 let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| { 104 error::Error::Malformed(format!( 105 "Cannot map ImageDebugDirectory rva {:#x} into offset", 106 rva 107 )) 108 })?; 109 let idd: Self = bytes.pread_with(offset, scroll::LE)?; 110 Ok(idd) 111 } 112 } 113 114 pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352; 115 pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e; 116 pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e; 117 pub const CODEVIEW_CV41_MAGIC: u32 = 0x3930_424e; 118 119 // http://llvm.org/doxygen/CVDebugRecord_8h_source.html 120 #[repr(C)] 121 #[derive(Debug, PartialEq, Copy, Clone, Default)] 122 pub struct CodeviewPDB70DebugInfo<'a> { 123 pub codeview_signature: u32, 124 pub signature: [u8; 16], 125 pub age: u32, 126 pub filename: &'a [u8], 127 } 128 129 impl<'a> CodeviewPDB70DebugInfo<'a> { parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>>130 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> { 131 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default()) 132 } 133 parse_with_opts( bytes: &'a [u8], idd: &ImageDebugDirectory, opts: &options::ParseOptions, ) -> error::Result<Option<Self>>134 pub fn parse_with_opts( 135 bytes: &'a [u8], 136 idd: &ImageDebugDirectory, 137 opts: &options::ParseOptions, 138 ) -> error::Result<Option<Self>> { 139 if idd.data_type != IMAGE_DEBUG_TYPE_CODEVIEW { 140 // not a codeview debug directory 141 // that's not an error, but it's not a CodeviewPDB70DebugInfo either 142 return Ok(None); 143 } 144 145 // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly 146 let mut offset: usize = match opts.resolve_rva { 147 true => idd.pointer_to_raw_data as usize, 148 false => idd.address_of_raw_data as usize, 149 }; 150 151 // calculate how long the eventual filename will be, which doubles as a check of the record size 152 let filename_length = idd.size_of_data as isize - 24; 153 if filename_length < 0 || filename_length > 1024 { 154 // the record is too short or too long to be plausible 155 return Err(error::Error::Malformed(format!( 156 "ImageDebugDirectory size of data seems wrong: {:?}", 157 idd.size_of_data 158 ))); 159 } 160 let filename_length = filename_length as usize; 161 162 // check the codeview signature 163 let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?; 164 if codeview_signature != CODEVIEW_PDB70_MAGIC { 165 return Ok(None); 166 } 167 168 // read the rest 169 let mut signature: [u8; 16] = [0; 16]; 170 signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?); 171 let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?; 172 let filename = &bytes[offset..offset + filename_length]; 173 174 Ok(Some(CodeviewPDB70DebugInfo { 175 codeview_signature, 176 signature, 177 age, 178 filename, 179 })) 180 } 181 } 182