Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PE: Support multiple debug directories and VCFeature, Repro, ExDllCharacteristics, POGO parsers #403

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4a432f4
PE: Support multiple debug directories and VCFeature metadata
kkent030315 Apr 12, 2024
e0b325f
Import `alloc::vec::Vec;`
kkent030315 Apr 12, 2024
f817a0f
Use `core::mem` instead of `std::mem`
kkent030315 Apr 12, 2024
b4c19ed
Merge remote-tracking branch 'upstream/master' into tls
kkent030315 Oct 27, 2024
22e1a3a
Refactor `DebugData` with TE compatibility
kkent030315 Oct 28, 2024
04dad2d
Add `ReproInfo` and `ExDllCharacteristicsInfo` parsers
kkent030315 Oct 28, 2024
bfa48ca
Remove impl `ImageDebugDirectory`
kkent030315 Oct 28, 2024
9f6254f
Fix docs
kkent030315 Oct 28, 2024
44bb522
Fix doc comments
kkent030315 Oct 28, 2024
7ebb036
add more `IMAGE_DLLCHARACTERISTICS_EX_` fields
kkent030315 Oct 28, 2024
b86b0b8
Merge branch 'tls' of https://github.com/kkent030315/goblin into tls
kkent030315 Oct 28, 2024
a0d22ce
Merge remote-tracking branch 'upstream/master' into tls
kkent030315 Oct 28, 2024
f2af2f6
Fix doc
kkent030315 Oct 28, 2024
10fc043
Format
kkent030315 Oct 28, 2024
b4c2672
Update doc comment
kkent030315 Oct 28, 2024
96ae98f
Add validation for scoped slice bytes
kkent030315 Oct 28, 2024
c5ff9c7
Add tests for codeview/pdb70
kkent030315 Oct 28, 2024
1be8826
Add test case for binaries with no debug directories
kkent030315 Oct 28, 2024
50bdcc8
Fix doc
kkent030315 Oct 28, 2024
2186578
Fix doc
kkent030315 Oct 28, 2024
24879d6
Add parser for `IMAGE_DEBUG_TYPE_POGO`
kkent030315 Oct 28, 2024
60c1430
Fix doc
kkent030315 Oct 28, 2024
ddca65d
Format
kkent030315 Oct 28, 2024
3651bd7
Simplify `POGOEntryIterator::next`
kkent030315 Oct 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 127 additions & 50 deletions src/pe/debug.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::error;
use alloc::vec::Vec;
use scroll::{Pread, Pwrite, SizeWith};

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

#[derive(Debug, PartialEq, Copy, Clone, Default)]
#[derive(Debug, PartialEq, Clone, Default)]
pub struct DebugData<'a> {
pub image_debug_directory: ImageDebugDirectory,
pub image_debug_directories: Vec<ImageDebugDirectory>,
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved
pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>,
pub vcfeature_info: Option<VCFeatureInfo>,
}

impl<'a> DebugData<'a> {
Expand All @@ -35,21 +37,30 @@ impl<'a> DebugData<'a> {
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<Self> {
let image_debug_directory =
let image_debug_directories =
ImageDebugDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?;
let codeview_pdb70_debug_info =
CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directory, opts)?;
CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directories, opts)?;
let vcfeature_info = VCFeatureInfo::parse_with_opts(bytes, &image_debug_directories, opts)?;

Ok(DebugData {
image_debug_directory,
image_debug_directories,
codeview_pdb70_debug_info,
vcfeature_info,
})
}

/// Return this executable's debugging GUID, suitable for matching against a PDB file.
pub fn guid(&self) -> Option<[u8; 16]> {
self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature)
}

/// Find a specific debug type in the debug data.
pub fn find_type(&self, data_type: u32) -> Option<&ImageDebugDirectory> {
self.image_debug_directories
.iter()
.find(|idd| idd.data_type == data_type)
}
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680307(v=vs.85).aspx
Expand All @@ -74,6 +85,7 @@ pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4;
pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;
pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12;

