1use core::iter::FusedIterator;
2
3use crate::error;
4use crate::options::Permissive;
5use log::debug;
6use scroll::{Pread, Pwrite, SizeWith};
7
8use crate::pe::data_directories;
9use crate::pe::options;
10use crate::pe::section_table;
11use crate::pe::utils;
12
13pub const IMAGE_DEBUG_DIRECTORY_SIZE: usize = 0x1C;
15
16#[derive(Debug, Copy, Clone)]
18pub struct ImageDebugDirectoryIterator<'a> {
19 data: &'a [u8],
21 rva_offset: u32,
26}
27
28impl Iterator for ImageDebugDirectoryIterator<'_> {
29 type Item = error::Result<ImageDebugDirectory>;
30
31 fn next(&mut self) -> Option<Self::Item> {
32 if self.data.is_empty() {
33 return None;
34 }
35
36 Some(
37 match self.data.pread_with::<ImageDebugDirectory>(0, scroll::LE) {
38 Ok(func) => {
39 self.data = &self.data[IMAGE_DEBUG_DIRECTORY_SIZE..];
40
41 let idd = ImageDebugDirectory {
43 address_of_raw_data: func.address_of_raw_data.wrapping_sub(self.rva_offset),
44 pointer_to_raw_data: func.pointer_to_raw_data.wrapping_sub(self.rva_offset),
45 ..func
46 };
47
48 debug!(
49 "ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}",
50 idd.address_of_raw_data.wrapping_add(self.rva_offset),
51 idd.address_of_raw_data,
52 );
53
54 debug!(
55 "ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
56 idd.pointer_to_raw_data.wrapping_add(self.rva_offset),
57 idd.pointer_to_raw_data,
58 );
59
60 Ok(idd)
61 }
62 Err(error) => {
63 self.data = &[];
64 Err(error.into())
65 }
66 },
67 )
68 }
69
70 fn size_hint(&self) -> (usize, Option<usize>) {
71 let len = self.data.len() / IMAGE_DEBUG_DIRECTORY_SIZE;
72 (len, Some(len))
73 }
74}
75
76impl FusedIterator for ImageDebugDirectoryIterator<'_> {}
77impl ExactSizeIterator for ImageDebugDirectoryIterator<'_> {}
78
79impl<'a> ImageDebugDirectoryIterator<'a> {
80 pub fn find_type(&self, data_type: u32) -> Option<ImageDebugDirectory> {
82 self.filter_map(Result::ok)
83 .find(|idd| idd.data_type == data_type)
84 }
85}
86
87#[derive(Debug, PartialEq, Clone, Default)]
89pub struct DebugData<'a> {
90 data: &'a [u8],
92 rva_offset: u32,
97 pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>,
104 pub codeview_pdb20_debug_info: Option<CodeviewPDB20DebugInfo<'a>>,
111 pub vcfeature_info: Option<VCFeatureInfo>,
118 pub ex_dll_characteristics_info: Option<ExDllCharacteristicsInfo>,
125 pub repro_info: Option<ReproInfo<'a>>,
133 pub pogo_info: Option<POGOInfo<'a>>,
142}
143
144impl<'a> DebugData<'a> {
145 pub fn parse(
146 bytes: &'a [u8],
147 dd: data_directories::DataDirectory,
148 sections: &[section_table::SectionTable],
149 file_alignment: u32,
150 ) -> error::Result<Self> {
151 Self::parse_with_opts(
152 bytes,
153 dd,
154 sections,
155 file_alignment,
156 &options::ParseOptions::default(),
157 )
158 }
159
160 pub fn parse_with_opts(
161 bytes: &'a [u8],
162 dd: data_directories::DataDirectory,
163 sections: &[section_table::SectionTable],
164 file_alignment: u32,
165 opts: &options::ParseOptions,
166 ) -> error::Result<Self> {
167 Self::parse_with_opts_and_fixup(bytes, dd, sections, file_alignment, opts, 0)
168 }
169
170 pub fn parse_with_opts_and_fixup(
171 bytes: &'a [u8],
172 dd: data_directories::DataDirectory,
173 sections: &[section_table::SectionTable],
174 file_alignment: u32,
175 opts: &options::ParseOptions,
176 rva_offset: u32,
177 ) -> error::Result<Self> {
178 let offset =
179 match utils::find_offset(dd.virtual_address as usize, sections, file_alignment, opts) {
180 Some(offset) => offset,
181 None => {
182 return Err(error::Error::Malformed(format!(
183 "Cannot map ImageDebugDirectory rva {:#x} into offset",
184 dd.virtual_address
185 )))?;
186 }
187 };
188
189 let available_size = if offset + dd.size as usize > bytes.len() {
191 let remaining_bytes = bytes.len().saturating_sub(offset);
192 Err(error::Error::Malformed(format!(
193 "ImageDebugDirectory offset {:#x} and size {:#x} exceeds the bounds of the bytes size {:#x}. \
194 This may indicate a packed binary.",
195 offset,
196 dd.size,
197 bytes.len()
198 )))
199 .or_permissive_and_value(
200 opts.parse_mode.is_permissive(),
201 "ImageDebugDirectory exceeds bounds; truncating",
202 remaining_bytes,
203 )?
204 } else {
205 dd.size as usize
206 };
207
208 let data = if available_size > 0 {
209 bytes.pread_with::<&[u8]>(offset, available_size)?
210 } else {
211 &[]
212 };
213 let it = ImageDebugDirectoryIterator { data, rva_offset };
214
215 let mut codeview_pdb70_debug_info = None;
216 let mut codeview_pdb20_debug_info = None;
217 let mut vcfeature_info = None;
218 let mut ex_dll_characteristics_info = None;
219 let mut repro_info = None;
220 let mut pogo_info = None;
221
222 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_CODEVIEW) {
223 codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse_with_opts(bytes, idd, opts)?;
224 codeview_pdb20_debug_info = CodeviewPDB20DebugInfo::parse_with_opts(bytes, idd, opts)?;
225 }
226 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_VC_FEATURE) {
227 vcfeature_info = Some(VCFeatureInfo::parse_with_opts(bytes, idd, opts)?);
228 }
229 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
230 ex_dll_characteristics_info =
231 Some(ExDllCharacteristicsInfo::parse_with_opts(bytes, idd, opts)?);
232 }
233 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_REPRO) {
234 repro_info = Some(ReproInfo::parse_with_opts(bytes, idd, opts)?);
235 }
236 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_POGO) {
237 pogo_info = POGOInfo::parse_with_opts(bytes, idd, opts)?;
238 }
239
240 Ok(DebugData {
241 data,
242 rva_offset,
243 codeview_pdb70_debug_info,
244 codeview_pdb20_debug_info,
245 vcfeature_info,
246 ex_dll_characteristics_info,
247 repro_info,
248 pogo_info,
249 })
250 }
251
252 pub fn guid(&self) -> Option<[u8; 16]> {
254 self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature)
255 }
256
257 pub fn find_type(&self, data_type: u32) -> Option<ImageDebugDirectory> {
259 self.entries().find_type(data_type)
260 }
261
262 pub fn entries(&self) -> ImageDebugDirectoryIterator<'a> {
264 ImageDebugDirectoryIterator {
265 data: &self.data,
266 rva_offset: self.rva_offset,
267 }
268 }
269}
270
271#[repr(C)]
281#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
282pub struct ImageDebugDirectory {
283 pub characteristics: u32,
285 pub time_date_stamp: u32,
287 pub major_version: u16,
289 pub minor_version: u16,
291 pub data_type: u32,
293 pub size_of_data: u32,
295 pub address_of_raw_data: u32,
297 pub pointer_to_raw_data: u32,
299}
300
301pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0;
303pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1;
305pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2;
307pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3;
309pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4;
311pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
313pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
315pub const IMAGE_DEBUG_TYPE_OMAP_TO_SRC: u32 = 7;
317pub const IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: u32 = 8;
319pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;
321pub const IMAGE_DEBUG_TYPE_RESERVED10: u32 = 10;
323pub const IMAGE_DEBUG_TYPE_BBT: u32 = IMAGE_DEBUG_TYPE_RESERVED10;
325pub const IMAGE_DEBUG_TYPE_CLSID: u32 = 11;
327pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12;
329pub const IMAGE_DEBUG_TYPE_POGO: u32 = 13;
331pub const IMAGE_DEBUG_TYPE_ILTCG: u32 = 14;
333pub const IMAGE_DEBUG_TYPE_MPX: u32 = 15;
335pub const IMAGE_DEBUG_TYPE_REPRO: u32 = 16;
337pub const IMAGE_DEBUG_TYPE_EMBEDDEDPORTABLEPDB: u32 = 17;
339pub const IMAGE_DEBUG_TYPE_SPGO: u32 = 18;
341pub const IMAGE_DEBUG_TYPE_PDBCHECKSUM: u32 = 19;
343pub const IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS: u32 = 20;
345pub const IMAGE_DEBUG_TYPE_PERFMAP: u32 = 21;
347
348pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352;
350pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e;
352pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e;
354pub const CODEVIEW_CV41_MAGIC: u32 = 0x3930_424e;
356
357#[repr(C)]
359#[derive(Debug, PartialEq, Copy, Clone, Default)]
360pub struct CodeviewPDB70DebugInfo<'a> {
361 pub codeview_signature: u32,
362 pub signature: [u8; 16],
363 pub age: u32,
364 pub filename: &'a [u8],
365}
366
367impl<'a> CodeviewPDB70DebugInfo<'a> {
368 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
369 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
370 }
371
372 pub fn parse_with_opts(
373 bytes: &'a [u8],
374 idd: &ImageDebugDirectory,
375 opts: &options::ParseOptions,
376 ) -> error::Result<Option<Self>> {
377 let mut offset: usize = match opts.resolve_rva {
379 true => idd.pointer_to_raw_data as usize,
380 false => idd.address_of_raw_data as usize,
381 };
382
383 let filename_length = idd.size_of_data as isize - 24;
385 if filename_length < 0 {
386 return Err(error::Error::Malformed(format!(
388 "ImageDebugDirectory size of data seems wrong: {:?}",
389 idd.size_of_data
390 )))
391 .or_permissive_and_default(
392 opts.parse_mode.is_permissive(),
393 "ImageDebugDirectory size of data seems wrong",
394 );
395 }
396 let filename_length = filename_length as usize;
397
398 let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
400 if codeview_signature != CODEVIEW_PDB70_MAGIC {
401 return Ok(None);
402 }
403
404 let mut signature: [u8; 16] = [0; 16];
406 signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?);
407 let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
408 if let Some(filename) = bytes.get(offset..offset + filename_length) {
409 Ok(Some(CodeviewPDB70DebugInfo {
410 codeview_signature,
411 signature,
412 age,
413 filename,
414 }))
415 } else {
416 Err(error::Error::Malformed(format!(
417 "ImageDebugDirectory seems corrupted: {:?}",
418 idd
419 )))
420 }
421 }
422}
423
424#[repr(C)]
426#[derive(Debug, PartialEq, Copy, Clone, Default)]
427pub struct VCFeatureInfo {
428 pub pre_vc_plusplus_count: u32,
430 pub c_and_cplusplus_count: u32,
432 pub guard_stack_count: u32,
434 pub sdl_count: u32,
436 pub guard_count: u32,
438}
439
440impl<'a> VCFeatureInfo {
441 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
442 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
443 }
444
445 pub fn parse_with_opts(
446 bytes: &'a [u8],
447 idd: &ImageDebugDirectory,
448 opts: &options::ParseOptions,
449 ) -> error::Result<Self> {
450 let mut offset: usize = match opts.resolve_rva {
451 true => idd.pointer_to_raw_data as usize,
452 false => idd.address_of_raw_data as usize,
453 };
454
455 let pre_vc_plusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
456 let c_and_cplusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
457 let guard_stack_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
458 let sdl_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
459 let guard_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
460
461 Ok(VCFeatureInfo {
462 pre_vc_plusplus_count,
463 c_and_cplusplus_count,
464 guard_stack_count,
465 sdl_count,
466 guard_count,
467 })
468 }
469}
470
471#[repr(C)]
473#[derive(Debug, PartialEq, Copy, Clone, Default)]
474pub struct CodeviewPDB20DebugInfo<'a> {
475 pub codeview_signature: u32,
476 pub codeview_offset: u32,
477 pub signature: u32,
478 pub age: u32,
479 pub filename: &'a [u8],
480}
481
482impl<'a> CodeviewPDB20DebugInfo<'a> {
483 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
484 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
485 }
486
487 pub fn parse_with_opts(
488 bytes: &'a [u8],
489 idd: &ImageDebugDirectory,
490 opts: &options::ParseOptions,
491 ) -> error::Result<Option<Self>> {
492 let mut offset: usize = match opts.resolve_rva {
494 true => idd.pointer_to_raw_data as usize,
495 false => idd.address_of_raw_data as usize,
496 };
497
498 let filename_length = idd.size_of_data as isize - 16;
500 if filename_length < 0 {
501 return Err(error::Error::Malformed(format!(
503 "ImageDebugDirectory size of data seems wrong: {:?}",
504 idd.size_of_data
505 )))
506 .or_permissive_and_default(
507 opts.parse_mode.is_permissive(),
508 "ImageDebugDirectory size of data seems wrong",
509 );
510 }
511 let filename_length = filename_length as usize;
512
513 let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
515 if codeview_signature != CODEVIEW_PDB20_MAGIC {
516 return Ok(None);
517 }
518 let codeview_offset: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
519
520 let signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
522 let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
523 if let Some(filename) = bytes.get(offset..offset + filename_length) {
524 Ok(Some(CodeviewPDB20DebugInfo {
525 codeview_signature,
526 codeview_offset,
527 signature,
528 age,
529 filename,
530 }))
531 } else {
532 Err(error::Error::Malformed(format!(
533 "ImageDebugDirectory seems corrupted: {:?}",
534 idd
535 )))
536 }
537 }
538}
539
540#[derive(Debug, PartialEq, Copy, Clone)]
547pub enum ReproInfo<'a> {
548 TimeDateStamp(u32),
553 Buffer {
558 length: u32,
560 buffer: &'a [u8],
562 },
563}
564
565impl<'a> ReproInfo<'a> {
566 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
567 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
568 }
569
570 pub fn parse_with_opts(
571 bytes: &'a [u8],
572 idd: &ImageDebugDirectory,
573 opts: &options::ParseOptions,
574 ) -> error::Result<Self> {
575 let mut offset: usize = match opts.resolve_rva {
576 true => idd.pointer_to_raw_data as usize,
577 false => idd.address_of_raw_data as usize,
578 };
579
580 if idd.size_of_data > 0 {
583 let length: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
584 if let Some(buffer) = bytes.get(offset..offset + length as usize) {
585 Ok(Self::Buffer { length, buffer })
586 } else {
587 Err(error::Error::Malformed(format!(
588 "ImageDebugDirectory seems corrupted: {:?}",
589 idd
590 )))
591 }
592 } else {
593 Ok(Self::TimeDateStamp(idd.time_date_stamp))
594 }
595 }
596}
597
598#[repr(C)]
606#[derive(Debug, PartialEq, Copy, Clone, Default)]
607pub struct ExDllCharacteristicsInfo {
608 pub characteristics_ex: u32,
624}
625
626pub const IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT: u32 = 0x1;
629pub const IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE: u32 = 0x2;
632pub const IMAGE_DLLCHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE: u32 = 0x4;
635pub const IMAGE_DLLCHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY: u32 = 0x8;
638pub const IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_1: u32 = 0x10;
640pub const IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_2: u32 = 0x20;
642pub const IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT: u32 = 0x40;
648pub const IMAGE_DLLCHARACTERISTICS_EX_HOTPATCH_COMPATIBLE: u32 = 0x80;
655
656impl<'a> ExDllCharacteristicsInfo {
657 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
658 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
659 }
660
661 pub fn parse_with_opts(
662 bytes: &'a [u8],
663 idd: &ImageDebugDirectory,
664 opts: &options::ParseOptions,
665 ) -> error::Result<Self> {
666 let mut offset: usize = match opts.resolve_rva {
668 true => idd.pointer_to_raw_data as usize,
669 false => idd.address_of_raw_data as usize,
670 };
671
672 let characteristics_ex: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
673
674 Ok(ExDllCharacteristicsInfo { characteristics_ex })
675 }
676}
677
678#[repr(C)]
688#[derive(Debug, PartialEq, Copy, Clone, Default)]
689pub struct POGOInfo<'a> {
690 pub signature: u32,
697 pub data: &'a [u8],
699}
700
701#[derive(Debug, PartialEq, Copy, Clone, Default)]
703pub struct POGOInfoEntry<'a> {
704 pub rva: u32,
706 pub size: u32,
708 pub name: &'a [u8],
713}
714
715#[derive(Debug, Copy, Clone)]
717pub struct POGOEntryIterator<'a> {
718 data: &'a [u8],
720}
721
722pub const IMAGE_DEBUG_POGO_SIGNATURE_LTCG: u32 = 0x4C544347;
727pub const IMAGE_DEBUG_POGO_SIGNATURE_PGU: u32 = 0x50475500;
732pub const POGO_SIGNATURE_SIZE: usize = core::mem::size_of::<u32>();
734
735impl<'a> POGOInfo<'a> {
736 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
737 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
738 }
739
740 pub fn parse_with_opts(
741 bytes: &'a [u8],
742 idd: &ImageDebugDirectory,
743 opts: &options::ParseOptions,
744 ) -> error::Result<Option<Self>> {
745 let mut offset: usize = match opts.resolve_rva {
747 true => idd.pointer_to_raw_data as usize,
748 false => idd.address_of_raw_data as usize,
749 };
750
751 let signature = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
752 if signature != IMAGE_DEBUG_POGO_SIGNATURE_LTCG
753 && signature != IMAGE_DEBUG_POGO_SIGNATURE_PGU
754 {
755 return Ok(None);
757 }
758
759 if idd.size_of_data as usize <= POGO_SIGNATURE_SIZE {
760 return Err(error::Error::Malformed(format!(
761 "ImageDebugDirectory size_of_data {:#x} is smaller or equal to POGO_SIGNATURE_SIZE {:#x}",
762 idd.size_of_data, POGO_SIGNATURE_SIZE
763 )));
764 }
765
766 let offset_end = offset
767 .checked_add(idd.size_of_data as usize - POGO_SIGNATURE_SIZE)
768 .ok_or_else(|| {
769 error::Error::Malformed(format!(
770 "ImageDebugDirectory offset ({:#x}) and size ({:#x}) cause an integer overflow",
771 offset,
772 idd.size_of_data as usize - POGO_SIGNATURE_SIZE
773 ))
774 })?;
775
776 if offset > bytes.len() || offset_end > bytes.len() {
777 return Err(error::Error::Malformed(format!(
778 "ImageDebugDirectory offset_start {:#x} or offset_end {:#x} exceed the bounds of the bytes size {:#x}",
779 offset,
780 offset_end,
781 bytes.len()
782 )));
783 }
784
785 let data = &bytes[offset..offset_end];
786 Ok(Some(POGOInfo { signature, data }))
787 }
788
789 pub fn entries(&self) -> POGOEntryIterator<'a> {
791 POGOEntryIterator { data: &self.data }
792 }
793}
794
795impl<'a> Iterator for POGOEntryIterator<'a> {
796 type Item = error::Result<POGOInfoEntry<'a>>;
797
798 fn next(&mut self) -> Option<Self::Item> {
799 if self.data.is_empty() {
800 return None;
801 }
802
803 let mut offset = 0;
804 let rva = match self.data.gread_with::<u32>(&mut offset, scroll::LE) {
805 Ok(rva) => rva,
806 Err(error) => return Some(Err(error.into())),
807 };
808 let size = match self.data.gread_with::<u32>(&mut offset, scroll::LE) {
809 Ok(size) => size,
810 Err(error) => return Some(Err(error.into())),
811 };
812
813 if offset >= self.data.len() {
816 return Some(Err(error::Error::Malformed(format!(
817 "Offset {offset:#x} is too big for containing name field of POGO entry (rva {rva:#x} and size {size:#X})",
818 ))));
819 }
820 let name = match self.data[offset..].iter().position(|&b| b == 0) {
821 Some(pos) => {
822 let Some(name) = self.data.gread_with::<&[u8]>(&mut offset, pos + 1).ok() else {
824 return Some(Err(error::Error::Malformed(format!(
825 "Null-terminator for POGO entry (rva {rva:#x} and size {size:#X}) found but exceeds iterator buffer",
826 ))));
827 };
828 offset = (offset + 3) & !3;
830 name
831 }
832 None => {
833 return Some(Err(error::Error::Malformed(format!(
834 "Cannot find null-terminator for POGO entry (rva {rva:#x} and size {size:#X})",
835 ))
836 .into()));
837 }
838 };
839
840 if offset > self.data.len() {
841 return Some(Err(error::Error::Malformed(format!(
842 "Offset {offset:#x} exceeds buffer length {:#x}",
843 self.data.len()
844 ))));
845 }
846 self.data = &self.data[offset..];
847 Some(Ok(POGOInfoEntry { rva, size, name }))
848 }
849}
850
851impl FusedIterator for POGOEntryIterator<'_> {}
852
853#[cfg(test)]
854mod tests {
855 use super::{
856 CODEVIEW_PDB70_MAGIC, ExDllCharacteristicsInfo, IMAGE_DEBUG_POGO_SIGNATURE_LTCG,
857 IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, IMAGE_DEBUG_TYPE_ILTCG,
858 IMAGE_DEBUG_TYPE_POGO, IMAGE_DEBUG_TYPE_REPRO, IMAGE_DEBUG_TYPE_VC_FEATURE,
859 IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT, IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE,
860 ImageDebugDirectory, POGO_SIGNATURE_SIZE, POGOEntryIterator, POGOInfoEntry, ReproInfo,
861 VCFeatureInfo,
862 };
863
864 const NO_DEBUG_DIRECTORIES_BIN: &[u8] =
865 include_bytes!("../../tests/bins/pe/no_debug_directories.exe.bin");
866 const DEBUG_DIRECTORIES_TEST_MSVC_BIN: &[u8] =
867 include_bytes!("../../tests/bins/pe/debug_directories-msvc.exe.bin");
868 const DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN: &[u8] =
869 include_bytes!("../../tests/bins/pe/debug_directories-clang_lld.exe.bin");
870
871 fn ffi_to_string(bytes: &[u8]) -> String {
872 unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
873 .to_string_lossy()
874 .to_string()
875 }
876
877 #[test]
878 fn parse_no_debug_directories() {
879 let binary =
880 crate::pe::PE::parse(NO_DEBUG_DIRECTORIES_BIN).expect("Unable to parse binary");
881 assert_eq!(binary.debug_data.is_none(), true);
882 }
883
884 #[test]
885 fn parse_debug_entries_iterator() {
886 let binary =
887 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
888 assert_eq!(binary.debug_data.is_some(), true);
889 let debug_data = binary.debug_data.unwrap();
890 let entries = debug_data.entries().collect::<Result<Vec<_>, _>>();
891 assert_eq!(entries.is_ok(), true);
892 let entries = entries.unwrap();
893 let entries_expect = vec![
894 ImageDebugDirectory {
895 characteristics: 0x0,
896 time_date_stamp: 0x80AC7661,
897 major_version: 0x0,
898 minor_version: 0x0,
899 data_type: IMAGE_DEBUG_TYPE_CODEVIEW,
900 size_of_data: 0x38,
901 address_of_raw_data: 0x20c0,
902 pointer_to_raw_data: 0x4c0,
903 },
904 ImageDebugDirectory {
905 characteristics: 0x0,
906 time_date_stamp: 0x80AC7661,
907 major_version: 0x0,
908 minor_version: 0x0,
909 data_type: IMAGE_DEBUG_TYPE_VC_FEATURE,
910 size_of_data: 0x14,
911 address_of_raw_data: 0x20f8,
912 pointer_to_raw_data: 0x4f8,
913 },
914 ImageDebugDirectory {
915 characteristics: 0x0,
916 time_date_stamp: 0x80AC7661,
917 major_version: 0x0,
918 minor_version: 0x0,
919 data_type: IMAGE_DEBUG_TYPE_POGO,
920 size_of_data: 0x58,
921 address_of_raw_data: 0x210c,
922 pointer_to_raw_data: 0x50c,
923 },
924 ImageDebugDirectory {
925 characteristics: 0x0,
926 time_date_stamp: 0x80AC7661,
927 major_version: 0x0,
928 minor_version: 0x0,
929 data_type: IMAGE_DEBUG_TYPE_ILTCG,
930 size_of_data: 0x0,
931 address_of_raw_data: 0x0,
932 pointer_to_raw_data: 0x0,
933 },
934 ImageDebugDirectory {
935 characteristics: 0x0,
936 time_date_stamp: 0x80AC7661,
937 major_version: 0x0,
938 minor_version: 0x0,
939 data_type: IMAGE_DEBUG_TYPE_REPRO,
940 size_of_data: 0x24,
941 address_of_raw_data: 0x2164,
942 pointer_to_raw_data: 0x564,
943 },
944 ImageDebugDirectory {
945 characteristics: 0x0,
946 time_date_stamp: 0x80AC7661,
947 major_version: 0x0,
948 minor_version: 0x0,
949 data_type: IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,
950 size_of_data: 0x4,
951 address_of_raw_data: 0x2188,
952 pointer_to_raw_data: 0x588,
953 },
954 ];
955 assert_eq!(entries, entries_expect);
956 }
957
958 #[test]
959 fn parse_debug_codeview_pdb70_msvc() {
960 let binary =
961 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
962 assert_eq!(binary.debug_data.is_some(), true);
963 let debug_data = binary.debug_data.unwrap();
964 assert_eq!(debug_data.codeview_pdb70_debug_info.is_some(), true);
965 let codeview_pdb70_debug_info = debug_data.codeview_pdb70_debug_info.unwrap();
966 let filename = ffi_to_string(codeview_pdb70_debug_info.filename);
967 assert_eq!(filename, String::from("THIS-IS-BINARY-FOR-GOBLIN-TESTS"));
968 assert_eq!(codeview_pdb70_debug_info.age, 3);
969 assert_eq!(
970 codeview_pdb70_debug_info.codeview_signature,
971 CODEVIEW_PDB70_MAGIC
972 );
973 assert_eq!(
974 codeview_pdb70_debug_info.signature,
975 [
976 0x1F, 0x4F, 0x58, 0x9C, 0x3C, 0xEA, 0x00, 0x83, 0x3F, 0x57, 0x00, 0xCC, 0x36, 0xA7,
977 0x84, 0xDF,
978 ]
979 );
980 }
981
982 #[test]
983 fn parse_debug_codeview_pdb70_clang() {
984 let binary = crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN)
985 .expect("Unable to parse binary");
986 assert_eq!(binary.debug_data.is_some(), true);
987 let debug_data = binary.debug_data.unwrap();
988 assert_eq!(debug_data.codeview_pdb70_debug_info.is_some(), true);
989 let codeview_pdb70_debug_info = debug_data.codeview_pdb70_debug_info.unwrap();
990 let filename = ffi_to_string(codeview_pdb70_debug_info.filename);
991 assert_eq!(filename, String::from("THIS-IS-BINARY-FOR-GOBLIN-TESTS"));
992 assert_eq!(codeview_pdb70_debug_info.age, 1);
993 assert_eq!(
994 codeview_pdb70_debug_info.codeview_signature,
995 CODEVIEW_PDB70_MAGIC
996 );
997 assert_eq!(
998 codeview_pdb70_debug_info.signature,
999 [
1000 0xC8, 0xBA, 0xF6, 0xAB, 0xB2, 0x98, 0xD1, 0x9E, 0x4C, 0x4C, 0x44, 0x20, 0x50, 0x44,
1001 0x42, 0x2E,
1002 ]
1003 );
1004 }
1005
1006 #[test]
1007 fn parse_debug_vcfeature() {
1008 let binary =
1009 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1010 assert_eq!(binary.debug_data.is_some(), true);
1011 let debug_data = binary.debug_data.unwrap();
1012 assert_eq!(debug_data.vcfeature_info.is_some(), true);
1013 let vcfeature_info = debug_data.vcfeature_info.unwrap();
1014 let vcfeature_info_expect = VCFeatureInfo {
1015 pre_vc_plusplus_count: 0,
1016 c_and_cplusplus_count: 1,
1017 guard_stack_count: 0,
1018 sdl_count: 0,
1019 guard_count: 0,
1020 };
1021 assert_eq!(vcfeature_info, vcfeature_info_expect);
1022 }
1023
1024 #[test]
1025 fn parse_debug_repro_msvc() {
1026 let binary =
1027 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1028 assert_eq!(binary.debug_data.is_some(), true);
1029 let debug_data = binary.debug_data.unwrap();
1030 assert_eq!(debug_data.repro_info.is_some(), true);
1031 let repro_info = debug_data.repro_info.unwrap();
1032 let repro_info_expect = ReproInfo::Buffer {
1033 length: 32,
1034 buffer: &[
1035 0x1F, 0x4F, 0x58, 0x9C, 0x3C, 0xEA, 0x00, 0x83, 0x3F, 0x57, 0x00, 0xCC, 0x36, 0xA7,
1036 0x84, 0xDF, 0xF7, 0x7C, 0x70, 0xE0, 0xEF, 0x7A, 0xBA, 0x08, 0xD0, 0xA6, 0x8B, 0x7F,
1037 0x61, 0x76, 0xAC, 0x80,
1038 ],
1039 };
1040 assert_eq!(repro_info, repro_info_expect);
1041 }
1042
1043 #[test]
1044 fn parse_debug_repro_clang_lld() {
1045 let binary = crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN)
1046 .expect("Unable to parse binary");
1047 assert_eq!(binary.debug_data.is_some(), true);
1048 let debug_data = binary.debug_data.unwrap();
1049 assert_eq!(debug_data.repro_info.is_some(), true);
1050 let repro_info = debug_data.repro_info.unwrap();
1051 let repro_info_expect = ReproInfo::TimeDateStamp(0xDB2F3908);
1052 assert_eq!(repro_info, repro_info_expect);
1053 }
1054
1055 #[test]
1056 fn parse_debug_exdllcharacteristics() {
1057 let binary =
1058 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1059 assert_eq!(binary.debug_data.is_some(), true);
1060 let debug_data = binary.debug_data.unwrap();
1061 assert_eq!(debug_data.ex_dll_characteristics_info.is_some(), true);
1062 let ex_dll_characteristics_info = debug_data.ex_dll_characteristics_info.unwrap();
1063 let ex_dll_characteristics_info_expect = ExDllCharacteristicsInfo {
1064 characteristics_ex: IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT
1065 | IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE,
1066 };
1067 assert_eq!(
1068 ex_dll_characteristics_info,
1069 ex_dll_characteristics_info_expect
1070 );
1071 }
1072
1073 #[test]
1074 fn parse_debug_pogo() {
1075 let binary =
1076 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1077 assert_eq!(binary.debug_data.is_some(), true);
1078 let debug_data = binary.debug_data.unwrap();
1079 assert_eq!(debug_data.pogo_info.is_some(), true);
1080 let pogo_info = debug_data.pogo_info.unwrap();
1081 assert_eq!(pogo_info.signature, IMAGE_DEBUG_POGO_SIGNATURE_LTCG);
1082 assert_eq!(pogo_info.data.len(), 88 - POGO_SIGNATURE_SIZE);
1083 let entries = pogo_info.entries().collect::<Result<Vec<_>, _>>().unwrap();
1084 let entries_expect = vec![
1085 POGOInfoEntry {
1086 rva: 0x1000,
1087 size: 0x3,
1088 name: b".text$mn\0",
1089 },
1090 POGOInfoEntry {
1091 rva: 0x2000,
1092 size: 0xA8,
1093 name: b".rdata\0",
1094 },
1095 POGOInfoEntry {
1096 rva: 0x20A8,
1097 size: 0x18,
1098 name: b".rdata$voltmd\0",
1099 },
1100 POGOInfoEntry {
1101 rva: 0x20C0,
1102 size: 0xCC,
1103 name: b".rdata$zzzdbg\0",
1104 },
1105 ];
1106 assert_eq!(entries, entries_expect);
1107 }
1108
1109 #[rustfmt::skip]
1110 const MALFORMED_POGO_NAME: &[u8; 83] = &[
1111 0x00, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x24, 0x6d, 0x6e,
1116 0x00, 0x00, 0x00, 0x00,
1117 0x00, 0x20, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x2e, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00,
1122 0xa8, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2e, 0x72, 0x64, 0x61, 0x74, 0x61, 0x24, 0x76,
1127 0x6f, 0x6c, 0x74, 0x6d,
1128 0x64, 0x00, 0x00, 0x00,
1129 0xc0, 0x20, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x2e, 0x72, 0x64, 0x61, 0x74, 0x61, 0x24, 0x7a,
1134 0x7a, 0x7a, 0x64, 0x62,
1135 0x67, 0x00, 0x00, ];
1137
1138 #[test]
1139 #[should_panic = "Malformed(\"Offset 0x18 exceeds buffer length 0x17\")"]
1140 fn parse_debug_pogo_malformed_name() {
1141 let entries = POGOEntryIterator {
1142 data: MALFORMED_POGO_NAME,
1143 };
1144 let _ = entries.collect::<Result<Vec<_>, _>>().unwrap();
1145 }
1146}