1use crate::error;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::fmt;
5use core::ops::Not;
6use log::debug;
7use scroll::{Pread, Pwrite, SizeWith};
8
9use crate::pe::data_directories;
10use crate::pe::options;
11use crate::pe::section_table;
12use crate::pe::utils;
13
14pub(super) const SIZE_OF_WCHAR: usize = core::mem::size_of::<u16>();
16pub(super) fn to_utf16_string(bytes: &[u8]) -> String {
20 let u16_slice = bytes
21 .chunks(2)
22 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
23 .take_while(|&wchar| wchar != 0)
24 .collect::<Vec<_>>();
25 String::from_utf16_lossy(&u16_slice)
26}
27#[inline]
29pub(super) fn align_up<N>(value: N, align: N) -> N
30where
31 N: core::ops::Add<Output = N>
32 + core::ops::Not<Output = N>
33 + core::ops::BitAnd<Output = N>
34 + core::ops::Sub<Output = N>
35 + core::cmp::PartialEq
36 + core::marker::Copy,
37 u8: Into<N>,
38{
39 debug_assert!(align != 0u8.into(), "Align must be non-zero");
40 (value + align - 1u8.into()) & !(align - 1u8.into())
41}
42
43pub const RT_CURSOR: u16 = 1;
45pub const RT_BITMAP: u16 = 2;
47pub const RT_ICON: u16 = 3;
49pub const RT_MENU: u16 = 4;
51pub const RT_DIALOG: u16 = 5;
53pub const RT_STRING: u16 = 6;
55pub const RT_FONTDIR: u16 = 7;
57pub const RT_FONT: u16 = 8;
59pub const RT_ACCELERATOR: u16 = 9;
61pub const RT_RCDATA: u16 = 10;
63pub const RT_MESSAGETABLE: u16 = 11;
65pub const RT_GROUP_CURSOR: u16 = 12;
67pub const RT_GROUP_ICON: u16 = 14;
69pub const RT_VERSION: u16 = 16;
71pub const RT_DLGINCLUDE: u16 = 17;
73pub const RT_PLUGPLAY: u16 = 19;
75pub const RT_VXD: u16 = 20;
77pub const RT_ANICURSOR: u16 = 21;
79pub const RT_ANIICON: u16 = 22;
81pub const RT_HTML: u16 = 23;
83pub const RT_MANIFEST: u16 = 24;
85
86#[repr(C)]
88#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
89pub struct ImageResourceDirectory {
90 pub characteristics: u32,
92 pub time_date_stamp: u32,
94 pub major_version: u16,
96 pub minor_version: u16,
98 pub number_of_named_entries: u16,
100 pub number_of_id_entries: u16,
102}
103
104pub const IMAGE_RESOURCE_NAME_IS_STRING: u32 = 0x80000000;
106pub const IMAGE_RESOURCE_DATA_IS_DIRECTORY: u32 = 0x80000000;
108pub const IMAGE_RESOURCE_MASK: u32 = 0x7FFFFFFF;
110
111impl<'a> ImageResourceDirectory {
112 pub fn parse(
113 bytes: &'a [u8],
114 dd: data_directories::DataDirectory,
115 sections: &[section_table::SectionTable],
116 file_alignment: u32,
117 ) -> error::Result<Self> {
118 Self::parse_with_opts(
119 bytes,
120 dd,
121 sections,
122 file_alignment,
123 &options::ParseOptions::default(),
124 )
125 }
126
127 pub fn parse_with_opts(
128 bytes: &'a [u8],
129 dd: data_directories::DataDirectory,
130 sections: &[section_table::SectionTable],
131 file_alignment: u32,
132 opts: &options::ParseOptions,
133 ) -> error::Result<Self> {
134 let rva = dd.virtual_address as usize;
135 let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
136 error::Error::Malformed(format!(
137 "Cannot map ImageResourceDirectory rva {:#x} into offset",
138 rva
139 ))
140 })?;
141 let resource_dir = bytes.pread_with(offset, scroll::LE)?;
142 Ok(resource_dir)
143 }
144
145 pub fn count(&self) -> u16 {
150 self.number_of_id_entries + self.number_of_named_entries
151 }
152
153 pub fn entries_size(&self) -> usize {
155 self.count() as usize * RESOURCE_ENTRY_SIZE
156 }
157
158 pub fn next_iter(&self, offset: usize, bytes: &'a [u8]) -> ResourceEntryIterator<'a> {
160 ResourceEntryIterator {
161 num_resources: self.count() as usize,
162 data: &bytes[offset..offset + self.entries_size()],
163 }
164 }
165}
166
167#[derive(Debug, Copy, Clone)]
169pub struct ResourceEntryIterator<'a> {
170 num_resources: usize,
174 data: &'a [u8],
176}
177
178impl Iterator for ResourceEntryIterator<'_> {
179 type Item = error::Result<ResourceEntry>;
180
181 fn next(&mut self) -> Option<Self::Item> {
182 if self.data.is_empty() {
183 return None;
184 }
185
186 Some(match self.data.pread_with(0, scroll::LE) {
187 Ok(func) => {
188 self.data = &self.data[RESOURCE_ENTRY_SIZE..];
189 Ok(func)
190 }
191 Err(error) => {
192 self.data = &[];
193 Err(error.into())
194 }
195 })
196 }
197
198 fn size_hint(&self) -> (usize, Option<usize>) {
199 let len = self.data.len() / RESOURCE_ENTRY_SIZE;
200 (len, Some(len))
201 }
202}
203
204impl<'a> ResourceEntryIterator<'a> {
205 pub fn find_by_id(&self, id: u16) -> error::Result<Option<ResourceEntry>> {
207 self.map(|x| {
208 x.and_then(|x| {
209 if x.id() == Some(id) {
210 Ok(Some(x))
211 } else {
212 Ok(None)
213 }
214 })
215 })
216 .find_map(Result::transpose)
217 .transpose()
218 }
219}
220
221#[repr(C)]
227#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
228pub struct ResourceDataEntry {
229 pub offset_to_data: u32,
234 pub size: u32,
236 pub code_page: u32,
239 pub reserved: u32,
241}
242
243#[repr(C)]
245#[derive(PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
246pub struct ResourceEntry {
247 pub name_or_id: u32,
249 pub offset_to_data_or_directory: u32,
251}
252
253pub const RESOURCE_ENTRY_SIZE: usize = core::mem::size_of::<u64>();
255
256impl fmt::Debug for ResourceEntry {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 f.debug_struct("ResourceEntry")
259 .field("value", &format_args!("{:#x}", self.value()))
260 .field("name_is_string", &self.name_is_string())
261 .field("name_offset", &format_args!("{:#x}", self.name_offset()))
262 .field("id", &self.id())
263 .field("data_is_directory", &self.data_is_directory())
264 .field(
265 "offset_to_directory",
266 &format_args!("{:#x}", self.offset_to_directory()),
267 )
268 .field(
269 "offset_to_data",
270 &format_args!("{:#x?}", self.offset_to_data()),
271 )
272 .finish()
273 }
274}
275
276impl ResourceEntry {
277 pub fn value(&self) -> u64 {
279 ((self.name_or_id) as u64) << 32 | self.offset_to_data_or_directory as u64
280 }
281
282 pub fn name_is_string(&self) -> bool {
286 self.name_or_id & IMAGE_RESOURCE_NAME_IS_STRING != 0
287 }
288
289 pub fn name_offset(&self) -> u32 {
294 self.name_or_id & IMAGE_RESOURCE_MASK
295 }
296
297 pub fn id(&self) -> Option<u16> {
324 self.name_is_string().not().then(|| self.name_or_id as u16)
325 }
326
327 pub fn data_is_directory(&self) -> bool {
331 self.offset_to_data_or_directory & IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0
332 }
333
334 pub fn offset_to_directory(&self) -> u32 {
338 self.offset_to_data_or_directory & IMAGE_RESOURCE_MASK
339 }
340
341 pub fn offset_to_data(&self) -> Option<u32> {
345 self.data_is_directory()
346 .not()
347 .then(|| self.offset_to_data_or_directory)
348 }
349
350 pub fn next_depth<'a>(&self, bytes: &'a [u8]) -> error::Result<Option<ResourceEntry>> {
352 let mut offset = self.offset_to_directory() as usize;
353
354 let dir = bytes.gread_with::<ImageResourceDirectory>(&mut offset, scroll::LE)?;
355 let iterator = dir.next_iter(offset, bytes);
356 let entries = iterator.collect::<Result<Vec<_>, _>>()?;
357
358 Ok(entries.first().map(|x| *x))
359 }
360
361 pub fn recursive_next_depth<'a, P>(
363 &self,
364 bytes: &'a [u8],
365 predicate: P,
366 ) -> error::Result<Option<ResourceEntry>>
367 where
368 P: Fn(&Self) -> bool,
369 {
370 if let Some(next) = self.next_depth(bytes)? {
371 if !predicate(&next) {
372 Ok(Some(next))
373 } else {
374 next.recursive_next_depth(bytes, predicate)
375 }
376 } else {
377 Ok(Some(*self))
378 }
379 }
380}
381
382impl From<u64> for ResourceEntry {
383 fn from(value: u64) -> Self {
384 Self {
385 name_or_id: (value >> 32) as u32,
386 offset_to_data_or_directory: value as u32,
387 }
388 }
389}
390
391#[derive(Debug, Copy, Clone, Default)]
393pub struct ResourceData<'a> {
394 pub image_resource_directory: ImageResourceDirectory,
396 data: &'a [u8],
398 pub version_info: Option<VersionInfo<'a>>,
400 pub manifest_data: Option<ManifestData<'a>>,
402}
403
404impl<'a> ResourceData<'a> {
405 pub fn parse(
406 bytes: &'a [u8],
407 dd: data_directories::DataDirectory,
408 sections: &[section_table::SectionTable],
409 file_alignment: u32,
410 ) -> error::Result<Self> {
411 Self::parse_with_opts(
412 bytes,
413 dd,
414 sections,
415 file_alignment,
416 &options::ParseOptions::default(),
417 )
418 }
419
420 pub fn parse_with_opts(
421 bytes: &'a [u8],
422 dd: data_directories::DataDirectory,
423 sections: &[section_table::SectionTable],
424 file_alignment: u32,
425 opts: &options::ParseOptions,
426 ) -> error::Result<Self> {
427 let image_resource_directory =
428 ImageResourceDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?;
429
430 let rva = dd.virtual_address as usize;
431 let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
432 error::Error::Malformed(format!(
433 "Cannot map ImageResourceDirectory rva {:#x} into offset",
434 rva
435 ))
436 })?;
437
438 if offset + dd.size as usize > bytes.len() {
439 return Err(error::Error::Malformed(format!(
440 "Resource directory offset ({:#x}) and size ({:#x}) exceeds bytes slice ({:#x})",
441 offset,
442 dd.size,
443 bytes.len()
444 )));
445 }
446 let data = &bytes[offset..offset + dd.size as usize];
447
448 let count = image_resource_directory.count() as usize;
449 let offset = core::mem::size_of::<ImageResourceDirectory>();
450 let size = image_resource_directory.entries_size();
451 if offset + size as usize > data.len() {
452 return Err(error::Error::Malformed(format!(
453 "Resource entry offset ({:#x}) and size ({:#x}) exceeds data slice ({:#x})",
454 offset,
455 size,
456 data.len()
457 )));
458 }
459 let iterator_data = &data[offset..offset + size];
460 let iterator = ResourceEntryIterator {
461 num_resources: count,
462 data: iterator_data,
463 };
464 let version_info =
465 VersionInfo::parse(bytes, data, iterator, sections, file_alignment, opts)?;
466 let manifest_data =
467 ManifestData::parse(bytes, data, iterator, sections, file_alignment, opts)?;
468
469 Ok(ResourceData {
470 image_resource_directory,
471 data,
472 version_info,
473 manifest_data,
474 })
475 }
476
477 pub fn count(&self) -> u16 {
482 self.image_resource_directory.count()
483 }
484
485 pub fn entries(&self) -> ResourceEntryIterator<'a> {
490 let offset = core::mem::size_of::<ImageResourceDirectory>();
491 let size = self.image_resource_directory.entries_size();
492 ResourceEntryIterator {
494 num_resources: self.count() as usize,
495 data: &self.data[offset..offset + size],
496 }
497 }
498}
499
500pub const VS_FFI_SIGNATURE: u32 = 0xFEEF04BD;
502pub const VS_FFI_STRUCVERSION: u32 = 0x00010000;
506pub const VS_FFI_FILEFLAGSMASK: u32 = 0x0000003F;
508
509pub const VS_FF_DEBUG: u32 = 0x00000001;
511pub const VS_FF_PRERELEASE: u32 = 0x00000002;
513pub const VS_FF_PATCHED: u32 = 0x00000004;
515pub const VS_FF_PRIVATEBUILD: u32 = 0x00000008;
517pub const VS_FF_INFOINFERRED: u32 = 0x00000010;
519pub const VS_FF_SPECIALBUILD: u32 = 0x00000020;
521
522pub const VOS_UNKNOWN: u32 = 0x00000000;
526pub const VOS_DOS: u32 = 0x00010000;
528pub const VOS_OS216: u32 = 0x00020000;
530pub const VOS_OS232: u32 = 0x00030000;
532pub const VOS_NT: u32 = 0x00040000;
534pub const VOS_WINCE: u32 = 0x00050000;
536
537#[doc(alias("VOS__BASE"))]
541pub const VOS_BASE: u32 = 0x00000000;
542#[doc(alias("VOS__WINDOWS16"))]
544pub const VOS_WINDOWS16: u32 = 0x00000001;
545#[doc(alias("VOS__PM16"))]
547pub const VOS_PM16: u32 = 0x00000002;
548#[doc(alias("VOS__PM32"))]
550pub const VOS_PM32: u32 = 0x00000003;
551#[doc(alias("VOS__WINDOWS32"))]
553pub const VOS_WINDOWS32: u32 = 0x00000004;
554
555pub const VOS_DOS_WINDOWS16: u32 = 0x00010001;
559pub const VOS_DOS_WINDOWS32: u32 = 0x00010004;
561pub const VOS_OS216_PM16: u32 = 0x00020002;
563pub const VOS_OS216_PM32: u32 = 0x00030003;
565pub const VOS_NT_WINDOWS32: u32 = 0x00040004;
567
568pub const VFT_UNKNOWN: u32 = 0x00000000;
572pub const VFT_APP: u32 = 0x00000001;
574pub const VFT_DLL: u32 = 0x00000002;
576pub const VFT_DRV: u32 = 0x00000003;
578pub const VFT_FONT: u32 = 0x00000004;
580pub const VFT_VXD: u32 = 0x00000005;
582pub const VFT_STATIC_LIB: u32 = 0x00000007;
584
585pub const VFT2_UNKNOWN: u32 = 0x00000000;
589pub const VFT2_DRV_PRINTER: u32 = 0x00000001;
591pub const VFT2_DRV_KEYBOARD: u32 = 0x00000002;
593pub const VFT2_DRV_LANGUAGE: u32 = 0x00000003;
595pub const VFT2_DRV_DISPLAY: u32 = 0x00000004;
597pub const VFT2_DRV_MOUSE: u32 = 0x00000005;
599pub const VFT2_DRV_NETWORK: u32 = 0x00000006;
601pub const VFT2_DRV_SYSTEM: u32 = 0x00000007;
603pub const VFT2_DRV_INSTALLABLE: u32 = 0x00000008;
605pub const VFT2_DRV_SOUND: u32 = 0x00000009;
607pub const VFT2_DRV_COMM: u32 = 0x0000000A;
609pub const VFT2_DRV_INPUTMETHOD: u32 = 0x0000000B;
611pub const VFT2_DRV_VERSIONED_PRINTER: u32 = 0x0000000C;
613
614pub const VFT2_FONT_RASTER: u32 = 0x00000001;
618pub const VFT2_FONT_VECTOR: u32 = 0x00000002;
620pub const VFT2_FONT_TRUETYPE: u32 = 0x00000003;
622
623#[derive(Debug, Copy, Clone)]
625pub struct ResourceStringIterator<'a> {
626 pub data: &'a [u8],
628}
629
630impl<'a> Iterator for ResourceStringIterator<'a> {
631 type Item = error::Result<ResourceString<'a>>;
632
633 fn next(&mut self) -> Option<Self::Item> {
634 if self.data.is_empty() {
635 return None;
636 }
637
638 let mut offset = 0;
639 Some(match ResourceString::parse(self.data, &mut offset) {
640 Ok(next) => {
641 debug!(
642 "Parsed next resource string as size {:#x}: {:#x?}",
643 offset, next?
644 );
645 self.data = &self.data[offset..];
646 Ok(next?)
647 }
648 Err(error) => {
649 self.data = &[];
650 Err(error.into())
651 }
652 })
653 }
654}
655
656#[derive(Copy, Clone, PartialEq)]
658pub struct ResourceString<'a> {
659 pub len: u16,
661 pub value_len: u16,
666 pub r#type: u16,
671 pub key: &'a [u8],
673 pub value: &'a [u8],
675}
676
677pub const RESOURCE_STRING_FIELD_ALIGNMENT: usize = core::mem::size_of::<u32>();
679
680impl fmt::Debug for ResourceString<'_> {
681 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
682 let mut debug_struct = f.debug_struct("ResourceString");
683
684 debug_struct.field("len", &format_args!("{:#x}", self.len));
685
686 if self.is_text_data() {
687 debug_struct.field(
689 "value_len",
690 &format_args!(
691 "{:#x} ({} bytes)",
692 self.value_len,
693 self.value_len * SIZE_OF_WCHAR as u16
694 ),
695 );
696 } else {
697 debug_struct.field("value_len", &format_args!("{:#x}", self.value_len));
698 }
699
700 debug_struct
701 .field(
702 "type",
703 &format_args!(
704 "{} ({})",
705 self.r#type,
706 match self.r#type {
707 0 => "Binary Data",
708 1 => "String Data",
709 _ => "Unknown",
710 }
711 ),
712 )
713 .field("key", &self.key_string())
714 .field(
715 "key_slice",
716 &format_args!("{:02x?} ({} bytes)", self.key, self.key.len()),
717 );
718
719 if self.is_text_data() && self.value_len > 0 {
720 debug_struct.field("value", &self.value_string());
721 }
722
723 debug_struct
724 .field(
725 "value_slice",
726 &format_args!(
727 "{:02x?} ({} bytes, {})",
728 self.value,
729 self.value.len(),
730 if self.value.len() == self.value_len as usize {
731 "Correct"
732 } else {
733 "Incorrect"
734 }
735 ),
736 )
737 .finish()
738 }
739}
740
741impl<'a> ResourceString<'a> {
742 pub fn parse(bytes: &'a [u8], offset: &mut usize) -> error::Result<Option<Self>> {
743 let len = bytes.gread_with::<u16>(offset, scroll::LE)?;
744 if len == 0 {
745 return Ok(None);
746 }
747 let value_len = bytes.gread_with::<u16>(offset, scroll::LE)?;
748 let r#type = bytes.gread_with::<u16>(offset, scroll::LE)?;
749 *offset = align_up(*offset, RESOURCE_STRING_FIELD_ALIGNMENT);
750 let key_size = &bytes[*offset..]
751 .chunks(2)
752 .take_while(|x| u16::from_le_bytes([x[0], x[1]]) != 0u16)
753 .count()
754 * SIZE_OF_WCHAR;
755 if (*offset - SIZE_OF_WCHAR) + key_size + SIZE_OF_WCHAR > bytes.len() {
756 return Err(error::Error::Malformed(format!(
757 "offset ({:#x}) and key_size ({:#x}) is greater than bytes len {:#x}",
758 offset,
759 key_size,
760 bytes.len()
761 )));
762 }
763 let key =
764 &bytes[*offset - SIZE_OF_WCHAR..*offset - SIZE_OF_WCHAR + key_size + SIZE_OF_WCHAR];
765 *offset += align_up(key.len(), RESOURCE_STRING_FIELD_ALIGNMENT);
766 let real_value_len = align_up(
767 if r#type == 1 {
768 value_len as usize * SIZE_OF_WCHAR
769 } else {
770 value_len as usize
771 },
772 4,
773 );
774 if *offset + (real_value_len as usize) > bytes.len() {
775 return Err(error::Error::Malformed(format!(
776 "offset ({:#x}) and real_value_len ({:#x}) is greater than bytes len {:#x}",
777 offset,
778 real_value_len,
779 bytes.len()
780 )));
781 }
782 let value = &bytes[*offset..*offset + real_value_len as usize];
783 *offset += value.len();
784 Ok(Some(Self {
785 len,
786 value_len,
787 r#type,
788 key,
789 value,
790 }))
791 }
792
793 pub fn is_text_data(&self) -> bool {
795 self.r#type == 1
796 }
797
798 pub fn is_binary_data(&self) -> bool {
800 self.r#type == 0
801 }
802
803 pub fn key_string(&self) -> String {
805 to_utf16_string(&self.key)
806 }
807
808 pub fn value_string(&self) -> String {
810 to_utf16_string(&self.value)
811 }
812}
813
814#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
822pub struct VersionField {
823 pub major: u16,
825 pub minor: u16,
827 pub build: u16,
829 pub revision: u16,
831}
832
833impl VersionField {
834 pub fn from_ms_ls(ms: u32, ls: u32) -> Self {
840 let major = (ms >> 16) as u16;
841 let minor = (ms & 0xFFFF) as u16;
842 let build = (ls >> 16) as u16;
843 let revision = (ls & 0xFFFF) as u16;
844 Self {
845 major,
846 minor,
847 build,
848 revision,
849 }
850 }
851
852 pub fn to_ms(&self) -> u32 {
857 ((self.major as u32) << 16) | (self.minor as u32)
858 }
859
860 pub fn to_ls(&self) -> u32 {
865 ((self.build as u32) << 16) | (self.revision as u32)
866 }
867
868 pub fn to_string(&self) -> String {
870 format!(
871 "{}.{}.{}.{}",
872 self.major, self.minor, self.build, self.revision
873 )
874 }
875}
876
877#[repr(C)]
880#[derive(PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
881pub struct VsFixedFileInfo {
882 pub signature: u32,
884 pub struct_version: u32,
886 pub file_version_ms: u32,
888 pub file_version_ls: u32,
890 pub product_version_ms: u32,
892 pub product_version_ls: u32,
894 pub file_flags_mask: u32,
896 pub file_flags: u32,
898 pub file_os: u32,
900 pub file_type: u32,
902 pub file_subtype: u32,
904 pub file_date_ms: u32,
906 pub file_date_ls: u32,
908}
909
910impl fmt::Debug for VsFixedFileInfo {
911 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
912 f.debug_struct("VsFixedFileInfo")
913 .field(
914 "signature",
915 &format_args!(
916 "{:#x} ({})",
917 &self.signature,
918 if self.is_valid() { "Valid" } else { "Invalid" }
919 ),
920 )
921 .field(
922 "struct_version",
923 &format_args!("{:#x}", &self.struct_version),
924 )
925 .field(
926 "file_version_ms",
927 &format_args!("{:#x}", &self.file_version_ms),
928 )
929 .field(
930 "file_version_ls",
931 &format_args!("{:#x}", &self.file_version_ls),
932 )
933 .field(
934 "product_version_ms",
935 &format_args!("{:#x}", &self.product_version_ms),
936 )
937 .field(
938 "product_version_ls",
939 &format_args!("{:#x}", &self.product_version_ls),
940 )
941 .field(
942 "file_flags_mask",
943 &format_args!("{:#x}", &self.file_flags_mask),
944 )
945 .field("file_flags", &format_args!("{:#x}", &self.file_flags))
946 .field("file_os", &format_args!("{:#x}", &self.file_os))
947 .field("file_type", &format_args!("{:#x}", &self.file_type))
948 .field("file_subtype", &format_args!("{:#x}", &self.file_subtype))
949 .field("file_date_ms", &format_args!("{:#x}", &self.file_date_ms))
950 .field("file_date_ls", &format_args!("{:#x}", &self.file_date_ls))
951 .finish()
952 }
953}
954
955pub const VERSION_INFO_US_ENGLISH_UNICODE: &str = "040904E4";
964pub const VS_VERSION_INFO_KEY: &str = "VS_VERSION_INFO";
966
967impl VsFixedFileInfo {
968 pub fn is_valid(&self) -> bool {
970 self.signature == VS_FFI_SIGNATURE
971 }
972
973 pub fn file_version(&self) -> VersionField {
975 VersionField::from_ms_ls(self.file_date_ms, self.file_date_ls)
976 }
977
978 pub fn product_version(&self) -> VersionField {
980 VersionField::from_ms_ls(self.product_version_ms, self.product_version_ls)
981 }
982}
983
984#[derive(Copy, Clone)]
990pub struct StringFileInfo<'a> {
991 pub comments: Option<&'a [u8]>,
993 pub company_name: Option<&'a [u8]>,
995 pub file_description: Option<&'a [u8]>,
997 pub file_version: Option<&'a [u8]>,
999 pub internal_name: Option<&'a [u8]>,
1001 pub legal_copyright: Option<&'a [u8]>,
1003 pub legal_trademarks: Option<&'a [u8]>,
1005 pub original_filename: Option<&'a [u8]>,
1007 pub private_build: Option<&'a [u8]>,
1010 pub product_name: Option<&'a [u8]>,
1012 pub product_version: Option<&'a [u8]>,
1014 pub special_build: Option<&'a [u8]>,
1017}
1018
1019impl fmt::Debug for StringFileInfo<'_> {
1020 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1021 f.debug_struct("StringFileInfo")
1022 .field("comments", &format_args!("{:?}", self.comments()))
1023 .field("company_name", &format_args!("{:?}", self.company_name()))
1024 .field(
1025 "file_description",
1026 &format_args!("{:?}", self.file_description()),
1027 )
1028 .field("file_version", &format_args!("{:?}", self.file_version()))
1029 .field("internal_name", &format_args!("{:?}", self.internal_name()))
1030 .field(
1031 "legal_copyright",
1032 &format_args!("{:?}", self.legal_copyright()),
1033 )
1034 .field(
1035 "legal_trademarks",
1036 &format_args!("{:?}", self.legal_trademarks()),
1037 )
1038 .field(
1039 "original_filename",
1040 &format_args!("{:?}", self.original_filename()),
1041 )
1042 .field("private_build", &format_args!("{:?}", self.private_build()))
1043 .field("product_name", &format_args!("{:?}", self.product_name()))
1044 .field(
1045 "product_version",
1046 &format_args!("{:?}", self.product_version()),
1047 )
1048 .field("special_build", &format_args!("{:?}", self.special_build()))
1049 .finish()
1050 }
1051}
1052
1053impl<'a> StringFileInfo<'a> {
1054 fn from_resource_string_iterator(it: ResourceStringIterator<'a>) -> Self {
1055 let find = |s| {
1056 it.filter_map(Result::ok)
1057 .find(|x| x.key_string() == s)
1058 .and_then(|x| Some(x.value))
1059 };
1060
1061 Self {
1062 comments: find("Comments"),
1063 company_name: find("CompanyName"),
1064 file_description: find("FileDescription"),
1065 file_version: find("FileVersion"),
1066 internal_name: find("InternalName"),
1067 legal_copyright: find("LegalCopyright"),
1068 legal_trademarks: find("LegalTrademarks"),
1069 original_filename: find("OriginalFilename"),
1070 private_build: find("PrivateBuild"),
1071 product_name: find("ProductName"),
1072 product_version: find("ProductVersion"),
1073 special_build: find("SpecialBuild"),
1074 }
1075 }
1076
1077 pub fn comments(&self) -> Option<String> {
1079 self.comments.map(|x| to_utf16_string(x))
1080 }
1081
1082 pub fn company_name(&self) -> Option<String> {
1084 self.company_name.map(|x| to_utf16_string(x))
1085 }
1086
1087 pub fn file_description(&self) -> Option<String> {
1089 self.file_description.map(|x| to_utf16_string(x))
1090 }
1091
1092 pub fn file_version(&self) -> Option<String> {
1094 self.file_version.map(|x| to_utf16_string(x))
1095 }
1096
1097 pub fn internal_name(&self) -> Option<String> {
1099 self.internal_name.map(|x| to_utf16_string(x))
1100 }
1101
1102 pub fn legal_copyright(&self) -> Option<String> {
1104 self.legal_copyright.map(|x| to_utf16_string(x))
1105 }
1106
1107 pub fn legal_trademarks(&self) -> Option<String> {
1109 self.legal_trademarks.map(|x| to_utf16_string(x))
1110 }
1111
1112 pub fn original_filename(&self) -> Option<String> {
1114 self.original_filename.map(|x| to_utf16_string(x))
1115 }
1116
1117 pub fn private_build(&self) -> Option<String> {
1119 self.private_build.map(|x| to_utf16_string(x))
1120 }
1121
1122 pub fn product_name(&self) -> Option<String> {
1124 self.product_name.map(|x| to_utf16_string(x))
1125 }
1126
1127 pub fn product_version(&self) -> Option<String> {
1129 self.product_version.map(|x| to_utf16_string(x))
1130 }
1131
1132 pub fn special_build(&self) -> Option<String> {
1134 self.special_build.map(|x| to_utf16_string(x))
1135 }
1136}
1137
1138#[derive(Copy, Clone)]
1140pub struct VersionInfo<'a> {
1141 data: &'a [u8],
1143 pub fixed_info: Option<VsFixedFileInfo>,
1145 pub string_info: StringFileInfo<'a>,
1147}
1148
1149impl fmt::Debug for VersionInfo<'_> {
1150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151 f.debug_struct("VersionInfo")
1152 .field(
1153 "data",
1154 &format_args!("{:02x?} ({} bytes)", &self.data, self.data.len()),
1155 )
1156 .field("fixed_info", &self.fixed_info)
1157 .field("string_info", &self.string_info)
1158 .finish()
1159 }
1160}
1161
1162impl<'a> VersionInfo<'a> {
1163 pub fn parse(
1164 pe: &'a [u8],
1165 bytes: &'a [u8],
1166 it: ResourceEntryIterator<'a>,
1167 sections: &[section_table::SectionTable],
1168 file_alignment: u32,
1169 opts: &options::ParseOptions,
1170 ) -> error::Result<Option<Self>> {
1171 if let Some(entry) = it.find_by_id(RT_VERSION)? {
1172 let offset_to_data =
1173 match entry.recursive_next_depth(bytes, |e| e.offset_to_data().is_none())? {
1174 Some(next) => match next.offset_to_data() {
1175 Some(offset_to_data) => offset_to_data,
1176 None => return Ok(None),
1177 },
1178 None => return Ok(None),
1179 };
1180 let mut offset = offset_to_data as usize;
1181 let data_entry = bytes.gread_with::<ResourceDataEntry>(&mut offset, scroll::LE)?;
1182 let rva = data_entry.offset_to_data as usize;
1183 offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
1184 error::Error::Malformed(format!(
1185 "Cannot map ResourceDataEntry rva {:#x} into offset",
1186 rva
1187 ))
1188 })?;
1189
1190 if offset + data_entry.size as usize > pe.len() {
1191 return Err(error::Error::Malformed(format!(
1192 "offset ({:#x}) and data_entry.size ({:#x}) is greater than pe len {:#x}",
1193 offset,
1194 data_entry.size,
1195 bytes.len()
1196 )));
1197 }
1198 let data = &pe[offset..offset + data_entry.size as usize];
1199 let iterator = ResourceStringIterator { data };
1200 let strings = iterator.collect::<Result<Vec<_>, _>>()?;
1201
1202 let fixed_info = match strings
1203 .iter()
1204 .find(|x| x.key_string() == VS_VERSION_INFO_KEY)
1205 {
1206 Some(version_info) => Some(version_info.value.pread_with(0, scroll::LE)?),
1207 None => None,
1208 };
1209 let string_info = StringFileInfo::from_resource_string_iterator(iterator);
1210
1211 Ok(Some(Self {
1212 data,
1213 fixed_info,
1214 string_info,
1215 }))
1216 } else {
1217 Ok(None)
1218 }
1219 }
1220}
1221
1222#[derive(Copy, Clone, Default)]
1224pub struct ManifestData<'a> {
1225 pub data: &'a [u8],
1227}
1228
1229impl fmt::Debug for ManifestData<'_> {
1230 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1231 f.debug_struct("ManifestData")
1232 .field("value", &format_args!("{:02x?}", self.data))
1233 .finish()
1234 }
1235}
1236
1237impl<'a> ManifestData<'a> {
1238 pub fn parse(
1239 pe: &'a [u8],
1240 bytes: &'a [u8],
1241 it: ResourceEntryIterator<'a>,
1242 sections: &[section_table::SectionTable],
1243 file_alignment: u32,
1244 opts: &options::ParseOptions,
1245 ) -> error::Result<Option<Self>> {
1246 if let Some(entry) = it.find_by_id(RT_MANIFEST)? {
1247 let offset_to_data =
1248 match entry.recursive_next_depth(bytes, |e| e.offset_to_data().is_none())? {
1249 Some(next) => match next.offset_to_data() {
1250 Some(offset_to_data) => offset_to_data,
1251 None => return Ok(None),
1252 },
1253 None => return Ok(None),
1254 };
1255 let mut offset = offset_to_data as usize;
1256 let data_entry = bytes.gread_with::<ResourceDataEntry>(&mut offset, scroll::LE)?;
1257 let rva = data_entry.offset_to_data as usize;
1258 offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
1259 error::Error::Malformed(format!(
1260 "Cannot map ResourceDataEntry rva {:#x} into offset",
1261 rva
1262 ))
1263 })?;
1264
1265 if offset + data_entry.size as usize > pe.len() {
1266 return Err(error::Error::Malformed(format!(
1267 "offset ({:#x}) and data_entry.size ({:#x}) is greater than pe len {:#x}",
1268 offset,
1269 data_entry.size,
1270 bytes.len()
1271 )));
1272 }
1273 let data = &pe[offset..offset + data_entry.size as usize];
1274 Ok(Some(Self { data }))
1275 } else {
1276 Ok(None)
1277 }
1278 }
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283 use super::{
1284 ResourceEntry, ResourceStringIterator, VersionField, VFT_APP, VOS_NT_WINDOWS32,
1285 VS_FFI_FILEFLAGSMASK, VS_FFI_SIGNATURE, VS_FFI_STRUCVERSION, VS_VERSION_INFO_KEY,
1286 };
1287
1288 const HAS_NO_RES: &[u8] = include_bytes!("../../tests/bins/pe/has_no_res.exe.bin");
1289 const HAS_RES_FULL_VERSION_AND_MANIFEST: &[u8] =
1290 include_bytes!("../../tests/bins/pe/has_res_full_version_and_manifest.exe.bin");
1291
1292 const EXPECTED_MANIFEST: &[u8; 413] = &[
1310 0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x27,
1311 0x31, 0x2E, 0x30, 0x27, 0x20, 0x65, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x3D, 0x27,
1312 0x55, 0x54, 0x46, 0x2D, 0x38, 0x27, 0x20, 0x73, 0x74, 0x61, 0x6E, 0x64, 0x61, 0x6C, 0x6F,
1313 0x6E, 0x65, 0x3D, 0x27, 0x79, 0x65, 0x73, 0x27, 0x3F, 0x3E, 0x0D, 0x0A, 0x3C, 0x61, 0x73,
1314 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x27, 0x75,
1315 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72,
1316 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76,
1317 0x31, 0x27, 0x20, 0x6D, 0x61, 0x6E, 0x69, 0x66, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x73,
1318 0x69, 0x6F, 0x6E, 0x3D, 0x27, 0x31, 0x2E, 0x30, 0x27, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20,
1319 0x20, 0x3C, 0x74, 0x72, 0x75, 0x73, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x20, 0x78, 0x6D, 0x6C,
1320 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73,
1321 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A,
1322 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
1323 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x3E, 0x0D, 0x0A,
1324 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x72, 0x65,
1325 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67,
1326 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1327 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,
1328 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6F, 0x6E, 0x4C, 0x65, 0x76, 0x65, 0x6C,
1329 0x20, 0x6C, 0x65, 0x76, 0x65, 0x6C, 0x3D, 0x27, 0x61, 0x73, 0x49, 0x6E, 0x76, 0x6F, 0x6B,
1330 0x65, 0x72, 0x27, 0x20, 0x75, 0x69, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3D, 0x27, 0x66,
1331 0x61, 0x6C, 0x73, 0x65, 0x27, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
1332 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
1333 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67, 0x65, 0x73, 0x3E, 0x0D,
1334 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x73, 0x65, 0x63, 0x75,
1335 0x72, 0x69, 0x74, 0x79, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x74, 0x72,
1336 0x75, 0x73, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x61, 0x73, 0x73,
1337 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x3E, 0x0D, 0x0A,
1338 ];
1339
1340 const PYTHON_INSTALLER_VERSION_INFO: &[u8; 876] = &[
1345 0x78, 0x03, 0x34, 0x00, 0x00, 0x00, 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, 0x45,
1346 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00,
1347 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x04, 0xEF, 0xFE, 0x00,
1348 0x00, 0x01, 0x00, 0x0B, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4E, 0x0C, 0x0B, 0x00, 0x03, 0x00,
1349 0x00, 0x00, 0x4E, 0x0C, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
1350 0x00, 0x01, 0x00, 0x00, 0x00, 0xD8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x74, 0x00,
1351 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65,
1352 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xB4, 0x02, 0x00, 0x00,
1353 0x00, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00, 0x45,
1354 0x00, 0x34, 0x00, 0x00, 0x00, 0x58, 0x00, 0x36, 0x00, 0x00, 0x00, 0x43, 0x00, 0x6F, 0x00,
1355 0x6D, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D,
1356 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00,
1357 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x53, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77,
1358 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x75, 0x00,
1359 0x6E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00,
1360 0x00, 0x00, 0x00, 0x58, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00,
1361 0x65, 0x00, 0x44, 0x00, 0x65, 0x00, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70,
1362 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00,
1363 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2E,
1364 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x20, 0x00, 0x28, 0x00, 0x36, 0x00,
1365 0x34, 0x00, 0x2D, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00,
1366 0x00, 0x38, 0x00, 0x18, 0x00, 0x00, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00,
1367 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00,
1368 0x00, 0x00, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33, 0x00,
1369 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06,
1370 0x00, 0x01, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00,
1371 0x61, 0x00, 0x6C, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x73,
1372 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x7E, 0x00,
1373 0x00, 0x00, 0x4C, 0x00, 0x65, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x43, 0x00, 0x6F,
1374 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00,
1375 0x00, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67,
1376 0x00, 0x68, 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20, 0x00,
1377 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x53,
1378 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00,
1379 0x20, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x75, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74,
1380 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x2E, 0x00, 0x20, 0x00, 0x41, 0x00, 0x6C, 0x00,
1381 0x6C, 0x00, 0x20, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73,
1382 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00,
1383 0x65, 0x00, 0x64, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x30, 0x00, 0x00,
1384 0x00, 0x4F, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x61, 0x00,
1385 0x6C, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D,
1386 0x00, 0x65, 0x00, 0x00, 0x00, 0x70, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00,
1387 0x6E, 0x00, 0x2D, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33,
1388 0x00, 0x2D, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x64, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2E, 0x00,
1389 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, 0x50, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x50,
1390 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x4E, 0x00,
1391 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x79, 0x00, 0x74,
1392 0x00, 0x68, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00,
1393 0x31, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x20, 0x00, 0x28, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2D,
1394 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00,
1395 0x18, 0x00, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63,
1396 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00,
1397 0x6E, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33,
1398 0x00, 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x44, 0x00,
1399 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x61, 0x00, 0x72, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C,
1400 0x00, 0x65, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00,
1401 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x73,
1402 0x00, 0x6C, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00,
1403 0x00, 0x00, 0x09, 0x04, 0xE4, 0x04,
1404 ];
1405
1406 const NTDLL_VERSION_INFO: &[u8; 896] = &[
1412 0x7C, 0x03, 0x34, 0x00, 0x00, 0x00, 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, 0x45,
1413 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00,
1414 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x04, 0xEF, 0xFE, 0x00,
1415 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xAA, 0x11, 0x61, 0x4A, 0x00, 0x00, 0x0A, 0x00,
1416 0xAA, 0x11, 0x61, 0x4A, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
1417 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1418 0x00, 0x00, 0xDC, 0x02, 0x00, 0x00, 0x01, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69,
1419 0x00, 0x6E, 0x00, 0x67, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x49, 0x00,
1420 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x00, 0x01, 0x00, 0x30,
1421 0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00, 0x42, 0x00, 0x30, 0x00,
1422 0x00, 0x00, 0x4C, 0x00, 0x16, 0x00, 0x01, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x70,
1423 0x00, 0x61, 0x00, 0x6E, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00,
1424 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73,
1425 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x72, 0x00,
1426 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E,
1427 0x00, 0x00, 0x00, 0x42, 0x00, 0x0D, 0x00, 0x01, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00,
1428 0x65, 0x00, 0x44, 0x00, 0x65, 0x00, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70,
1429 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x00,
1430 0x54, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20,
1431 0x00, 0x44, 0x00, 0x4C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x27, 0x00,
1432 0x01, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72,
1433 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00,
1434 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34,
1435 0x00, 0x31, 0x00, 0x2E, 0x00, 0x34, 0x00, 0x35, 0x00, 0x32, 0x00, 0x32, 0x00, 0x20, 0x00,
1436 0x28, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x42, 0x00, 0x75, 0x00, 0x69, 0x00, 0x6C,
1437 0x00, 0x64, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x36, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00,
1438 0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00,
1439 0x00, 0x00, 0x00, 0x34, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00,
1440 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D,
1441 0x00, 0x65, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00,
1442 0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x2E, 0x00, 0x01,
1443 0x00, 0x4C, 0x00, 0x65, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x43, 0x00, 0x6F, 0x00,
1444 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x00,
1445 0x00, 0xA9, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00,
1446 0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x72,
1447 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00,
1448 0x6E, 0x00, 0x2E, 0x00, 0x20, 0x00, 0x41, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x72,
1449 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x20, 0x00, 0x72, 0x00,
1450 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x64, 0x00, 0x2E,
1451 0x00, 0x00, 0x00, 0x3C, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x4F, 0x00, 0x72, 0x00, 0x69, 0x00,
1452 0x67, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C,
1453 0x00, 0x65, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6E, 0x00,
1454 0x74, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C,
1455 0x00, 0x00, 0x00, 0x6A, 0x00, 0x25, 0x00, 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00,
1456 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65,
1457 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00,
1458 0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0xAE, 0x00, 0x20, 0x00, 0x57, 0x00, 0x69,
1459 0x00, 0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0xAE, 0x00, 0x20, 0x00,
1460 0x4F, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6E,
1461 0x00, 0x67, 0x00, 0x20, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00,
1462 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x10, 0x00, 0x01, 0x00, 0x50, 0x00, 0x72,
1463 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
1464 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30,
1465 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00,
1466 0x31, 0x00, 0x2E, 0x00, 0x34, 0x00, 0x35, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x44,
1467 0x00, 0x00, 0x00, 0x01, 0x00, 0x56, 0x00, 0x61, 0x00, 0x72, 0x00, 0x46, 0x00, 0x69, 0x00,
1468 0x6C, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00,
1469 0x00, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6E, 0x00,
1470 0x73, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00,
1471 0x00, 0x00, 0x00, 0x09, 0x04, 0xB0, 0x04, 0x00, 0x00, 0x00, 0x00,
1472 ];
1473
1474 #[test]
1475 fn test_resource_entry_unions() {
1476 let entry = ResourceEntry::from(0x8000183880002938);
1477 assert_eq!(entry.name_is_string(), true);
1478 assert_eq!(entry.name_offset(), 0x1838);
1479 assert_eq!(entry.id(), None);
1480 assert_eq!(entry.data_is_directory(), true);
1481 assert_eq!(entry.offset_to_directory(), 0x2938);
1482 assert_eq!(entry.value(), 0x8000183880002938);
1483
1484 let entry = ResourceEntry::from(0x183880002938);
1485 assert_eq!(entry.name_is_string(), false);
1486 assert_eq!(entry.name_offset(), 0x1838); assert_eq!(entry.id(), Some(6200)); assert_eq!(entry.data_is_directory(), true);
1489 assert_eq!(entry.offset_to_directory(), 0x2938);
1490 assert_eq!(entry.value(), 0x183880002938);
1491
1492 let entry = ResourceEntry::from(0x8000183800002938);
1493 assert_eq!(entry.name_is_string(), true);
1494 assert_eq!(entry.name_offset(), 0x1838);
1495 assert_eq!(entry.id(), None);
1496 assert_eq!(entry.data_is_directory(), false);
1497 assert_eq!(entry.offset_to_directory(), 0x2938);
1498 assert_eq!(entry.value(), 0x8000183800002938);
1499
1500 let entry = ResourceEntry::from(0x208800008080);
1501 assert_eq!(entry.name_is_string(), false);
1502 assert_eq!(entry.name_offset(), 0x2088); assert_eq!(entry.id(), Some(8328)); assert_eq!(entry.data_is_directory(), false);
1505 assert_eq!(entry.offset_to_directory(), 0x8080);
1506 assert_eq!(entry.value(), 0x208800008080);
1507
1508 let entry = ResourceEntry::from(0x3880008080);
1509 assert_eq!(entry.name_is_string(), false);
1510 assert_eq!(entry.id(), Some(56));
1511 assert_eq!(entry.data_is_directory(), true);
1512 assert_eq!(entry.offset_to_directory(), 0x8080);
1513 assert_eq!(entry.value(), 0x3880008080);
1514 }
1515
1516 #[test]
1517 fn test_version_field_from_ms_ls() {
1518 const MS: u32 = (4 << 16) | 2; const LS: u32 = (3 << 16) | 1; let mut version = VersionField::from_ms_ls(MS, LS);
1522
1523 assert_eq!(version.major, 4);
1524 assert_eq!(version.minor, 2);
1525 assert_eq!(version.build, 3);
1526 assert_eq!(version.revision, 1);
1527
1528 assert_eq!(version.to_string(), "4.2.3.1");
1529 assert_eq!(version.to_ms(), MS);
1530 assert_eq!(version.to_ls(), LS);
1531
1532 version.major += 1;
1533 version.minor += 2;
1534 assert_eq!(version.to_ms(), (4 + 1 << 16) | 2 + 2);
1535 version.build += 3;
1536 version.revision += 4;
1537 assert_eq!(version.to_ls(), (3 + 3 << 16) | 1 + 4);
1538 }
1539
1540 #[test]
1541 fn parse_no_resource() {
1542 let binary = crate::pe::PE::parse(HAS_NO_RES).expect("Unable to parse binary");
1543 assert_eq!(binary.resource_data.is_none(), true);
1544 }
1545
1546 #[test]
1547 fn parse_full_version_and_manifest() {
1548 let binary = crate::pe::PE::parse(HAS_RES_FULL_VERSION_AND_MANIFEST)
1549 .expect("Unable to parse binary");
1550 assert_eq!(binary.resource_data.is_some(), true);
1551 let res_data = binary.resource_data.unwrap();
1552 assert_eq!(res_data.version_info.is_some(), true);
1553 let ver_info = res_data.version_info.unwrap();
1554
1555 assert_eq!(ver_info.fixed_info.is_some(), true);
1556 let fixed_info = ver_info.fixed_info.unwrap();
1557 assert_eq!(fixed_info.signature, VS_FFI_SIGNATURE);
1558 assert_eq!(fixed_info.is_valid(), true);
1559 assert_eq!(fixed_info.struct_version, VS_FFI_STRUCVERSION);
1560 assert_eq!(fixed_info.file_version_ms, 0x1);
1561 assert_eq!(fixed_info.file_version_ls, 0x0);
1562 assert_eq!(fixed_info.product_version_ms, 0x1);
1563 assert_eq!(fixed_info.product_version_ls, 0x0);
1564 assert_eq!(fixed_info.file_flags_mask, VS_FFI_FILEFLAGSMASK);
1565 assert_eq!(fixed_info.file_flags, 0x0);
1566 assert_eq!(fixed_info.file_os, VOS_NT_WINDOWS32);
1567 assert_eq!(fixed_info.file_type, VFT_APP);
1568 assert_eq!(fixed_info.file_subtype, 0x0);
1569 assert_eq!(fixed_info.file_date_ms, 0x0);
1570 assert_eq!(fixed_info.file_date_ls, 0x0);
1571
1572 let str_info = ver_info.string_info;
1573 assert_eq!(
1574 str_info.comments(),
1575 Some(String::from("GOBLIN-TEST-BIN-COMMENTS"))
1576 );
1577 assert_eq!(
1578 str_info.company_name(),
1579 Some(String::from("GOBLIN-TEST-BIN-COMPANY-NAME"))
1580 );
1581 assert_eq!(
1582 str_info.file_description(),
1583 Some(String::from("GOBLIN-TEST-BIN-FILE-DESCRIPTION"))
1584 );
1585 assert_eq!(
1586 str_info.file_version(),
1587 Some(String::from("GOBLIN-TEST-BIN-FILE-VERSION"))
1588 );
1589 assert_eq!(
1590 str_info.internal_name(),
1591 Some(String::from("GOBLIN-TEST-BIN-INTERNAL-NAME"))
1592 );
1593 assert_eq!(
1594 str_info.legal_copyright(),
1595 Some(String::from("GOBLIN-TEST-BIN-LEGAL-COPYRIGHT"))
1596 );
1597 assert_eq!(
1598 str_info.legal_trademarks(),
1599 Some(String::from("GOBLIN-TEST-BIN-LEGAL-TRADEMARKS"))
1600 );
1601 assert_eq!(
1602 str_info.original_filename(),
1603 Some(String::from("GOBLIN-TEST-BIN-ORIGINAL-FILENAME"))
1604 );
1605 assert_eq!(
1606 str_info.private_build(),
1607 Some(String::from("GOBLIN-TEST-BIN-PRIVATE-BUILD"))
1608 );
1609 assert_eq!(
1610 str_info.product_name(),
1611 Some(String::from("GOBLIN-TEST-BIN-PRODUCT-NAME"))
1612 );
1613 assert_eq!(
1614 str_info.product_version(),
1615 Some(String::from("GOBLIN-TEST-BIN-PRODUCT-VERSION"))
1616 );
1617 assert_eq!(
1618 str_info.special_build(),
1619 Some(String::from("GOBLIN-TEST-BIN-SPECIAL-BUILD"))
1620 );
1621
1622 assert_eq!(res_data.manifest_data.is_some(), true);
1623 let manifest_info = res_data.manifest_data.unwrap();
1624 assert_eq!(manifest_info.data, EXPECTED_MANIFEST);
1625 }
1626
1627 #[test]
1628 fn test_resource_string_iterator() {
1629 let it = ResourceStringIterator {
1631 data: PYTHON_INSTALLER_VERSION_INFO,
1632 };
1633 let it_vec = it.collect::<Result<Vec<_>, _>>();
1634 assert_eq!(it_vec.is_ok(), true);
1635 let it_vec = it_vec.unwrap();
1636
1637 assert_eq!(it_vec[0].is_binary_data(), true);
1638 assert_eq!(it_vec[0].key_string(), VS_VERSION_INFO_KEY);
1639 assert_eq!(
1640 it_vec[0].value,
1641 &[
1642 0xbd, 0x04, 0xef, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00,
1643 0x4e, 0x0c, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x0c, 0x3f, 0x00, 0x00, 0x00,
1644 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x02,
1645 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00
1646 ]
1647 );
1648
1649 assert_eq!(it_vec[1].r#type, 103); assert_eq!(it_vec[1].key_string(), "FileInfo");
1651 assert_eq!(
1652 it_vec[1].value,
1653 &[
1654 0xb4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00,
1655 0x30, 0x00, 0x34, 0x00, 0x45, 0x00, 0x34, 0x00, 0x00, 0x00, 0x58, 0x00, 0x36, 0x00,
1656 0x00, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00,
1657 0x79, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
1658 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1659 0x53, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00,
1660 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00,
1661 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
1662 ]
1663 );
1664
1665 assert_eq!(it_vec[2].is_binary_data(), true);
1666 assert_eq!(it_vec[2].key_string(), "FileDescription");
1667 assert_eq!(it_vec[2].value_string(), "Python 3.11.3 (64-bit)");
1668 assert_eq!(
1669 it_vec[2].value,
1670 &[
1671 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1672 0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x20, 0x00,
1673 0x28, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00,
1674 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
1675 ]
1676 );
1677
1678 assert_eq!(it_vec[3].is_binary_data(), true);
1679 assert_eq!(it_vec[3].key_string(), "FileVersion");
1680 assert_eq!(it_vec[3].value_string(), "3.11.3150.0");
1681 assert_eq!(
1682 it_vec[3].value,
1683 &[
1684 0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x31, 0x00,
1685 0x35, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00,
1686 ]
1687 );
1688
1689 assert_eq!(it_vec[4].is_text_data(), true);
1690 assert_eq!(it_vec[4].key_string(), "InternalName");
1691 assert_eq!(it_vec[4].value_string(), "setup");
1692 assert_eq!(
1693 it_vec[4].value,
1694 &[0x73, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x00, 0x00,]
1695 );
1696
1697 assert_eq!(it_vec[5].is_binary_data(), true);
1698 assert_eq!(it_vec[5].key_string(), "LegalCopyright");
1699 assert_eq!(
1700 it_vec[5].value_string(),
1701 "Copyright (c) Python Software Foundation. All rights reserved."
1702 );
1703 assert_eq!(
1704 it_vec[5].value,
1705 &[
1706 0x43, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00,
1707 0x68, 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20, 0x00,
1708 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1709 0x53, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00,
1710 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00,
1711 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x20, 0x00,
1712 0x41, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00,
1713 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00,
1714 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x00, 0x00,
1715 0x00, 0x00,
1716 ]
1717 );
1718
1719 assert_eq!(it_vec[6].is_binary_data(), true);
1720 assert_eq!(it_vec[6].key_string(), "OriginalFilename");
1721 assert_eq!(it_vec[6].value_string(), "python-3.11.3-amd64.exe");
1722 assert_eq!(
1723 it_vec[6].value,
1724 &[
1725 0x70, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2d, 0x00,
1726 0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2d, 0x00,
1727 0x61, 0x00, 0x6d, 0x00, 0x64, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x65, 0x00,
1728 0x78, 0x00, 0x65, 0x00, 0x00, 0x00,
1729 ]
1730 );
1731
1732 assert_eq!(it_vec[7].is_binary_data(), true);
1733 assert_eq!(it_vec[7].key_string(), "ProductName");
1734 assert_eq!(it_vec[7].value_string(), "Python 3.11.3 (64-bit)");
1735 assert_eq!(
1736 it_vec[7].value,
1737 &[
1738 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1739 0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x20, 0x00,
1740 0x28, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00,
1741 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
1742 ]
1743 );
1744
1745 assert_eq!(it_vec[8].is_binary_data(), true);
1746 assert_eq!(it_vec[8].key_string(), "ProductVersion");
1747 assert_eq!(it_vec[8].value_string(), "3.11.3150.0");
1748 assert_eq!(
1749 it_vec[8].value,
1750 &[
1751 0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x31, 0x00,
1752 0x35, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00,
1753 ]
1754 );
1755
1756 assert_eq!(it_vec[9].is_binary_data(), true);
1757 assert_eq!(it_vec[9].key_string(), "VarFileInfo");
1758 assert_eq!(it_vec[9].value, &[]);
1759
1760 assert_eq!(it_vec[9].is_binary_data(), true);
1761 assert_eq!(it_vec[10].key_string(), "Translation");
1762 assert_eq!(it_vec[10].value, &[0x09, 0x04, 0xe4, 0x04]);
1763
1764 assert_eq!(it_vec.get(11), None);
1765
1766 let it = ResourceStringIterator {
1768 data: NTDLL_VERSION_INFO,
1769 };
1770 let it_vec = it.collect::<Result<Vec<_>, _>>();
1771 assert_eq!(it_vec.is_ok(), true);
1772 }
1773}