impl ImageDebugDirectory {
#[allow(unused)]
Expand All @@ -82,7 +94,7 @@ impl ImageDebugDirectory {
dd: data_directories::DataDirectory,
sections: &[section_table::SectionTable],
file_alignment: u32,
) -> error::Result<Self> {
) -> error::Result<Vec<Self>> {
Self::parse_with_opts(
bytes,
dd,
Expand All @@ -98,16 +110,22 @@ impl ImageDebugDirectory {
sections: &[section_table::SectionTable],
file_alignment: u32,
opts: &options::ParseOptions,
) -> error::Result<Self> {
) -> error::Result<Vec<Self>> {
let rva = dd.virtual_address as usize;
let entries = dd.size as usize / core::mem::size_of::<ImageDebugDirectory>();
let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
error::Error::Malformed(format!(
"Cannot map ImageDebugDirectory rva {:#x} into offset",
rva
))
})?;
let idd: Self = bytes.pread_with(offset, scroll::LE)?;
Ok(idd)
let idd_list = (0..entries)
.map(|i| {
let entry = offset + i * core::mem::size_of::<ImageDebugDirectory>();
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved
bytes.pread_with(entry, scroll::LE)
})
.collect::<Result<Vec<ImageDebugDirectory>, scroll::Error>>()?;
Ok(idd_list)
}
}

Expand All @@ -127,60 +145,119 @@ pub struct CodeviewPDB70DebugInfo<'a> {
}

impl<'a> CodeviewPDB70DebugInfo<'a> {
pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
pub fn parse(bytes: &'a [u8], idd: &Vec<ImageDebugDirectory>) -> error::Result<Option<Self>> {
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved
Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
}

pub fn parse_with_opts(
bytes: &'a [u8],
idd: &ImageDebugDirectory,
idd: &Vec<ImageDebugDirectory>,
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved
opts: &options::ParseOptions,
) -> error::Result<Option<Self>> {
if idd.data_type != IMAGE_DEBUG_TYPE_CODEVIEW {
// not a codeview debug directory
// that's not an error, but it's not a CodeviewPDB70DebugInfo either
return Ok(None);
}
let idd = idd
.iter()
.find(|idd| idd.data_type == IMAGE_DEBUG_TYPE_CODEVIEW);

// ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
let mut offset: usize = match opts.resolve_rva {
true => idd.pointer_to_raw_data as usize,
false => idd.address_of_raw_data as usize,
};

// calculate how long the eventual filename will be, which doubles as a check of the record size
let filename_length = idd.size_of_data as isize - 24;
if filename_length < 0 {
// the record is too short to be plausible
return Err(error::Error::Malformed(format!(
"ImageDebugDirectory size of data seems wrong: {:?}",
idd.size_of_data
)));
}
let filename_length = filename_length as usize;
if let Some(idd) = idd {
// ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
let mut offset: usize = match opts.resolve_rva {
true => idd.pointer_to_raw_data as usize,
false => idd.address_of_raw_data as usize,
};
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved

// check the codeview signature
let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
if codeview_signature != CODEVIEW_PDB70_MAGIC {
return Ok(None);
// calculate how long the eventual filename will be, which doubles as a check of the record size
let filename_length = idd.size_of_data as isize - 24;
if filename_length < 0 {
// the record is too short to be plausible
return Err(error::Error::Malformed(format!(
"ImageDebugDirectory size of data seems wrong: {:?}",
idd.size_of_data
)));
}
let filename_length = filename_length as usize;

// check the codeview signature
let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
if codeview_signature != CODEVIEW_PDB70_MAGIC {
return Ok(None);
}

// read the rest
let mut signature: [u8; 16] = [0; 16];
signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?);
let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
if let Some(filename) = bytes.get(offset..offset + filename_length) {
Ok(Some(CodeviewPDB70DebugInfo {
codeview_signature,
signature,
age,
filename,
}))
} else {
Err(error::Error::Malformed(format!(
"ImageDebugDirectory seems corrupted: {:?}",
idd
)))
}
} else {
// CodeView debug info not found
Ok(None)
}
}
}

