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