diff --git a/src/pe/header.rs b/src/pe/header.rs index d1b4f36a..8558194d 100644 --- a/src/pe/header.rs +++ b/src/pe/header.rs @@ -1,8 +1,9 @@ +use core::iter::FusedIterator; + use crate::error; -use crate::pe::{data_directories, optional_header, section_table, symbol}; +use crate::pe::{data_directories, debug, optional_header, section_table, symbol}; use crate::strtab; use alloc::vec::Vec; -use log::debug; use scroll::{ctx, IOread, IOwrite, Pread, Pwrite, SizeWith}; /// In `winnt.h` and `pe.h`, it's `IMAGE_DOS_HEADER`. It's a DOS header present in all PE binaries. @@ -332,7 +333,7 @@ impl DosHeader { offset ); - let pe_pointer = bytes + let pe_pointer: u32 = bytes .pread_with(PE_POINTER_OFFSET as usize, scroll::LE) .map_err(|_| { error::Error::Malformed(format!( @@ -381,8 +382,7 @@ impl DosHeader { } } -#[repr(C)] -#[derive(Debug, PartialEq, Copy, Clone, Pread, Pwrite)] +#[derive(Debug, PartialEq, Copy, Clone)] /// The DOS stub program which should be executed in DOS mode. It prints the message "This program cannot be run in DOS mode" and exits. /// /// ## Position in a modern PE file @@ -391,17 +391,96 @@ impl DosHeader { /// /// * De facto, can be followed by a non-standard ["Rich header"](https://0xrick.github.io/win-internals/pe3/#rich-header). /// * According to the standard, is followed by the [Header::signature] and then the [CoffHeader]. -pub struct DosStub(pub [u8; 0x40]); -impl Default for DosStub { +pub struct DosStub<'a> { + pub data: &'a [u8], +} +impl<'a> Default for DosStub<'a> { + /// This is the very basic DOS program bytecode representation embedded in MSVC linker. + /// + /// An equivalent (Not a equal) DOS program can be follows: + /// + /// ```asm + /// push cs ; 0E Push the code segment onto the stack + /// pop ds ; 1F Pop the top of the stack into the data segment register + /// + /// _start: + /// mov dx, aMessage ; BA 0E 00 Load the address of the message to the DX register + /// mov ah, 09h ; B4 09 DOS function 09h (display string) to print the message at DS:DX + /// int 21h ; CD 21 Call DOS interrupt 21h for displaying the message + /// + /// mov ax, 4C01h ; B8 01 4C DOS function 4Ch (terminate program) with return code 1 + /// int 21h ; CD 21 Call DOS interrupt 21h for program termination + /// + /// aMessage db 'This program cannot be run in DOS mode.' + /// ``` + #[rustfmt::skip] fn default() -> Self { - // "This program cannot be run in DOS mode" error program - Self([ - 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, - 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, - 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, - 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]) + Self { + data: &[ + 0x0E, // push cs: Setup segment registers + 0x1F, // pop ds: Setup segment registers + 0xBA, 0x0E, 0x00, // mov dx, 0x000E: Load the message address into the DX register + 0xB4, 0x09, // mov ah, 0x09: DOS function to print a string + 0xCD, 0x21, // int 0x21: Trigger DOS interrupt 21h to print the message + 0xB8, 0x01, 0x4C, // mov ax, 0x4C01: Prepare to terminate the program (DOS function 4Ch) + 0xCD, 0x21, // int 0x21: Trigger DOS interrupt 21h to terminate the program + 0x54, 0x68, 0x69, 0x73, // "This" ASCII string "This program cannot be run in DOS mode." + 0x20, 0x70, 0x72, 0x6F, // " pro" Continuation of the ASCII string, + 0x67, 0x72, 0x61, 0x6D, // "gram" Continuation of the ASCII string, + 0x20, 0x63, 0x61, 0x6E, // " can" Continuation of the ASCII string, + 0x6E, 0x6F, 0x74, 0x20, // "not " Continuation of the ASCII string, + 0x62, 0x65, 0x20, 0x72, // "be r" Continuation of the ASCII string, + 0x75, 0x6E, 0x20, 0x69, // "un i" Continuation of the ASCII string, + 0x6E, 0x20, 0x44, 0x4F, // "n DO" Continuation of the ASCII string, + 0x53, 0x20, 0x6D, 0x6F, // "S mo" Continuation of the ASCII string, + 0x64, 0x65, 0x2E, // "DE." Continuation of the ASCII string, ending with a period. + 0x0D, 0x0D, 0x0A, // Carriage return (CR `0x0D, 0x0D`) and line feed (LF `0x0A`) + 0x24, // '$' (End of string marker for DOS function 09h) + 0x00, 0x00, 0x00, 0x00, // Padding bytes (8-byte alignment) + 0x00, 0x00, 0x00, // Padding bytes (8-byte alignment) + ], + } + } +} +impl<'a> ctx::TryIntoCtx for DosStub<'a> { + type Error = error::Error; + + fn try_into_ctx(self, bytes: &mut [u8], _: scroll::Endian) -> Result { + let offset = &mut 0; + bytes.gwrite_with(&*self.data, offset, ())?; + Ok(*offset) + } +} + +impl<'a> DosStub<'a> { + /// Parse the DOS stub. + /// + /// The DOS stub is a small program that prints the message "This program cannot be run in DOS mode" and exits; and + /// is not really read for the PECOFF file format. It's a relic from the MS-DOS era. + pub fn parse(bytes: &'a [u8], pe_pointer: u32) -> error::Result { + let start_offset = DOS_STUB_OFFSET as usize; + let end_offset = pe_pointer as usize; + + // Check if the end_offset is less than or equal to start_offset + if end_offset <= start_offset { + return Err(error::Error::Malformed(format!( + "PE pointer ({:#x}) must be greater than the start offset ({:#x})", + pe_pointer, start_offset + ))); + } + + if bytes.len() < end_offset as usize { + return Err(error::Error::Malformed(format!( + "DOS stub is too short ({} bytes) to contain the PE header pointer ({:#x})", + bytes.len(), + end_offset + ))); + } + + let dos_stub_area = &bytes[start_offset..end_offset]; + Ok(Self { + data: dos_stub_area, + }) } } @@ -774,25 +853,26 @@ impl CoffHeader { /// The PE header is located at the very beginning of the file and /// is followed by the section table and sections. #[derive(Debug, PartialEq, Copy, Clone, Default)] -pub struct Header { +pub struct Header<'a> { pub dos_header: DosHeader, /// DOS program for legacy loaders - pub dos_stub: DosStub, + pub dos_stub: DosStub<'a>, + pub rich_header: Option>, - // Q (JohnScience): should we care about the "rich header"? - // https://0xrick.github.io/win-internals/pe3/#rich-header - // Introducing it would be a breaking change because it would require a new field in the struct - // but it would be a good addition to the library. - // /// PE Magic: PE\0\0, little endian pub signature: u32, pub coff_header: CoffHeader, pub optional_header: Option, } -impl Header { - fn parse_impl(bytes: &[u8], dos_header: DosHeader, dos_stub: DosStub) -> error::Result { +impl<'a> Header<'a> { + fn parse_impl( + bytes: &'a [u8], + dos_header: DosHeader, + dos_stub: DosStub<'a>, + ) -> error::Result { let mut offset = dos_header.pe_pointer as usize; + let rich_header = RichHeader::parse(&bytes)?; let signature = bytes.gread_with(&mut offset, scroll::LE).map_err(|_| { error::Error::Malformed(format!("cannot parse PE signature (offset {:#x})", offset)) })?; @@ -806,6 +886,7 @@ impl Header { Ok(Header { dos_header, dos_stub, + rich_header, signature, coff_header, optional_header, @@ -813,26 +894,21 @@ impl Header { } /// Parses PE header from the given bytes; this will fail if the DosHeader or DosStub is malformed or missing in some way - pub fn parse(bytes: &[u8]) -> error::Result { + pub fn parse(bytes: &'a [u8]) -> error::Result { let dos_header = DosHeader::parse(&bytes)?; - let dos_stub = bytes.pread(DOS_STUB_OFFSET as usize).map_err(|_| { - error::Error::Malformed(format!( - "cannot parse DOS stub (offset {:#x})", - DOS_STUB_OFFSET - )) - })?; + let dos_stub = DosStub::parse(bytes, dos_header.pe_pointer)?; Header::parse_impl(bytes, dos_header, dos_stub) } /// Parses PE header from the given bytes, a default DosHeader and DosStub are generated, and any malformed header or stub is ignored - pub fn parse_without_dos(bytes: &[u8]) -> error::Result { + pub fn parse_without_dos(bytes: &'a [u8]) -> error::Result { let dos_header = DosHeader::default(); Header::parse_impl(bytes, dos_header, DosStub::default()) } } -impl ctx::TryIntoCtx for Header { +impl<'a> ctx::TryIntoCtx for Header<'a> { type Error = error::Error; fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result { @@ -848,6 +924,251 @@ impl ctx::TryIntoCtx for Header { } } +/// The DANS marker is a XOR-decoded version of the string "DanS" and is used to identify the Rich header. +pub const DANS_MARKER: u32 = 0x536E6144; +/// Size of [DANS_MARKER] in bytes +pub const DANS_MARKER_SIZE: usize = core::mem::size_of::(); +/// The Rich marker is a XOR-decoded version of the string "Rich" and is used to identify the Rich header. +pub const RICH_MARKER: u32 = 0x68636952; +/// Size of [RICH_MARKER] in bytes +pub const RICH_MARKER_SIZE: usize = core::mem::size_of::(); + +/// The Rich header is a undocumented header that is used to store information about the build environment. +/// +/// The Rich Header first appeared in Visual Studio 6.0 and contains: a product identifier, build number, and the number of times it was used during the build process. +#[derive(Debug, PartialEq, Copy, Clone, Default)] +pub struct RichHeader<'a> { + /// Key is 32-bit value used for XOR encrypt/decrypt fields + pub key: u32, + /// The Rich header data with the padding. + pub data: &'a [u8], + /// Padding bytes at the prologue of [Self::data] + pub padding_size: usize, + /// Start offset of the Rich header. + pub start_offset: u32, + /// End offset of the Rich header. + pub end_offset: u32, +} + +/// The Rich metadata is a pair of 32-bit values that store the tool version and the use count. +#[repr(C)] +#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite)] +pub struct RichMetadata { + /// Build version is a 16-bit value that stores the version of the tool used to build the PE file. + pub build: u16, + /// Product identifier is a 16-bit value that stores the type of tool used to build the PE file. + pub product: u16, + /// The use count is a 32-bit value that stores the number of times the tool was used during the build process. + pub use_count: u32, +} + +impl RichMetadata { + /// Parse [`RichMetadata`] from given bytes + fn parse(bytes: &[u8], key: u32) -> error::Result { + let mut offset = 0; + let build_and_product = bytes.gread_with::(&mut offset, scroll::LE)? ^ key; + let build = (build_and_product & 0xFFFF) as u16; + let product = (build_and_product >> 16) as u16; + let use_count = bytes.gread_with::(&mut offset, scroll::LE)? ^ key; + Ok(Self { + build, + product, + use_count, + }) + } +} + +/// Size of [`RichMetadata`] entries. +const RICH_METADATA_SIZE: usize = 8; + +/// Iterator over [`RichMetadata`] in [`RichHeader`]. +#[derive(Debug)] +pub struct RichMetadataIterator<'a> { + /// The key of [RichHeader::key] + key: u32, + /// The raw data [RichHeader::data] without padding + data: &'a [u8], +} + +impl Iterator for RichMetadataIterator<'_> { + type Item = error::Result; + + fn next(&mut self) -> Option { + if self.data.is_empty() { + return None; + } + + // Data within this iterator should not have padding + Some(match RichMetadata::parse(&self.data, self.key) { + Ok(metadata) => { + self.data = &self.data[RICH_METADATA_SIZE..]; + Ok(metadata) + } + Err(error) => { + self.data = &[]; + Err(error.into()) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.data.len() / RICH_METADATA_SIZE; + (len, Some(len)) + } +} + +impl FusedIterator for RichMetadataIterator<'_> {} +impl ExactSizeIterator for RichMetadataIterator<'_> {} + +impl<'a> RichHeader<'a> { + /// Parse the rich header from the given bytes. + /// + /// To decode the Rich header, + /// - First locate the Rich marker and the subsequent 32-bit encryption key. + /// - Then, work backwards from the Rich marker, XORing the key with the stored 32-bit values until you decode the DanS marker. + /// + /// Between these markers, you'll find pairs of 32-bit values: + /// + /// - the first indicates the Microsoft tool used, and + /// - the second shows the count of linked object files made with that tool. + /// - The upper 16 bits of the tool ID describe the tool type, + /// - while the lower 16 bits specify the tool’s build version. + pub fn parse(bytes: &'a [u8]) -> error::Result> { + // Parse the DOS header; some fields are required to locate the Rich header. + let dos_header = DosHeader::parse(bytes)?; + let dos_header_end_offset = PE_POINTER_OFFSET as usize; + let pe_header_start_offset = dos_header.pe_pointer as usize; + + // The Rich header is not present in all PE files. + if (pe_header_start_offset - dos_header_end_offset) < 8 { + return Ok(None); + } + + // The Rich header is located between the DOS header and the PE header. + let scan_start = dos_header_end_offset + 4; + let scan_end = pe_header_start_offset; + if scan_start > scan_end { + return Err(error::Error::Malformed(format!( + "Rich header scan start ({:#X}) is greater than scan end ({:#X})", + scan_start, scan_end + ))); + } + let scan_stub = &bytes[scan_start..scan_end]; + + // First locate the Rich marker and the subsequent 32-bit encryption key. + let (rich_end_offset, key) = match scan_stub + .windows(8) + .enumerate() + .filter_map( + |(index, window)| match window.pread_with::(0, scroll::LE) { + // Marker matches, then return its index + Ok(marker) if marker == RICH_MARKER => Some(Ok(index)), + // Error reading with scroll + Err(e) => Some(Err(error::Error::from(e))), + // Marker did not match + _ => None, + }, + ) + // Next is the very first element succeeded + .next() + { + Some(Ok(rich_end_offset)) => { + let rich_key = + scan_stub.pread_with::(rich_end_offset + RICH_MARKER_SIZE, scroll::LE)?; + (rich_end_offset, rich_key) + } + // Something went wrong, e.g., reading with scroll + Some(Err(e)) => return Err(e), + // Marker did not found, rich header is assumed it does not exist + None => return Ok(None), + }; + + // Ensure rich_end_offset is within bounds + if rich_end_offset >= scan_stub.len() { + return Err(error::Error::Malformed(format!( + "Rich end offset ({:#X}) exceeds scan stub length ({:#X})", + rich_end_offset, + scan_stub.len() + ))); + } + // Scope the buffer + let rich_header = &scan_stub[..rich_end_offset]; + + // Look for DanS marker + let rich_start_offset = match scan_stub + .windows(4) + .enumerate() + .filter_map( + |(index, window)| match window.pread_with::(0, scroll::LE) { + // If we do found the DanS marker, return the offset + Ok(value) if (value ^ key) == DANS_MARKER => Some(Ok(index + DANS_MARKER_SIZE)), + // This is scroll error, likely malformed rich header + Err(e) => Some(Err(error::Error::from(e))), + // No matching DanS marker found + _ => None, + }, + ) + // Next is the very first element succeeded + .next() + { + // Suceeded + Some(Ok(offset)) => offset, + // Errors such as from scroll reader + Some(Err(e)) => return Err(e), + // DanS marker did not found + None => { + return Err(error::Error::Malformed(format!( + "Rich header does not contain the DanS marker" + ))); + } + }; + + // Ensure rich_start_offset is within bounds + if rich_start_offset >= rich_header.len() { + return Err(error::Error::Malformed(format!( + "Rich start offset ({:#X}) exceeds rich header length ({:#X})", + rich_start_offset, + rich_header.len() + ))); + } + // Scope the buffer + let rich_header = &rich_header[rich_start_offset..]; + + // Skip padding bytes + let padding_size = rich_header + .chunks(4) + .map(|chunk| chunk.pread_with::(0, scroll::LE)) + .collect::, _>>()? + .into_iter() + .take_while(|value| value == &key) + .count() + * core::mem::size_of_val(&key); + + // Extract the Rich header data without the padding + let data = rich_header; + + // Subtract the sizeof DanS marker (u32, 4 bytes) + let start_offset = scan_start as u32 + rich_start_offset as u32 - DANS_MARKER_SIZE as u32; + let end_offset = scan_start as u32 + rich_end_offset as u32; + + Ok(Some(RichHeader { + key, + data, + padding_size, + start_offset, + end_offset, + })) + } + + /// Returns [`RichMetadataIterator`] iterator for [`RichMetadata`] + pub fn metadatas(&self) -> RichMetadataIterator<'a> { + RichMetadataIterator { + key: self.key, + data: &self.data[self.padding_size..], + } + } +} + /// The TE header is a reduced PE32/PE32+ header containing only fields /// required for execution in the Platform Initialization /// ([PI](https://uefi.org/specs/PI/1.8/V1_Introduction.html)) architecture. @@ -1031,7 +1352,11 @@ pub fn machine_to_str(machine: u16) -> &'static str { #[cfg(test)] mod tests { - use super::{machine_to_str, Header, COFF_MACHINE_X86, DOS_MAGIC, PE_MAGIC}; + use crate::{error, pe::header::DosStub}; + + use super::{ + machine_to_str, Header, RichHeader, RichMetadata, COFF_MACHINE_X86, DOS_MAGIC, PE_MAGIC, + }; const CRSS_HEADER: [u8; 688] = [ 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, @@ -1082,6 +1407,133 @@ mod tests { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + const NO_RICH_HEADER: [u8; 262] = [ + 0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, + 0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, + 0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, + 0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, + ]; + + const NO_RICH_HEADER_INVALID_PE_POINTER: [u8; 304] = [ + 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0xFF, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, + 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, + 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, + 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xC7, 0xEA, 0x07, 0xC9, 0xA6, 0x84, + 0x54, 0xC9, 0xA6, 0x84, 0x54, 0xC9, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x81, 0x55, 0xCB, 0xA6, + 0x84, 0x54, 0xC0, 0xDE, 0x17, 0x54, 0xC1, 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x80, 0x55, 0xC7, + 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x87, 0x55, 0xC1, 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x81, 0x55, + 0x7E, 0xA6, 0x84, 0x54, 0xB9, 0x27, 0x85, 0x55, 0xCA, 0xA6, 0x84, 0x54, 0xC9, 0xA6, 0x85, + 0x54, 0x08, 0xA6, 0x84, 0x54, 0xA5, 0xD2, 0x81, 0x55, 0xE8, 0xA6, 0x84, 0x54, 0xA5, 0xD2, + 0x80, 0x55, 0xD9, 0xA6, 0x84, 0x54, 0xA5, 0xD2, 0x87, 0x55, 0xC0, 0xA6, 0x84, 0x54, 0x10, + 0xD2, 0x80, 0x55, 0x49, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x84, 0x55, 0xC8, 0xA6, 0x84, 0x54, + 0x10, 0xD2, 0x7B, 0x54, 0xC8, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x86, 0x55, 0xC8, 0xA6, 0x84, + 0x54, 0x52, 0x69, 0x63, 0x68, 0xC9, 0xA6, 0x84, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x07, 0x00, 0xEC, 0xA5, 0x5B, 0x66, + 0x00, 0x00, 0x00, 0x00, + ]; + + const CORRECT_RICH_HEADER: [u8; 256] = [ + 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, + 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, + 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, + 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x4C, 0x5B, 0xB1, 0x37, 0x2D, 0x35, + 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x31, 0xE3, 0x3D, 0x2D, + 0x35, 0xE2, 0x44, 0x4F, 0x36, 0xE3, 0x32, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x30, 0xE3, 0x48, + 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x36, 0xE3, 0x3E, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x30, 0xE3, + 0x14, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x31, 0xE3, 0x25, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x34, + 0xE3, 0x3C, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x34, 0xE2, 0xAF, 0x2D, 0x35, 0xE2, 0x37, 0x2D, + 0x35, 0xE2, 0x23, 0x2D, 0x35, 0xE2, 0xFC, 0x4E, 0x37, 0xE3, 0x36, 0x2D, 0x35, 0xE2, 0x52, + 0x69, 0x63, 0x68, 0x37, 0x2D, 0x35, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05, + 0x00, + ]; + + const CORRUPTED_RICH_HEADER: [u8; 256] = [ + 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, + 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, + 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, + 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x4C, 0x5B, 0xB1, 0x37, 0x2D, 0x35, + 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x31, 0xE3, 0x3D, 0x2D, + 0x35, 0xE2, 0x44, 0x4F, 0x36, 0xE3, 0x32, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x30, 0xE3, 0x48, + 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x36, 0xE3, 0x3E, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x30, 0xE3, + 0x14, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x31, 0xE3, 0x25, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x34, + 0xE3, 0x3C, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x34, 0xE2, 0xAF, 0x2D, 0x35, 0xE2, 0x37, 0x2D, + 0x35, 0xE2, 0x23, 0x2D, 0x35, 0xE2, 0xFC, 0x4E, 0x37, 0xE3, 0x36, 0x2D, 0x35, 0xE2, 0x52, + 0x69, 0x63, 0x68, 0x37, 0x2D, 0x35, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05, + 0x00, + ]; + + const BORLAND_PE32_VALID_NO_RICH_HEADER: [u8; 528] = [ + 0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, + 0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, + 0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, + 0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // PE + 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x08, 0x00, 0xC0, 0x9C, 0x07, 0x67, 0x00, 0x00, 0x00, + 0x00, + ]; + #[test] fn crss_header() { let header = Header::parse(&&CRSS_HEADER[..]).unwrap(); @@ -1091,4 +1543,117 @@ mod tests { assert!(machine_to_str(header.coff_header.machine) == "X86"); println!("header: {:?}", &header); } + + #[test] + fn parse_without_dos() { + let header = Header::parse_without_dos(&BORLAND_PE32_VALID_NO_RICH_HEADER).unwrap(); + assert_eq!(header.dos_stub, DosStub::default()); + assert_eq!(header.rich_header.is_none(), true); + + // DOS stub is default but rich parser still works + let header = Header::parse_without_dos(&CORRECT_RICH_HEADER).unwrap(); + assert_eq!(header.dos_stub, DosStub::default()); + assert_eq!(header.rich_header.is_some(), true); + } + + #[test] + fn parse_borland_weird_dos_stub() { + let dos_stub = DosStub::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER, 0x200).unwrap(); + assert_ne!(dos_stub.data, BORLAND_PE32_VALID_NO_RICH_HEADER.to_vec()); + } + + #[test] + fn parse_borland_no_rich_header() { + let header = RichHeader::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER).unwrap(); + assert_eq!(header, None); + } + + #[test] + fn parse_no_rich_header() { + let header = RichHeader::parse(&NO_RICH_HEADER).unwrap(); + assert_eq!(header, None); + } + + #[test] + fn parse_no_rich_header_invalid_pe_pointer() { + let header = RichHeader::parse(&NO_RICH_HEADER_INVALID_PE_POINTER); + assert_eq!(header.is_err(), true); + if let Err(error::Error::Malformed(msg)) = header { + assert_eq!(msg, "cannot parse PE header signature (offset 0xff3c)"); + } else { + panic!("Expected a Malformed error but got {:?}", header); + } + } + + #[test] + fn parse_correct_rich_header() { + let header = RichHeader::parse(&CORRECT_RICH_HEADER).unwrap(); + assert_ne!(header, None); + let header = header.unwrap(); + let expected = vec![ + RichMetadata { + build: 25203, + product: 260, + use_count: 10, + }, + RichMetadata { + build: 25203, + product: 259, + use_count: 5, + }, + RichMetadata { + build: 25203, + product: 261, + use_count: 127, + }, + RichMetadata { + build: 25305, + product: 259, + use_count: 9, + }, + RichMetadata { + build: 25305, + product: 261, + use_count: 35, + }, + RichMetadata { + build: 25305, + product: 260, + use_count: 18, + }, + RichMetadata { + build: 25203, + product: 257, + use_count: 11, + }, + RichMetadata { + build: 0, + product: 1, + use_count: 152, + }, + RichMetadata { + build: 0, + product: 0, + use_count: 20, + }, + RichMetadata { + build: 25547, + product: 258, + use_count: 1, + }, + ]; + assert_eq!( + header + .metadatas() + .filter_map(Result::ok) + .collect::>(), + expected + ); + } + + #[test] + fn parse_corrupted_rich_header() { + let header_result = RichHeader::parse(&CORRUPTED_RICH_HEADER); + assert_eq!(header_result.is_err(), true); + } } diff --git a/src/pe/mod.rs b/src/pe/mod.rs index 3c8c3c3e..e921f2e2 100644 --- a/src/pe/mod.rs +++ b/src/pe/mod.rs @@ -45,7 +45,7 @@ pub struct PE<'a> { bytes: &'a [u8], authenticode_excluded_sections: Option, /// The PE header - pub header: header::Header, + pub header: header::Header<'a>, /// A list of the sections in this PE binary pub sections: Vec, /// The size of the binary @@ -438,6 +438,7 @@ impl<'a> ctx::TryIntoCtx for PE<'a> { } _ => None, }; + bytes.gwrite_with(self.header, &mut offset, ctx)?; max_offset = max(offset, max_offset); self.write_sections(bytes, &mut offset, file_alignment, ctx)?;