From 8e116d29801cfac357946768aacd5124524de1c0 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 20 Jan 2019 00:38:32 +0000 Subject: [PATCH] Fix parsing executables with non-US-English version info resources pelite is hardcoded to only look for US English version info resources, but executables may not have any, so just get the first version info resource instead. --- src/version.rs | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/version.rs b/src/version.rs index 78b4767..5802e7b 100644 --- a/src/version.rs +++ b/src/version.rs @@ -2,11 +2,13 @@ use std::cmp::Ordering; use std::path::Path; use pelite::resources::version_info::VersionInfo; -use pelite::resources::FindError; +use pelite::resources::{FindError, Resources}; use pelite::FileMap; use error::Error; +const VERSION_INFO_RESOURCE_PATH: &str = "/16/1"; + #[derive(Clone, Debug, PartialEq, PartialOrd)] enum Identifier { Numeric(u32), @@ -66,18 +68,36 @@ impl Version { } fn get_pe_version_info(bytes: &[u8]) -> Result { - use pelite; + let resources = get_pe_resources(bytes)?; + + // Can't just call resources.version_info() because that only gets the + // version info for US English, which may not exist. Instead, get the first + // version info block, whatever the language. + let bytes = resources + .find_dir(Path::new(VERSION_INFO_RESOURCE_PATH))? + .entries() + .next() + .ok_or(FindError::NotFound)? + .entry()? + .data() + .ok_or(FindError::UnDirectory)? + .bytes()?; + + VersionInfo::try_from(bytes).map_err(FindError::Pe) +} + +fn get_pe_resources(bytes: &[u8]) -> Result { use pelite::pe64; match pe64::PeFile::from_bytes(bytes) { Ok(file) => { use pelite::pe64::Pe; - file.resources()?.version_info() + file.resources() } Err(pelite::Error::PeMagic) => { use pelite::pe32::{Pe, PeFile}; - PeFile::from_bytes(bytes)?.resources()?.version_info() + PeFile::from_bytes(bytes)?.resources() } Err(e) => Err(e.into()), } @@ -248,6 +268,25 @@ mod tests { assert!(version.pre_release_ids.is_empty()); } + #[test] + fn version_read_file_version_should_find_non_us_english_version_strings() { + let tmp_dir = tempfile::tempdir().unwrap(); + let dll_path = tmp_dir.path().join("7zxa.ru.dll"); + + // Set the version info block's language code to 1049 (Russian). + let mut dll_bytes = std::fs::read("tests/7z/7zxa.dll").unwrap(); + dll_bytes[0x23B10] = 0x19; + std::fs::write(&dll_path, dll_bytes).unwrap(); + + let version = Version::read_product_version(&dll_path).unwrap(); + + assert_eq!( + version.release_ids, + vec![Identifier::Numeric(18), Identifier::Numeric(5)] + ); + assert!(version.pre_release_ids.is_empty()); + } + #[test] fn version_read_product_version_should_error_with_path_if_path_does_not_exist() { let error = Version::read_product_version(Path::new("missing")).unwrap_err();