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..fd955e3c 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 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 + } }