// read the rest
let mut signature: [u8; 16] = [0; 16];
signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?);
let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
if let Some(filename) = bytes.get(offset..offset + filename_length) {
Ok(Some(CodeviewPDB70DebugInfo {
codeview_signature,
signature,
age,
filename,
/// Represents the `IMAGE_DEBUG_VC_FEATURE_ENTRY` structure
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct VCFeatureInfo {
/// The count of pre-VC++
pub pre_vc_plusplus_count: u32,
/// The count of C and C++
pub c_and_cplusplus_count: u32,
/// The count of guard stack
pub guard_stack_count: u32,
/// The count of SDL
pub sdl_count: u32,
/// The count of guard
pub guard_count: u32,
}

impl<'a> VCFeatureInfo {
pub fn parse(bytes: &'a [u8], idd: &Vec<ImageDebugDirectory>) -> error::Result<Option<Self>> {
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved
Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
}

pub fn parse_with_opts(
bytes: &'a [u8],
idd: &Vec<ImageDebugDirectory>,
kkent030315 marked this conversation as resolved.
Show resolved Hide resolved
opts: &options::ParseOptions,
) -> error::Result<Option<Self>> {
let idd = idd
.iter()
.find(|idd| idd.data_type == IMAGE_DEBUG_TYPE_VC_FEATURE);

if let Some(idd) = idd {
let mut offset: usize = match opts.resolve_rva {
true => idd.pointer_to_raw_data as usize,
false => idd.address_of_raw_data as usize,
};

let pre_vc_plusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
let c_and_cplusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
let guard_stack_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
let sdl_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
let guard_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;

Ok(Some(VCFeatureInfo {
pre_vc_plusplus_count,
c_and_cplusplus_count,
guard_stack_count,
sdl_count,
guard_count,
}))
} else {
Err(error::Error::Malformed(format!(
"ImageDebugDirectory seems corrupted: {:?}",
idd
)))
// VC Feature info not found
return Ok(None);
}
}
}
42 changes: 17 additions & 25 deletions src/pe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ impl<'a> TE<'a> {

// Parse the debug data. Must adjust offsets before parsing the image_debug_directory
let mut debug_data = debug::DebugData::default();
debug_data.image_debug_directory = debug::ImageDebugDirectory::parse_with_opts(
debug_data.image_debug_directories = debug::ImageDebugDirectory::parse_with_opts(
bytes,
header.debug_dir,
&sections,
Expand All @@ -519,7 +519,7 @@ impl<'a> TE<'a> {
TE::fixup_debug_data(&mut debug_data, rva_offset as u32);
debug_data.codeview_pdb70_debug_info = debug::CodeviewPDB70DebugInfo::parse_with_opts(
bytes,
&debug_data.image_debug_directory,
&debug_data.image_debug_directories,
opts,
)?;

Expand All @@ -533,29 +533,21 @@ impl<'a> TE<'a> {

/// Adjust all addresses in the TE binary debug data.
fn fixup_debug_data(dd: &mut debug::DebugData, rva_offset: u32) {
debug!(
"ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}",
dd.image_debug_directory.address_of_raw_data,
dd.image_debug_directory
.address_of_raw_data
.wrapping_sub(rva_offset),
);
dd.image_debug_directory.address_of_raw_data = dd
.image_debug_directory
.address_of_raw_data
.wrapping_sub(rva_offset);

debug!(
"ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
dd.image_debug_directory.pointer_to_raw_data,
dd.image_debug_directory
.pointer_to_raw_data
.wrapping_sub(rva_offset),
);
dd.image_debug_directory.pointer_to_raw_data = dd
.image_debug_directory
.pointer_to_raw_data
.wrapping_sub(rva_offset);
dd.image_debug_directories.iter_mut().for_each(|idd| {
debug!(
"ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}",
idd.address_of_raw_data,
idd.address_of_raw_data.wrapping_sub(rva_offset),
);
idd.address_of_raw_data = idd.address_of_raw_data.wrapping_sub(rva_offset);

debug!(
"ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
idd.pointer_to_raw_data,
idd.pointer_to_raw_data.wrapping_sub(rva_offset),
);
idd.pointer_to_raw_data = idd.pointer_to_raw_data.wrapping_sub(rva_offset);
});
}
}

Expand Down
6 changes: 3 additions & 3 deletions tests/te.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ mod te_tests {
assert_eq!(te.sections[4].pointer_to_relocations, 0);

// Verify the debug directory is correct
assert_eq!(te.debug_data.image_debug_directory.size_of_data, 0xab);
assert_eq!(te.debug_data.image_debug_directories[0].size_of_data, 0xab);
assert_eq!(
te.debug_data.image_debug_directory.address_of_raw_data,
te.debug_data.image_debug_directories[0].address_of_raw_data,
0x3b54
);
assert_eq!(
te.debug_data.image_debug_directory.pointer_to_raw_data,
te.debug_data.image_debug_directories[0].pointer_to_raw_data,
0x3b54
);
let debug_info = te.debug_data.codeview_pdb70_debug_info.unwrap();
Expand Down
Loading