Skip to content

Commit ba65c43

Browse files
committed
fix: Iterate all ImageDebugDirectory entries
Turns out, a PE file can have more than one ImageDebugDirectory. Thus far, goblin only looks at the very first one, trying to interpret it as a CV record. This code changes that logic to rather iterate over all the entries to find the one that is a CV record. We still only capture a single entry in the `DebugData` for backwards compatibility reasons. In the future we could as well capture all of them.
1 parent 0fea4f0 commit ba65c43

File tree

1 file changed

+33
-43
lines changed

1 file changed

+33
-43
lines changed

src/pe/debug.rs

+33-43
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::error;
2+
use scroll::ctx::SizeWith;
23
use scroll::{Pread, Pwrite, SizeWith};
34

45
use crate::pe::data_directories;
@@ -8,6 +9,9 @@ use crate::pe::utils;
89

910
#[derive(Debug, PartialEq, Copy, Clone, Default)]
1011
pub struct DebugData<'a> {
12+
// TODO: There can be more than one ImageDebugDirectory.
13+
// To avoid breaking the public API here, we just return the ImageDebugDirectory that has the
14+
// `IMAGE_DEBUG_TYPE_CODEVIEW` type, or the last one if none has a codeview record.
1115
pub image_debug_directory: ImageDebugDirectory,
1216
pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>,
1317
}
@@ -35,14 +39,36 @@ impl<'a> DebugData<'a> {
3539
file_alignment: u32,
3640
opts: &options::ParseOptions,
3741
) -> 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-
42+
let rva = dd.virtual_address as usize;
43+
let mut offset =
44+
utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
45+
error::Error::Malformed(format!(
46+
"Cannot map ImageDebugDirectory rva {:#x} into offset",
47+
rva
48+
))
49+
})?;
50+
51+
let len = dd.size as usize;
52+
let sizeof_directory = ImageDebugDirectory::size_with(&scroll::LE);
53+
let num_entries = len / sizeof_directory;
54+
55+
let mut entries = (0..num_entries)
56+
.map(|_| bytes.gread_with(&mut offset, scroll::LE))
57+
.collect::<Result<Vec<ImageDebugDirectory>, _>>()?;
58+
59+
// find the debug directory that references the codeview record
60+
for (idx, idd) in entries.iter().enumerate() {
61+
if let Some(cv_record) = CodeviewPDB70DebugInfo::parse_with_opts(bytes, idd, opts)? {
62+
return Ok(DebugData {
63+
image_debug_directory: entries[idx],
64+
codeview_pdb70_debug_info: Some(cv_record),
65+
});
66+
}
67+
}
68+
// if we don't have a codeview record, just return the last debug directory
4369
Ok(DebugData {
44-
image_debug_directory,
45-
codeview_pdb70_debug_info,
70+
image_debug_directory: entries.pop().unwrap(),
71+
codeview_pdb70_debug_info: None,
4672
})
4773
}
4874

@@ -75,42 +101,6 @@ pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
75101
pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
76102
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;
77103

78-
impl ImageDebugDirectory {
79-
#[allow(unused)]
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-
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-
114104
pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352;
115105
pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e;
116106
pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e;

0 commit comments

Comments
 (0)