From 0efa02fbb59f64d07cddd7382c5d62526f908598 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 23 Mar 2026 16:59:06 +0100 Subject: [PATCH] feat(pe): parse_imports option --- src/pe/import.rs | 30 ++++++++++++++++++++++++++++++ src/pe/mod.rs | 5 ++++- src/pe/options.rs | 11 +++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/pe/import.rs b/src/pe/import.rs index 99183567..ef3b8c6a 100644 --- a/src/pe/import.rs +++ b/src/pe/import.rs @@ -593,4 +593,34 @@ mod tests { assert_eq!(binary.imports[0].rva, 0x21B8); assert_eq!(binary.imports[0].size, 8); } + + #[test] + fn skip_import_parsing_when_disabled() { + let opts = crate::pe::options::ParseOptions::default().with_parse_imports(false); + let binary = crate::pe::PE::parse_with_opts(WELL_FORMED_IMPORT, &opts) + .expect("Unable to parse binary"); + assert!(binary.import_data.is_none()); + assert!(binary.imports.is_empty()); + assert!(binary.libraries.is_empty()); + assert!(binary.header.optional_header.is_some()); + assert!(!binary.sections.is_empty()); + } + + #[test] + fn skip_imports_preserves_debug_data() { + // Use a binary with debug directories to verify that skipping imports + // does not affect debug data parsing. + const MSVC_BIN: &[u8] = + include_bytes!("../../tests/bins/pe/debug_directories-msvc.exe.bin"); + let opts = crate::pe::options::ParseOptions::default().with_parse_imports(false); + let binary = + crate::pe::PE::parse_with_opts(MSVC_BIN, &opts).expect("Unable to parse binary"); + assert!(binary.import_data.is_none()); + assert!(binary.imports.is_empty()); + let debug_data = binary + .debug_data + .as_ref() + .expect("debug_data should be present"); + assert!(debug_data.codeview_pdb70_debug_info.is_some()); + } } diff --git a/src/pe/mod.rs b/src/pe/mod.rs index 70e88226..53d3836d 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -194,7 +194,10 @@ impl<'a> PE<'a> { } } debug!("exports: {:#?}", exports); - if let Some(&import_table) = optional_header.data_directories.get_import_table() { + if let (true, Some(&import_table)) = ( + opts.parse_imports, + optional_header.data_directories.get_import_table(), + ) { let id = if is_64 { import::ImportData::parse_with_opts::( bytes, diff --git a/src/pe/options.rs b/src/pe/options.rs index 99595a3c..3461829f 100644 --- a/src/pe/options.rs +++ b/src/pe/options.rs @@ -17,6 +17,10 @@ pub struct ParseOptions { pub parse_resources: bool, /// Whether or not to end with an error in case of incorrect data or continue parsing if able. Default: ParseMode::Strict pub parse_mode: ParseMode, + /// Whether or not to parse import tables. Set to false if you only need headers, + /// debug info, or exports. This can dramatically speed up parsing for PE files with + /// large or malformed import tables. Default: true + pub parse_imports: bool, } impl Default for ParseOptions { @@ -28,6 +32,7 @@ impl Default for ParseOptions { parse_tls_data: true, parse_resources: true, parse_mode: ParseMode::Strict, + parse_imports: true, } } } @@ -41,6 +46,7 @@ impl ParseOptions { parse_tls_data: true, parse_resources: true, parse_mode: ParseMode::Strict, + parse_imports: true, } } @@ -58,4 +64,9 @@ impl ParseOptions { self.parse_resources = parse_resources; self } + + pub fn with_parse_imports(mut self, parse_imports: bool) -> Self { + self.parse_imports = parse_imports; + self + } }