From cb9cf7fbc0fd92c0d3c28f13b28bada935e97484 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 23 Mar 2026 16:59:06 +0100 Subject: [PATCH 1/3] feat(pe): parse_imports option --- src/pe/import.rs | 30 ++++++++++++++++++++++ src/pe/mod.rs | 64 ++++++++++++++++++++++++----------------------- src/pe/options.rs | 11 ++++++++ 3 files changed, 74 insertions(+), 31 deletions(-) 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 e55d02f5..29c1f17e 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -194,38 +194,40 @@ impl<'a> PE<'a> { } } debug!("exports: {:#?}", exports); - if let Some(&import_table) = optional_header.data_directories.get_import_table() { - let id = if is_64 { - import::ImportData::parse_with_opts::( - bytes, - import_table, - §ions, - file_alignment, - opts, - )? - } else { - import::ImportData::parse_with_opts::( - bytes, - import_table, - §ions, - file_alignment, - opts, - )? - }; - debug!("import data {:#?}", id); - if is_64 { - imports = import::Import::parse::(bytes, &id, §ions)? - } else { - imports = import::Import::parse::(bytes, &id, §ions)? + if opts.parse_imports { + if let Some(&import_table) = optional_header.data_directories.get_import_table() { + let id = if is_64 { + import::ImportData::parse_with_opts::( + bytes, + import_table, + §ions, + file_alignment, + opts, + )? + } else { + import::ImportData::parse_with_opts::( + bytes, + import_table, + §ions, + file_alignment, + opts, + )? + }; + debug!("import data {:#?}", id); + if is_64 { + imports = import::Import::parse::(bytes, &id, §ions)? + } else { + imports = import::Import::parse::(bytes, &id, §ions)? + } + libraries = id + .import_data + .iter() + .map(|data| data.name) + .collect::>(); + libraries.sort(); + libraries.dedup(); + import_data = Some(id); } - libraries = id - .import_data - .iter() - .map(|data| data.name) - .collect::>(); - libraries.sort(); - libraries.dedup(); - import_data = Some(id); } debug!("imports: {:#?}", imports); if let Some(&debug_table) = optional_header.data_directories.get_debug_table() { diff --git a/src/pe/options.rs b/src/pe/options.rs index 61fd9db4..c5d85657 100644 --- a/src/pe/options.rs +++ b/src/pe/options.rs @@ -14,6 +14,10 @@ pub struct ParseOptions { pub parse_tls_data: 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 { @@ -24,6 +28,7 @@ impl Default for ParseOptions { parse_attribute_certificates: true, parse_tls_data: true, parse_mode: ParseMode::Strict, + parse_imports: true, } } } @@ -36,6 +41,7 @@ impl ParseOptions { parse_attribute_certificates: false, parse_tls_data: true, parse_mode: ParseMode::Strict, + parse_imports: true, } } @@ -43,4 +49,9 @@ impl ParseOptions { self.parse_mode = parse_mode; self } + + pub fn with_parse_imports(mut self, parse_imports: bool) -> Self { + self.parse_imports = parse_imports; + self + } } From 9f632aeff2ab2dea4a25b721c7ddf2b0cd6a8b9f Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 20 Apr 2026 13:48:04 +0200 Subject: [PATCH 2/3] merge parse_imports and import_table result in single if --- src/pe/mod.rs | 66 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/pe/mod.rs b/src/pe/mod.rs index 29c1f17e..e9c4b851 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -194,40 +194,40 @@ impl<'a> PE<'a> { } } debug!("exports: {:#?}", exports); - if opts.parse_imports { - if let Some(&import_table) = optional_header.data_directories.get_import_table() { - let id = if is_64 { - import::ImportData::parse_with_opts::( - bytes, - import_table, - §ions, - file_alignment, - opts, - )? - } else { - import::ImportData::parse_with_opts::( - bytes, - import_table, - §ions, - file_alignment, - opts, - )? - }; - debug!("import data {:#?}", id); - if is_64 { - imports = import::Import::parse::(bytes, &id, §ions)? - } else { - imports = import::Import::parse::(bytes, &id, §ions)? - } - libraries = id - .import_data - .iter() - .map(|data| data.name) - .collect::>(); - libraries.sort(); - libraries.dedup(); - import_data = Some(id); + if opts.parse_imports + && let Some(&import_table) = optional_header.data_directories.get_import_table() + { + let id = if is_64 { + import::ImportData::parse_with_opts::( + bytes, + import_table, + §ions, + file_alignment, + opts, + )? + } else { + import::ImportData::parse_with_opts::( + bytes, + import_table, + §ions, + file_alignment, + opts, + )? + }; + debug!("import data {:#?}", id); + if is_64 { + imports = import::Import::parse::(bytes, &id, §ions)? + } else { + imports = import::Import::parse::(bytes, &id, §ions)? } + libraries = id + .import_data + .iter() + .map(|data| data.name) + .collect::>(); + libraries.sort(); + libraries.dedup(); + import_data = Some(id); } debug!("imports: {:#?}", imports); if let Some(&debug_table) = optional_header.data_directories.get_debug_table() { From db951f2fbe540cd4bfbcedab5411e5d11b2e910c Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 20 Apr 2026 14:06:14 +0200 Subject: [PATCH 3/3] use tuple for merged if --- src/pe/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pe/mod.rs b/src/pe/mod.rs index e9c4b851..fd955e3c 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -194,9 +194,10 @@ impl<'a> PE<'a> { } } debug!("exports: {:#?}", exports); - if opts.parse_imports - && 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,