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