Skip to main content

goblin_experimental/pe/
header.rs

1use core::iter::FusedIterator;
2
3use crate::error;
4use crate::pe::{data_directories, debug, optional_header, section_table, symbol};
5use crate::strtab;
6use alloc::vec::Vec;
7use scroll::{ctx, IOread, IOwrite, Pread, Pwrite, SizeWith};
8
9/// In `winnt.h` and `pe.h`, it's `IMAGE_DOS_HEADER`. It's a DOS header present in all PE binaries.
10///
11/// The DOS header is a relic from the MS-DOS era. It used to be useful to display an
12/// error message if the binary is run in MS-DOS by utilizing the DOS stub.
13///
14/// Nowadays, only two fields from
15/// the DOS header are used on Windows: [`signature` (aka `e_magic`)](DosHeader::signature)
16/// and [`pe_pointer` (aka `e_lfanew`)](DosHeader::pe_pointer).
17///
18/// ## Position in a modern PE file
19///
20/// The DOS header is located at the beginning of the PE file and is usually followed by the [DosStub].
21///
22/// ## Note on the archaic "formatted header"
23///
24/// The subset of the structure spanning from its start to the [`overlay_number` (aka `e_ovno`)](DosHeader::overlay_number) field
25/// included (i.e. till the offset 0x1C) used to be commonly known as "formatted header", since their position and contents were
26/// fixed. Optional information used by overlay managers could have followed the formatted header. In the absence of optional
27/// information, the formatted header was followed by the ["relocation pointer table"](https://www.tavi.co.uk/phobos/exeformat.html#reloctable).
28///
29/// Overlays were sections of a program that remained on disk until the program actually required them. Different overlays
30/// could thus share the same memory area. The overlays were loaded and unloaded by special code provided by the program
31/// or its run-time library.
32///
33/// [Source](https://www.tavi.co.uk/phobos/exeformat.html#:~:text=Format%20of%20the%20.EXE%20file%20header).
34#[repr(C)]
35#[derive(Debug, PartialEq, Copy, Clone, Default, Pwrite)]
36#[doc(alias("IMAGE_DOS_HEADER"))]
37pub struct DosHeader {
38    /// Magic number: `[0x5A, 0x4D]`. In [little endian](https://en.wikipedia.org/wiki/Endianness)
39    /// [ASCII](https://en.wikipedia.org/wiki/ASCII), it reads "MZ" for [Mark Zbikowski](https://en.wikipedia.org/wiki/Mark_Zbikowski)).
40    ///
41    /// ## Non-MZ DOS executables
42    ///
43    /// * For [IBM OS/2](https://www.britannica.com/technology/IBM-OS-2), the value was "NE".
44    /// * For IBM OS/2 LE, the value was "LE".
45    /// * For [NT](https://en.wikipedia.org/wiki/Windows_NT), the value was "PE00".
46    ///
47    /// Sources:
48    ///
49    /// * <https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/>
50    /// * <https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/february/inside-windows-win32-portable-executable-file-format-in-detail>
51    #[doc(alias("e_magic"))]
52    pub signature: u16,
53    /// In `winnt.h` and `pe.h`, it's `e_cblp`.
54    ///
55    /// It used to specify the number of bytes actually used in the last "page".
56    /// Page used to refer to a segment of memory, usually of 512 bytes size.
57    ///
58    /// The case of full page was represented by 0x0000 (since the last page is never empty).
59    ///
60    /// For example, assuming a page size of 512 bytes, this value would
61    /// be 0x0000 for a 1024 byte file, and 0x0001 for a 1025 byte file
62    /// (since it only contains one valid byte).
63    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
64    ///
65    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
66    #[doc(alias("e_cblp"))]
67    pub bytes_on_last_page: u16,
68    /// In `winnt.h` and `pe.h`, it's `e_cp`.
69    ///
70    /// It used to specify the number of pages required to hold a file. For example,
71    /// if the file contained 1024 bytes, and the file had pages of a size of 512 bytes,
72    /// this [word](https://en.wikipedia.org/wiki/Word_(computer_architecture)) would contain
73    /// 0x0002 (2 pages); if the file contained 1025 bytes, this word would contain 0x0003 (3 pages).
74    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
75    ///
76    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
77    #[doc(alias("e_cp"))]
78    pub pages_in_file: u16,
79    /// In `winnt.h` and `pe.h`, it's `e_crlc`.
80    ///
81    /// It used to specify the number of "relocation items", i.e. the number of entries that
82    /// existed in the ["relocation pointer table"](https://www.tavi.co.uk/phobos/exeformat.html#reloctable).
83    /// If there were no relocations, this field would contain 0x0000.
84    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
85    ///
86    /// ## On relocation items and relocation pointer table
87    ///
88    /// When a program is compiled, memory addresses are often hard-coded into the binary code.
89    /// These addresses are usually relative to the base address where the program expects to be loaded into memory.
90    /// However, when the program is loaded into memory, it might not be loaded at its preferred base address due to
91    /// various reasons such as memory fragmentation or other programs already occupying that space.
92    ///
93    /// Relocation items, also known as fixups or relocations, are pieces of data embedded within the executable file
94    /// that indicate which memory addresses need to be adjusted when the program is loaded at a different base address.
95    /// These relocations specify the location and type of adjustment needed.
96    ///
97    /// The relocation pointer table is a data structure that contains pointers to the locations within the executable file
98    /// where relocations need to be applied. It allows the operating system's loader to efficiently locate and process the
99    /// relocation data during the loading process.
100    ///
101    /// ---
102    ///
103    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
104    #[doc(alias("e_crlc"))]
105    pub relocations: u16,
106    /// In `winnt.h` and `pe.h`, it's `e_cparhdr`.
107    ///
108    /// It used to specify the size of the "executable header" in terms of "paragraphs" (16 byte chunks). It used to indicate
109    /// the offset of the program's compiled/assembled and linked image (the [load module](https://www.tavi.co.uk/phobos/exeformat.html#loadmodule)) within the executable file. The size
110    /// of the load module could have been deduced by substructing this value (converted to bytes) from the overall size that could
111    /// have been derived from combining the value of [`pages_in_file` (aka `e_cp`)](DosHeader::pages_in_file) and the value of
112    /// [`bytes_on_last_page` (aka `e_cblp)`](DosHeader::bytes_on_last_page). The header used to always span an even number of
113    /// paragraphs.
114    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
115    ///
116    /// The "executable header" in this context refers to the DOS header itself.
117    ///
118    /// Typically, this field is set to 4. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
119    /// This is because the modern DOS header is 64 bytes long, and 64 / 16 = 4.
120    #[doc(alias("e_cparhdr"))]
121    pub size_of_header_in_paragraphs: u16,
122    /// In `winnt.h` and `pe.h`, it's `e_minalloc`.
123    ///
124    /// It used to specify the minimum number of extra paragraphs needed to be allocated to begin execution. This is
125    /// **in addition** to the memory required to hold the [load module](https://www.tavi.co.uk/phobos/exeformat.html#loadmodule). This value normally represented the total size
126    /// of any uninitialized data and/or stack segments that were linked at the end of the program. This space was not
127    /// directly included in the load module, since there were no particular initializing values and it would simply waste
128    /// disk space.
129    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
130    ///
131    /// If both the [`minimum_extra_paragraphs_needed` (aka `e_minalloc`)](DosHeader::minimum_extra_paragraphs_needed) and
132    /// [`maximum_extra_paragraphs_needed` (aka `e_maxalloc`)](DosHeader::maximum_extra_paragraphs_needed) fields were set to 0x0000,
133    /// the program would be allocated as much memory as available. [Source](https://www.tavi.co.uk/phobos/exeformat.html)
134    ///
135    /// Typically, this field is set to 0x10. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
136    #[doc(alias("e_minalloc"))]
137    pub minimum_extra_paragraphs_needed: u16,
138    /// In `winnt.h` and `pe.h`, it's `e_maxalloc`.
139    ///
140    /// It used to specify the maximum number of extra paragraphs needed to be allocated by to begin execution. This indicated
141    /// **additional** memory over and above that required by the [load module](https://www.tavi.co.uk/phobos/exeformat.html#loadmodule) and the value specified in
142    /// [`minimum_extra_paragraphs_needed` (aka `e_minalloc`)](DosHeader::minimum_extra_paragraphs_needed).
143    /// If the request could not be satisfied, the program would be allocated as much memory as available.
144    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
145    ///
146    /// If both the [`minimum_extra_paragraphs_needed` (aka `e_minalloc`)](DosHeader::minimum_extra_paragraphs_needed) and
147    /// [`maximum_extra_paragraphs_needed` (aka `e_maxalloc`)](DosHeader::maximum_extra_paragraphs_needed) fields were set to 0x0000,
148    /// the program would be allocated as much memory as available. [Source](https://www.tavi.co.uk/phobos/exeformat.html)
149    ///
150    /// Typically, this field is set to 0xFFFF. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
151    #[doc(alias("e_maxalloc"))]
152    pub maximum_extra_paragraphs_needed: u16,
153    /// In `winnt.h` and `pe.h`, it's `e_ss`.
154    ///
155    /// It used to specify the initial SS ("stack segment") value. SS value was a paragraph address of the stack segment
156    /// relative to the start of the [load module](https://www.tavi.co.uk/phobos/exeformat.html#loadmodule). At load time, the value was relocated by adding the address of the
157    /// start segment of the program to it, and the resulting value was placed in the SS register before the program is
158    /// started. To read more about x86 memory segmentation and SS register, see the
159    /// [wikipedia article](https://en.wikipedia.org/wiki/X86_memory_segmentation) on this topic. In DOS, the start segment
160    /// boundary of the program was the first segment boundary in memory after
161    /// [Program Segment Prefix (PSP)](https://en.wikipedia.org/wiki/Program_Segment_Prefix).
162    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
163    ///
164    /// The Program Segment Prefix (PSP) was a data structure used in DOS (Disk Operating System) environments.
165    /// It was located at the beginning of the memory allocated for a running program and it contained various
166    /// pieces of information about the program, including command-line arguments, environment variables,
167    /// and pointers to various system resources.
168    ///
169    /// [According to Wikipedia](https://en.wikipedia.org/wiki/Data_segment#Stack), the stack segment contains the call stack,
170    /// a LIFO structure, typically located in the higher parts of memory. A "stack pointer" register tracks the top of the
171    /// stack; it is adjusted each time a value is "pushed" onto the stack. The set of values pushed for one function call
172    /// is termed a "stack frame".
173    ///
174    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
175    #[doc(alias("e_ss"))]
176    pub initial_relative_ss: u16,
177    /// In `winnt.h` and `pe.h`, it's `e_sp`.
178    ///
179    /// It used to specify the initial SP ("stack pointer") value. SP value was the absolute value that must have been loaded
180    /// into the SP register before the program is given control. Since the actual stack segment was determined by the loader,
181    /// and this was merely a value within that segment, it didn't need to be relocated.
182    ///
183    /// [According to Wikipedia](https://en.wikipedia.org/wiki/Data_segment#Stack), the stack segment contains the call stack,
184    /// a LIFO structure, typically located in the higher parts of memory. A "stack pointer" register tracks the top of the
185    /// stack; it is adjusted each time a value is "pushed" onto the stack. The set of values pushed for one function call
186    /// is termed a "stack frame".
187    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
188    ///
189    /// Typically, this field is set to 0xB8. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
190    // TODO: Clarify what exactly is meany by "this was merely a value within that segment".
191    #[doc(alias("e_sp"))]
192    pub initial_sp: u16,
193    /// In `winnt.h` and `pe.h`, it's `e_csum`.
194    ///
195    /// It used to specify the checksum of the contents of the executable file It used to ensure the integrity of the data
196    /// within the file. For full details on how this checksum was calculated, see <http://www.tavi.co.uk/phobos/exeformat.html#checksum>.
197    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
198    ///
199    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
200    #[doc(alias("e_csum"))]
201    pub checksum: u16,
202    /// In `winnt.h` and `pe.h`, it's `e_ip`.
203    ///
204    /// It used to specify the initial IP ("instruction pointer") value. IP value was the absolute value that must have been
205    /// loaded into the IP register in order to transfer control to the program. Since the actual code segment was determined
206    /// by the loader and, and this was merely a value within that segment, it didn't need to be relocated.
207    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
208    ///
209    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
210    // TODO: Clarify what exactly is meany by "this was merely a value within that segment".
211    #[doc(alias("e_ip"))]
212    pub initial_ip: u16,
213    /// In `winnt.h` and `pe.h`, it's `e_cs`.
214    ///
215    /// It used to specify the pre-relocated initial CS ("code segment") value relative to the start of the [load module](https://www.tavi.co.uk/phobos/exeformat.html#loadmodule),
216    /// that should have been placed in the CS register in order to transfer control to the program. At load time, this value
217    /// was relocated by adding the address of the start segment of the program to it, and the resulting value was placed in
218    /// the CS register when control is transferred.
219    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
220    ///
221    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
222    #[doc(alias("e_cs"))]
223    pub initial_relative_cs: u16,
224    /// In `winnt.h` and `pe.h`, it's `e_lfarlc`.
225    ///
226    /// It used to specify the logical file address of the relocation table, or more specifically, the offset from the start
227    /// of the file to the [relocation pointer table](https://www.tavi.co.uk/phobos/exeformat.html#reloctable). This value
228    /// must have been used to locate the relocation table (rather than assuming a fixed location) because variable-length
229    /// information pertaining to program overlays could have occurred before this table, causing its position to vary.
230    /// A value of 0x40 in this field generally indicated a different kind of executable, not a DOS 'MZ' type.
231    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
232    ///
233    /// Typically, this field is set to 0x40. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
234    #[doc(alias("e_lfarlc"))]
235    pub file_address_of_relocation_table: u16,
236    /// In `winnt.h` and `pe.h`, it's `e_ovno`.
237    ///
238    /// It used to specify the overlay number, which was normally set to 0x0000, because few programs actually had overlays.
239    /// It changed only in files containing programs that used overlays.
240    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
241    ///
242    /// Overlays were sections of a program that remained on disk until the program actually required them. Different overlays
243    /// could thus share the same memory area. The overlays were loaded and unloaded by special code provided by the program
244    /// or its run-time library.
245    ///
246    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
247    #[doc(alias("e_ovno"))]
248    pub overlay_number: u16,
249    /// In `winnt.h` and `pe.h`, it's `e_res[4]`.
250    ///
251    /// It used to specify the reserved words for the program, i.e. an array reserved for future use.
252    /// Usually, the array was zeroed by the linker.
253    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
254    ///
255    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
256    #[doc(alias("e_res"))]
257    pub reserved: [u16; 4],
258    /// In `winnt.h` and `pe.h`, it's `e_oemid`.
259    ///
260    /// It used to specify the identifier for the OEM ("Original Equipment Manufacturer") for [`oem_info` aka `e_oeminfo`](DosHeader::oem_info).
261    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
262    ///
263    /// More specifically, it used to specify the OEM of the system or hardware platform for which the executable file was created.
264    /// This field was used to specify certain characteristics or requirements related to the hardware environment in which the
265    /// executable was intended to run.
266    ///
267    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
268    #[doc(alias("e_oemid"))]
269    pub oem_id: u16,
270    /// In `winnt.h` and `pe.h`, it's `e_oeminfo`.
271    ///
272    /// It used to specify the extra information, the kind of which was specific to the OEM identified by [`oem_id` aka `e_oemid`](DosHeader::oem_id).
273    #[doc(alias("e_oeminfo"))]
274    pub oem_info: u16,
275    /// In `winnt.h` and `pe.h`, it's `e_res2[10]`.
276    ///
277    /// It used to specify the reserved words for the program, i.e. an array reserved for future use.
278    /// Usually, the array was zeroed by the linker.
279    /// [Source](https://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/).
280    ///
281    /// Typically, this field is set to 0. [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/dos-header/).
282    #[doc(alias("e_res2"))]
283    pub reserved2: [u16; 10],
284    /// In `winnt.h` and `pe.h`, it's `e_lfanew`.
285    ///
286    /// Today, it specifies the logcal file address of the of the new exe header. In particular, it is a 4-byte offset into
287    /// the file where the PE file header is located. It is necessary to use this offset to locate the PE header in the file.
288    ///
289    /// Typically, this field is set to 0x3c ([`PE_POINTER_OFFSET`]).
290    #[doc(alias("e_lfanew"))]
291    pub pe_pointer: u32,
292}
293
294#[doc(alias("IMAGE_DOS_SIGNATURE"))]
295pub const DOS_MAGIC: u16 = 0x5a4d;
296pub const PE_POINTER_OFFSET: u32 = 0x3c;
297pub const DOS_STUB_OFFSET: u32 = PE_POINTER_OFFSET + (core::mem::size_of::<u32>() as u32);
298
299impl DosHeader {
300    pub fn parse(bytes: &[u8]) -> error::Result<Self> {
301        let mut offset = 0;
302        let signature = bytes.gread_with(&mut offset, scroll::LE).map_err(|_| {
303            error::Error::Malformed(format!("cannot parse DOS signature (offset {:#x})", 0))
304        })?;
305        if signature != DOS_MAGIC {
306            return Err(error::Error::Malformed(format!(
307                "DOS header is malformed (signature {:#x})",
308                signature
309            )));
310        }
311
312        let bytes_on_last_page = bytes.gread_with(&mut offset, scroll::LE)?;
313        let pages_in_file = bytes.gread_with(&mut offset, scroll::LE)?;
314        let relocations = bytes.gread_with(&mut offset, scroll::LE)?;
315        let size_of_header_in_paragraphs = bytes.gread_with(&mut offset, scroll::LE)?;
316        let minimum_extra_paragraphs_needed = bytes.gread_with(&mut offset, scroll::LE)?;
317        let maximum_extra_paragraphs_needed = bytes.gread_with(&mut offset, scroll::LE)?;
318        let initial_relative_ss = bytes.gread_with(&mut offset, scroll::LE)?;
319        let initial_sp = bytes.gread_with(&mut offset, scroll::LE)?;
320        let checksum = bytes.gread_with(&mut offset, scroll::LE)?;
321        let initial_ip = bytes.gread_with(&mut offset, scroll::LE)?;
322        let initial_relative_cs = bytes.gread_with(&mut offset, scroll::LE)?;
323        let file_address_of_relocation_table = bytes.gread_with(&mut offset, scroll::LE)?;
324        let overlay_number = bytes.gread_with(&mut offset, scroll::LE)?;
325        let reserved = bytes.gread_with(&mut offset, scroll::LE)?; // 4
326        let oem_id = bytes.gread_with(&mut offset, scroll::LE)?;
327        let oem_info = bytes.gread_with(&mut offset, scroll::LE)?;
328        let reserved2 = bytes.gread_with(&mut offset, scroll::LE)?; // 10
329
330        debug_assert!(
331            offset == PE_POINTER_OFFSET as usize,
332            "expected offset ({:#x}) after reading DOS header to be at 0x3C",
333            offset
334        );
335
336        let pe_pointer: u32 = bytes
337            .pread_with(PE_POINTER_OFFSET as usize, scroll::LE)
338            .map_err(|_| {
339                error::Error::Malformed(format!(
340                    "cannot parse PE header pointer (offset {:#x})",
341                    PE_POINTER_OFFSET
342                ))
343            })?;
344
345        let pe_signature: u32 =
346            bytes
347                .pread_with(pe_pointer as usize, scroll::LE)
348                .map_err(|_| {
349                    error::Error::Malformed(format!(
350                        "cannot parse PE header signature (offset {:#x})",
351                        pe_pointer
352                    ))
353                })?;
354        if pe_signature != PE_MAGIC {
355            return Err(error::Error::Malformed(format!(
356                "PE header is malformed (signature {:#x})",
357                pe_signature
358            )));
359        }
360
361        Ok(DosHeader {
362            signature,
363            bytes_on_last_page,
364            pages_in_file,
365            relocations,
366            size_of_header_in_paragraphs,
367            minimum_extra_paragraphs_needed,
368            maximum_extra_paragraphs_needed,
369            initial_relative_ss,
370            initial_sp,
371            checksum,
372            initial_ip,
373            initial_relative_cs,
374            file_address_of_relocation_table,
375            overlay_number,
376            reserved,
377            oem_id,
378            oem_info,
379            reserved2,
380            pe_pointer,
381        })
382    }
383}
384
385#[derive(Debug, PartialEq, Copy, Clone)]
386/// 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.
387///
388/// ## Position in a modern PE file
389///
390/// The [DosStub] is usually located immediately after the [DosHeader] and...
391///
392/// * De facto, can be followed by a non-standard ["Rich header"](https://0xrick.github.io/win-internals/pe3/#rich-header).
393/// * According to the standard, is followed by the  [Header::signature] and then the [CoffHeader].
394pub struct DosStub<'a> {
395    pub data: &'a [u8],
396}
397impl<'a> Default for DosStub<'a> {
398    /// This is the very basic DOS program bytecode representation embedded in MSVC linker.
399    ///
400    /// An equivalent (Not a equal) DOS program can be follows:
401    ///
402    /// ```asm
403    ///     push cs           ; 0E         Push the code segment onto the stack
404    ///     pop ds            ; 1F         Pop the top of the stack into the data segment register
405    ///
406    /// _start:
407    ///     mov dx, aMessage  ; BA 0E 00   Load the address of the message to the DX register
408    ///     mov ah, 09h       ; B4 09      DOS function 09h (display string) to print the message at DS:DX
409    ///     int 21h           ; CD 21      Call DOS interrupt 21h for displaying the message
410    ///
411    ///     mov ax, 4C01h     ; B8 01 4C   DOS function 4Ch (terminate program) with return code 1
412    ///     int 21h           ; CD 21      Call DOS interrupt 21h for program termination
413    ///
414    /// aMessage db 'This program cannot be run in DOS mode.'
415    /// ```
416    #[rustfmt::skip]
417    fn default() -> Self {
418        Self {
419            data: &[
420                0x0E,                   // push cs: Setup segment registers
421                0x1F,                   // pop ds: Setup segment registers
422                0xBA, 0x0E, 0x00,       // mov dx, 0x000E: Load the message address into the DX register
423                0xB4, 0x09,             // mov ah, 0x09: DOS function to print a string
424                0xCD, 0x21,             // int 0x21: Trigger DOS interrupt 21h to print the message
425                0xB8, 0x01, 0x4C,       // mov ax, 0x4C01: Prepare to terminate the program (DOS function 4Ch)
426                0xCD, 0x21,             // int 0x21: Trigger DOS interrupt 21h to terminate the program
427                0x54, 0x68, 0x69, 0x73, // "This" ASCII string "This program cannot be run in DOS mode."
428                0x20, 0x70, 0x72, 0x6F, // " pro" Continuation of the ASCII string,
429                0x67, 0x72, 0x61, 0x6D, // "gram" Continuation of the ASCII string,
430                0x20, 0x63, 0x61, 0x6E, // " can" Continuation of the ASCII string,
431                0x6E, 0x6F, 0x74, 0x20, // "not " Continuation of the ASCII string,
432                0x62, 0x65, 0x20, 0x72, // "be r" Continuation of the ASCII string,
433                0x75, 0x6E, 0x20, 0x69, // "un i" Continuation of the ASCII string,
434                0x6E, 0x20, 0x44, 0x4F, // "n DO" Continuation of the ASCII string,
435                0x53, 0x20, 0x6D, 0x6F, // "S mo" Continuation of the ASCII string,
436                0x64, 0x65, 0x2E,       // "DE." Continuation of the ASCII string, ending with a period.
437                0x0D, 0x0D, 0x0A,       // Carriage return (CR `0x0D, 0x0D`) and line feed (LF `0x0A`)
438                0x24,                   // '$' (End of string marker for DOS function 09h)
439                0x00, 0x00, 0x00, 0x00, // Padding bytes (8-byte alignment)
440                0x00, 0x00, 0x00,       // Padding bytes (8-byte alignment)
441            ],
442        }
443    }
444}
445impl<'a> ctx::TryIntoCtx<scroll::Endian> for DosStub<'a> {
446    type Error = error::Error;
447
448    fn try_into_ctx(self, bytes: &mut [u8], _: scroll::Endian) -> Result<usize, Self::Error> {
449        let offset = &mut 0;
450        bytes.gwrite_with(&*self.data, offset, ())?;
451        Ok(*offset)
452    }
453}
454
455impl<'a> DosStub<'a> {
456    /// Parse the DOS stub.
457    ///
458    /// The DOS stub is a small program that prints the message "This program cannot be run in DOS mode" and exits; and
459    /// is not really read for the PECOFF file format. It's a relic from the MS-DOS era.
460    pub fn parse(bytes: &'a [u8], pe_pointer: u32) -> error::Result<Self> {
461        let start_offset = DOS_STUB_OFFSET as usize;
462        let end_offset = pe_pointer as usize;
463
464        // Check if the end_offset is less than or equal to start_offset
465        if end_offset <= start_offset {
466            return Err(error::Error::Malformed(format!(
467                "PE pointer ({:#x}) must be greater than the start offset ({:#x})",
468                pe_pointer, start_offset
469            )));
470        }
471
472        if bytes.len() < end_offset as usize {
473            return Err(error::Error::Malformed(format!(
474                "DOS stub is too short ({} bytes) to contain the PE header pointer ({:#x})",
475                bytes.len(),
476                end_offset
477            )));
478        }
479
480        let dos_stub_area = &bytes[start_offset..end_offset];
481        Ok(Self {
482            data: dos_stub_area,
483        })
484    }
485}
486
487/// In `winnt.h`, it's `IMAGE_FILE_HEADER`. COFF Header.
488///
489/// Together with the [Header::signature] and the [Header::optional_header], it forms the
490/// [`IMAGE_NT_HEADERS`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_nt_headers32).
491///
492/// ## Position in a modern PE file
493///
494/// The COFF header is located after the [Header::signature], which in turn is located after the
495/// non-standard ["Rich header"](https://0xrick.github.io/win-internals/pe3/#rich-header), if present,
496/// and after the [DosStub], according to the standard.
497///
498/// COFF header is followed by the [Header::optional_header].
499#[repr(C)]
500#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)]
501#[doc(alias("IMAGE_FILE_HEADER"))]
502pub struct CoffHeader {
503    /// The architecture type of the computer. An image file can only be run
504    /// on the specified computer or a system that emulates the specified computer.
505    ///
506    /// Can be one of the following values:
507    ///
508    /// * [`COFF_MACHINE_UNKNOWN`],
509    /// * [`COFF_MACHINE_ALPHA`],
510    /// * [`COFF_MACHINE_ALPHA64`],
511    /// * [`COFF_MACHINE_AM33`],
512    /// * [`COFF_MACHINE_X86_64`],
513    /// * [`COFF_MACHINE_ARM`],
514    /// * [`COFF_MACHINE_ARM64`],
515    /// * [`COFF_MACHINE_ARMNT`],
516    /// * [`COFF_MACHINE_EBC`],
517    /// * [`COFF_MACHINE_X86`],
518    /// * [`COFF_MACHINE_IA64`],
519    /// * [`COFF_MACHINE_LOONGARCH32`],
520    /// * [`COFF_MACHINE_LOONGARCH64`],
521    /// * [`COFF_MACHINE_M32R`],
522    /// * [`COFF_MACHINE_MIPS16`],
523    /// * [`COFF_MACHINE_MIPSFPU`],
524    /// * [`COFF_MACHINE_MIPSFPU16`],
525    /// * [`COFF_MACHINE_POWERPC`],
526    /// * [`COFF_MACHINE_POWERPCFP`],
527    /// * [`COFF_MACHINE_R4000`],
528    /// * [`COFF_MACHINE_RISCV32`],
529    /// * [`COFF_MACHINE_RISCV64`],
530    /// * [`COFF_MACHINE_RISCV128`],
531    /// * [`COFF_MACHINE_SH3`],
532    /// * [`COFF_MACHINE_SH3DSP`],
533    /// * [`COFF_MACHINE_SH4`],
534    /// * [`COFF_MACHINE_SH5`],
535    /// * [`COFF_MACHINE_THUMB`],
536    /// * [`COFF_MACHINE_WCEMIPSV2`],
537    ///
538    /// or any other value that is not listed here.
539    ///
540    /// The constants above are sourced from <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types>.
541    /// If there's a missing constant, please open an issue or a pull request.
542    // TODO: insert the values names with a macro
543    #[doc(alias("Machine"))]
544    pub machine: u16,
545    /// The number of sections. This indicates the size of the section table, which immediately follows the headers.
546    /// Note that the Windows loader limits the number of sections to 96.
547    /// [Source](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_file_header).
548    #[doc(alias("NumberOfSections"))]
549    pub number_of_sections: u16,
550    /// The low 32 bits of the time stamp of the image. This represents the date and time the image was created by the linker.
551    /// The value is represented in the number of seconds elapsed since midnight (00:00:00), January 1, 1970, Universal
552    /// Coordinated Time, according to the system clock.
553    #[doc(alias("TimeDateStamp"))]
554    pub time_date_stamp: u32,
555    /// The offset of the symbol table, in bytes, or zero if no COFF symbol table exists.
556    ///
557    /// Typically, this field is set to 0 because COFF debugging information is deprecated.
558    /// [Source](https://0xrick.github.io/win-internals/pe4/#file-header-image_file_header).
559    // TODO: further explain the COFF symbol table. This seems to be a nuanced topic.
560    #[doc(alias("PointerToSymbolTable"))]
561    pub pointer_to_symbol_table: u32,
562    /// The number of symbols in the symbol table.
563    ///
564    /// Typically, this field is set to 0 because COFF debugging information is deprecated.
565    /// [Source](https://0xrick.github.io/win-internals/pe4/#file-header-image_file_header).
566    // Q (JohnScience): Why is the name `number_of_symbol_table` and not `number_of_symbols`?
567    #[doc(alias("NumberOfSymbols"))]
568    pub number_of_symbol_table: u32,
569    /// The size of the optional header, in bytes. This value should be zero for object files.
570    ///
571    /// The [`goblin::pe::optional_header::OptionalHeader`](crate::pe::optional_header::OptionalHeader) is meant to
572    /// represent either the 32-bit or the 64-bit optional header. The size of the optional header is used to determine
573    /// which one it is.
574    #[doc(alias("SizeOfOptionalHeader"))]
575    pub size_of_optional_header: u16,
576    /// The [characteristics](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics) of the image.
577    ///
578    /// The constants for the characteristics are available in the [`goblin::pe::characteristic`](crate::pe::characteristic) module.
579    #[doc(alias("Characteristics"))]
580    pub characteristics: u16,
581}
582
583pub const SIZEOF_COFF_HEADER: usize = 20;
584/// PE\0\0, little endian
585pub const PE_MAGIC: u32 = 0x0000_4550;
586pub const SIZEOF_PE_MAGIC: usize = 4;
587
588// Q (JohnScience): doesn't it make sense to move all these constants to a dedicated module
589// and then re-export them from here? This way, the module will be more organized.
590//
591// Also, don't we want to declare them in a macro to remove the boilerplate and make the implementation
592// of `machine_to_str` more future-proof and concise? For example, addition of...
593//
594// * `IMAGE_FILE_MACHINE_LOONGARCH32`,
595// * `IMAGE_FILE_MACHINE_LOONGARCH64`,
596// * `IMAGE_FILE_MACHINE_ALPHA`,
597// * `IMAGE_FILE_MACHINE_ALPHA64`
598//
599// didn't trigger the exhaustiveness check because there was a necessary default case.
600//
601// This way, we can also generate a test that would parse <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types>
602// and check that there are no missing constants.
603
604/// The contents of this field are assumed to be applicable to any machine type.
605///
606/// One of the possible values for [`CoffHeader::machine`].
607#[doc(alias("IMAGE_FILE_MACHINE_UNKNOWN"))]
608pub const COFF_MACHINE_UNKNOWN: u16 = 0x0;
609
610/// Alpha AXP, 32-bit address space.
611///
612/// One of the possible values for [`CoffHeader::machine`].
613#[doc(alias("IMAGE_FILE_MACHINE_ALPHA"))]
614pub const COFF_MACHINE_ALPHA: u16 = 0x184;
615
616/// Alpha AXP, 64-bit address space.
617///
618/// One of the possible values for [`CoffHeader::machine`].
619#[doc(alias("IMAGE_FILE_MACHINE_ALPHA64"))]
620#[doc(alias("IMAGE_FILE_MACHINE_AXP64"))]
621pub const COFF_MACHINE_ALPHA64: u16 = 0x284;
622
623/// Matsushita AM33.
624///
625/// One of the possible values for [`CoffHeader::machine`].
626#[doc(alias("IMAGE_FILE_MACHINE_AM33"))]
627pub const COFF_MACHINE_AM33: u16 = 0x1d3;
628
629/// x64 aka amd64.
630///
631/// One of the possible values for [`CoffHeader::machine`].
632#[doc(alias("IMAGE_FILE_MACHINE_AMD64"))]
633// Q (JohnScience): why is this `COFF_MACHINE_X86_64` and not `COFF_MACHINE_AMD64`?
634// Should we deprecate the former and use the latter instead?
635pub const COFF_MACHINE_X86_64: u16 = 0x8664;
636
637/// ARM little endian.
638///
639/// One of the possible values for [`CoffHeader::machine`].
640#[doc(alias("IMAGE_FILE_MACHINE_ARM"))]
641pub const COFF_MACHINE_ARM: u16 = 0x1c0;
642
643/// ARM64 little endian.
644///
645/// One of the possible values for [`CoffHeader::machine`].
646#[doc(alias("IMAGE_FILE_MACHINE_ARM64"))]
647pub const COFF_MACHINE_ARM64: u16 = 0xaa64;
648
649/// ARM Thumb-2 little endian.
650///
651/// One of the possible values for [`CoffHeader::machine`].
652#[doc(alias("IMAGE_FILE_MACHINE_ARMNT"))]
653pub const COFF_MACHINE_ARMNT: u16 = 0x1c4;
654
655/// EFI byte code.
656///
657/// One of the possible values for [`CoffHeader::machine`].
658#[doc(alias("IMAGE_FILE_MACHINE_EBC"))]
659pub const COFF_MACHINE_EBC: u16 = 0xebc;
660
661/// Intel 386 or later processors and compatible processors.
662///
663/// One of the possible values for [`CoffHeader::machine`].
664// Q (JohnScience): why is this `COFF_MACHINE_X86` and not `COFF_MACHINE_I386`?
665// Should we deprecate the former and use the latter instead?
666#[doc(alias("IMAGE_FILE_MACHINE_I386"))]
667pub const COFF_MACHINE_X86: u16 = 0x14c;
668
669/// Intel Itanium processor family.
670///
671/// One of the possible values for [`CoffHeader::machine`].
672#[doc(alias("IMAGE_FILE_MACHINE_IA64"))]
673pub const COFF_MACHINE_IA64: u16 = 0x200;
674
675/// LoongArch 32-bit processor family.
676///
677/// One of the possible values for [`CoffHeader::machine`].
678#[doc(alias("IMAGE_FILE_MACHINE_LOONGARCH32"))]
679pub const COFF_MACHINE_LOONGARCH32: u16 = 0x6232;
680
681/// LoongArch 64-bit processor family.
682///
683/// One of the possible values for [`CoffHeader::machine`].
684#[doc(alias("IMAGE_FILE_MACHINE_LOONGARCH64"))]
685pub const COFF_MACHINE_LOONGARCH64: u16 = 0x6264;
686
687/// Mitsubishi M32R little endian.
688///
689/// One of the possible values for [`CoffHeader::machine`].
690#[doc(alias("IMAGE_FILE_MACHINE_M32R"))]
691pub const COFF_MACHINE_M32R: u16 = 0x9041;
692
693/// MIPS16.
694///
695/// One of the possible values for [`CoffHeader::machine`].
696#[doc(alias("IMAGE_FILE_MACHINE_MIPS16"))]
697pub const COFF_MACHINE_MIPS16: u16 = 0x266;
698
699/// MIPS with FPU.
700///
701/// One of the possible values for [`CoffHeader::machine`].
702#[doc(alias("IMAGE_FILE_MACHINE_MIPSFPU"))]
703pub const COFF_MACHINE_MIPSFPU: u16 = 0x366;
704
705/// MIPS16 with FPU.
706///
707/// One of the possible values for [`CoffHeader::machine`].
708#[doc(alias("IMAGE_FILE_MACHINE_MIPSFPU16"))]
709pub const COFF_MACHINE_MIPSFPU16: u16 = 0x466;
710
711/// Power PC little endian.
712///
713/// One of the possible values for [`CoffHeader::machine`].
714#[doc(alias("IMAGE_FILE_MACHINE_POWERPC"))]
715pub const COFF_MACHINE_POWERPC: u16 = 0x1f0;
716
717/// Power PC with floating point support.
718///
719/// One of the possible values for [`CoffHeader::machine`].
720#[doc(alias("IMAGE_FILE_MACHINE_POWERPCFP"))]
721pub const COFF_MACHINE_POWERPCFP: u16 = 0x1f1;
722
723/// MIPS little endian.
724///
725/// One of the possible values for [`CoffHeader::machine`].
726#[doc(alias("IMAGE_FILE_MACHINE_R4000"))]
727pub const COFF_MACHINE_R4000: u16 = 0x166;
728
729/// RISC-V 32-bit address space.
730///
731/// One of the possible values for [`CoffHeader::machine`].
732#[doc(alias("IMAGE_FILE_MACHINE_RISCV32"))]
733pub const COFF_MACHINE_RISCV32: u16 = 0x5032;
734
735/// RISC-V 64-bit address space.
736///
737/// One of the possible values for [`CoffHeader::machine`].
738#[doc(alias("IMAGE_FILE_MACHINE_RISCV64"))]
739pub const COFF_MACHINE_RISCV64: u16 = 0x5064;
740
741/// RISC-V 128-bit address space
742///
743/// One of the possible values for [`CoffHeader::machine`].
744#[doc(alias("IMAGE_FILE_MACHINE_RISCV128"))]
745pub const COFF_MACHINE_RISCV128: u16 = 0x5128;
746
747/// Hitachi SH3.
748///
749/// One of the possible values for [`CoffHeader::machine`].
750#[doc(alias("IMAGE_FILE_MACHINE_SH3"))]
751pub const COFF_MACHINE_SH3: u16 = 0x1a2;
752
753/// Hitachi SH3 DSP.
754///
755/// One of the possible values for [`CoffHeader::machine`].
756#[doc(alias("IMAGE_FILE_MACHINE_SH3DSP"))]
757pub const COFF_MACHINE_SH3DSP: u16 = 0x1a3;
758
759/// Hitachi SH4.
760///
761/// One of the possible values for [`CoffHeader::machine`].
762#[doc(alias("IMAGE_FILE_MACHINE_SH4"))]
763pub const COFF_MACHINE_SH4: u16 = 0x1a6;
764
765/// Hitachi SH5.
766///
767/// One of the possible values for [`CoffHeader::machine`].
768#[doc(alias("IMAGE_FILE_MACHINE_SH5"))]
769pub const COFF_MACHINE_SH5: u16 = 0x1a8;
770
771/// Thumb.
772///
773/// One of the possible values for [`CoffHeader::machine`].
774#[doc(alias("IMAGE_FILE_MACHINE_THUMB"))]
775pub const COFF_MACHINE_THUMB: u16 = 0x1c2;
776
777/// MIPS little-endian WCE v2.
778///
779/// One of the possible values for [`CoffHeader::machine`].
780#[doc(alias("IMAGE_FILE_MACHINE_WCEMIPSV2"))]
781pub const COFF_MACHINE_WCEMIPSV2: u16 = 0x169;
782
783impl CoffHeader {
784    pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> {
785        Ok(bytes.gread_with(offset, scroll::LE)?)
786    }
787
788    /// Parse the COFF section headers.
789    ///
790    /// For COFF, these immediately follow the COFF header. For PE, these immediately follow the
791    /// optional header.
792    pub fn sections(
793        &self,
794        bytes: &[u8],
795        offset: &mut usize,
796    ) -> error::Result<Vec<section_table::SectionTable>> {
797        let nsections = self.number_of_sections as usize;
798
799        // a section table is at least 40 bytes
800        if nsections > bytes.len() / 40 {
801            return Err(error::Error::BufferTooShort(nsections, "sections"));
802        }
803
804        let mut sections = Vec::with_capacity(nsections);
805        // Note that if we are handling a BigCoff, the size of the symbol will be different!
806        let string_table_offset = self.pointer_to_symbol_table as usize
807            + symbol::SymbolTable::size(self.number_of_symbol_table as usize);
808        for i in 0..nsections {
809            let section =
810                section_table::SectionTable::parse(bytes, offset, string_table_offset as usize)?;
811            debug!("({}) {:#?}", i, section);
812            sections.push(section);
813        }
814        Ok(sections)
815    }
816
817    /// Return the COFF symbol table.
818    pub fn symbols<'a>(&self, bytes: &'a [u8]) -> error::Result<Option<symbol::SymbolTable<'a>>> {
819        let offset = self.pointer_to_symbol_table as usize;
820        let number = self.number_of_symbol_table as usize;
821        if offset == 0 {
822            Ok(None)
823        } else {
824            symbol::SymbolTable::parse(bytes, offset, number).map(Some)
825        }
826    }
827
828    /// Return the COFF string table.
829    pub fn strings<'a>(&self, bytes: &'a [u8]) -> error::Result<Option<strtab::Strtab<'a>>> {
830        // > The file offset of the COFF symbol table, or zero if no COFF symbol table is present.
831        // > This value should be zero for an image because COFF debugging information is deprecated.
832        if self.pointer_to_symbol_table == 0 {
833            return Ok(None);
834        }
835
836        let mut offset = self.pointer_to_symbol_table as usize
837            + symbol::SymbolTable::size(self.number_of_symbol_table as usize);
838
839        let length_field_size = core::mem::size_of::<u32>();
840        let length = bytes
841            .pread_with::<u32>(offset, scroll::LE)?
842            .checked_sub(length_field_size as u32)
843            .ok_or(error::Error::Malformed(format!(
844                "COFF length field size ({length_field_size:#x}) is larger than the parsed length value"
845            )))? as usize;
846
847        // The offset needs to be advanced in order to read the strings.
848        offset += length_field_size;
849
850        Ok(Some(strtab::Strtab::parse(bytes, offset, length, 0)?))
851    }
852}
853
854/// The PE header.
855///
856/// ## Position in a modern PE file
857///
858/// The PE header is located at the very beginning of the file and
859/// is followed by the section table and sections.
860#[derive(Debug, PartialEq, Copy, Clone, Default)]
861pub struct Header<'a> {
862    pub dos_header: DosHeader,
863    /// DOS program for legacy loaders
864    pub dos_stub: DosStub<'a>,
865    pub rich_header: Option<RichHeader<'a>>,
866
867    /// PE Magic: PE\0\0, little endian
868    pub signature: u32,
869    pub coff_header: CoffHeader,
870    pub optional_header: Option<optional_header::OptionalHeader>,
871}
872
873impl<'a> Header<'a> {
874    fn parse_impl(
875        bytes: &'a [u8],
876        dos_header: DosHeader,
877        dos_stub: DosStub<'a>,
878        parse_rich_header: bool,
879    ) -> error::Result<Self> {
880        let mut offset = dos_header.pe_pointer as usize;
881        let rich_header = if parse_rich_header {
882            RichHeader::parse(&bytes)?
883        } else {
884            None
885        };
886        let signature = bytes.gread_with(&mut offset, scroll::LE).map_err(|_| {
887            error::Error::Malformed(format!("cannot parse PE signature (offset {:#x})", offset))
888        })?;
889        let coff_header = CoffHeader::parse(&bytes, &mut offset)?;
890        let optional_header = if coff_header.size_of_optional_header > 0 {
891            Some(bytes.pread::<optional_header::OptionalHeader>(offset)?)
892        } else {
893            None
894        };
895
896        Ok(Header {
897            dos_header,
898            dos_stub,
899            rich_header,
900            signature,
901            coff_header,
902            optional_header,
903        })
904    }
905
906    /// Parses PE header from the given bytes; this will fail if the DosHeader or DosStub is malformed or missing in some way
907    pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
908        let dos_header = DosHeader::parse(&bytes)?;
909        let dos_stub = DosStub::parse(bytes, dos_header.pe_pointer)?;
910
911        Header::parse_impl(bytes, dos_header, dos_stub, true)
912    }
913
914    /// Parses PE header from the given bytes, a default DosHeader and DosStub are generated, and any malformed header or stub is ignored
915    pub fn parse_without_dos(bytes: &'a [u8]) -> error::Result<Self> {
916        debug_assert!(
917            !bytes.starts_with(b"MZ"),
918            "Buf should not contain DOS header and stub"
919        );
920        let dos_header = DosHeader::default();
921        Header::parse_impl(bytes, dos_header, DosStub::default(), false)
922    }
923}
924
925impl<'a> ctx::TryIntoCtx<scroll::Endian> for Header<'a> {
926    type Error = error::Error;
927
928    fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
929        let offset = &mut 0;
930        bytes.gwrite_with(self.dos_header, offset, ctx)?;
931        bytes.gwrite_with(self.dos_stub, offset, ctx)?;
932        bytes.gwrite_with(self.signature, offset, scroll::LE)?;
933        bytes.gwrite_with(self.coff_header, offset, ctx)?;
934        if let Some(opt_header) = self.optional_header {
935            bytes.gwrite_with(opt_header, offset, ctx)?;
936        }
937        Ok(*offset)
938    }
939}
940
941/// The DANS marker is a XOR-decoded version of the string "DanS" and is used to identify the Rich header.
942pub const DANS_MARKER: u32 = 0x536E6144;
943/// Size of [DANS_MARKER] in bytes
944pub const DANS_MARKER_SIZE: usize = core::mem::size_of::<u32>();
945/// The Rich marker is a XOR-decoded version of the string "Rich" and is used to identify the Rich header.
946pub const RICH_MARKER: u32 = 0x68636952;
947/// Size of [RICH_MARKER] in bytes
948pub const RICH_MARKER_SIZE: usize = core::mem::size_of::<u32>();
949
950/// The Rich header is a undocumented header that is used to store information about the build environment.
951///
952/// 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.
953#[derive(Debug, PartialEq, Copy, Clone, Default)]
954pub struct RichHeader<'a> {
955    /// Key is 32-bit value used for XOR encrypt/decrypt fields
956    pub key: u32,
957    /// The Rich header data with the padding.
958    pub data: &'a [u8],
959    /// Padding bytes at the prologue of [Self::data]
960    pub padding_size: usize,
961    /// Start offset of the Rich header.
962    pub start_offset: u32,
963    /// End offset of the Rich header.
964    pub end_offset: u32,
965}
966
967/// The Rich metadata is a pair of 32-bit values that store the tool version and the use count.
968#[repr(C)]
969#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite)]
970pub struct RichMetadata {
971    /// Build version is a 16-bit value that stores the version of the tool used to build the PE file.
972    pub build: u16,
973    /// Product identifier is a 16-bit value that stores the type of tool used to build the PE file.
974    pub product: u16,
975    /// The use count is a 32-bit value that stores the number of times the tool was used during the build process.
976    pub use_count: u32,
977}
978
979impl RichMetadata {
980    /// Parse [`RichMetadata`] from given bytes
981    fn parse(bytes: &[u8], key: u32) -> error::Result<Self> {
982        let mut offset = 0;
983        let build_and_product = bytes.gread_with::<u32>(&mut offset, scroll::LE)? ^ key;
984        let build = (build_and_product & 0xFFFF) as u16;
985        let product = (build_and_product >> 16) as u16;
986        let use_count = bytes.gread_with::<u32>(&mut offset, scroll::LE)? ^ key;
987        Ok(Self {
988            build,
989            product,
990            use_count,
991        })
992    }
993}
994
995/// Size of [`RichMetadata`] entries.
996const RICH_METADATA_SIZE: usize = 8;
997
998/// Iterator over [`RichMetadata`] in [`RichHeader`].
999#[derive(Debug)]
1000pub struct RichMetadataIterator<'a> {
1001    /// The key of [RichHeader::key]
1002    key: u32,
1003    /// The raw data [RichHeader::data] without padding
1004    data: &'a [u8],
1005}
1006
1007impl Iterator for RichMetadataIterator<'_> {
1008    type Item = error::Result<RichMetadata>;
1009
1010    fn next(&mut self) -> Option<Self::Item> {
1011        if self.data.is_empty() {
1012            return None;
1013        }
1014
1015        // Data within this iterator should not have padding
1016        Some(match RichMetadata::parse(&self.data, self.key) {
1017            Ok(metadata) => {
1018                self.data = &self.data[RICH_METADATA_SIZE..];
1019                Ok(metadata)
1020            }
1021            Err(error) => {
1022                self.data = &[];
1023                Err(error.into())
1024            }
1025        })
1026    }
1027
1028    fn size_hint(&self) -> (usize, Option<usize>) {
1029        let len = self.data.len() / RICH_METADATA_SIZE;
1030        (len, Some(len))
1031    }
1032}
1033
1034impl FusedIterator for RichMetadataIterator<'_> {}
1035impl ExactSizeIterator for RichMetadataIterator<'_> {}
1036
1037impl<'a> RichHeader<'a> {
1038    /// Parse the rich header from the given bytes.
1039    ///
1040    /// To decode the Rich header,
1041    /// - First locate the Rich marker and the subsequent 32-bit encryption key.
1042    /// - Then, work backwards from the Rich marker, XORing the key with the stored 32-bit values until you decode the DanS marker.
1043    ///
1044    /// Between these markers, you'll find pairs of 32-bit values:
1045    ///
1046    /// - the first indicates the Microsoft tool used, and
1047    /// - the second shows the count of linked object files made with that tool.
1048    /// - The upper 16 bits of the tool ID describe the tool type,
1049    /// - while the lower 16 bits specify the tool’s build version.
1050    pub fn parse(bytes: &'a [u8]) -> error::Result<Option<Self>> {
1051        // Parse the DOS header; some fields are required to locate the Rich header.
1052        let dos_header = DosHeader::parse(bytes)?;
1053        let dos_header_end_offset = PE_POINTER_OFFSET as usize;
1054        let pe_header_start_offset = dos_header.pe_pointer as usize;
1055
1056        // The Rich header is not present in all PE files.
1057        if (pe_header_start_offset - dos_header_end_offset) < 8 {
1058            return Ok(None);
1059        }
1060
1061        // The Rich header is located between the DOS header and the PE header.
1062        let scan_start = dos_header_end_offset + 4;
1063        let scan_end = pe_header_start_offset;
1064        if scan_start > scan_end {
1065            return Err(error::Error::Malformed(format!(
1066                "Rich header scan start ({:#X}) is greater than scan end ({:#X})",
1067                scan_start, scan_end
1068            )));
1069        }
1070        let scan_stub = &bytes[scan_start..scan_end];
1071
1072        // First locate the Rich marker and the subsequent 32-bit encryption key.
1073        let (rich_end_offset, key) = match scan_stub
1074            .windows(8)
1075            .enumerate()
1076            .filter_map(
1077                |(index, window)| match window.pread_with::<u32>(0, scroll::LE) {
1078                    // Marker matches, then return its index
1079                    Ok(marker) if marker == RICH_MARKER => Some(Ok(index)),
1080                    // Error reading with scroll
1081                    Err(e) => Some(Err(error::Error::from(e))),
1082                    // Marker did not match
1083                    _ => None,
1084                },
1085            )
1086            // Next is the very first element succeeded
1087            .next()
1088        {
1089            Some(Ok(rich_end_offset)) => {
1090                let rich_key =
1091                    scan_stub.pread_with::<u32>(rich_end_offset + RICH_MARKER_SIZE, scroll::LE)?;
1092                (rich_end_offset, rich_key)
1093            }
1094            // Something went wrong, e.g., reading with scroll
1095            Some(Err(e)) => return Err(e),
1096            // Marker did not found, rich header is assumed it does not exist
1097            None => return Ok(None),
1098        };
1099
1100        // Ensure rich_end_offset is within bounds
1101        if rich_end_offset >= scan_stub.len() {
1102            return Err(error::Error::Malformed(format!(
1103                "Rich end offset ({:#X}) exceeds scan stub length ({:#X})",
1104                rich_end_offset,
1105                scan_stub.len()
1106            )));
1107        }
1108        // Scope the buffer
1109        let rich_header = &scan_stub[..rich_end_offset];
1110
1111        // Look for DanS marker
1112        let rich_start_offset = match scan_stub
1113            .windows(4)
1114            .enumerate()
1115            .filter_map(
1116                |(index, window)| match window.pread_with::<u32>(0, scroll::LE) {
1117                    // If we do found the DanS marker, return the offset
1118                    Ok(value) if (value ^ key) == DANS_MARKER => Some(Ok(index + DANS_MARKER_SIZE)),
1119                    // This is scroll error, likely malformed rich header
1120                    Err(e) => Some(Err(error::Error::from(e))),
1121                    // No matching DanS marker found
1122                    _ => None,
1123                },
1124            )
1125            // Next is the very first element succeeded
1126            .next()
1127        {
1128            // Suceeded
1129            Some(Ok(offset)) => offset,
1130            // Errors such as from scroll reader
1131            Some(Err(e)) => return Err(e),
1132            // DanS marker did not found
1133            None => {
1134                return Err(error::Error::Malformed(format!(
1135                    "Rich header does not contain the DanS marker"
1136                )));
1137            }
1138        };
1139
1140        // Ensure rich_start_offset is within bounds
1141        if rich_start_offset >= rich_header.len() {
1142            return Err(error::Error::Malformed(format!(
1143                "Rich start offset ({:#X}) exceeds rich header length ({:#X})",
1144                rich_start_offset,
1145                rich_header.len()
1146            )));
1147        }
1148        // Scope the buffer
1149        let rich_header = &rich_header[rich_start_offset..];
1150
1151        // Skip padding bytes
1152        let padding_size = rich_header
1153            .chunks(4)
1154            .map(|chunk| chunk.pread_with::<u32>(0, scroll::LE))
1155            .collect::<Result<Vec<_>, _>>()?
1156            .into_iter()
1157            .take_while(|value| value == &key)
1158            .count()
1159            * core::mem::size_of_val(&key);
1160
1161        // Extract the Rich header data without the padding
1162        let data = rich_header;
1163
1164        // Subtract the sizeof DanS marker (u32, 4 bytes)
1165        let start_offset = scan_start as u32 + rich_start_offset as u32 - DANS_MARKER_SIZE as u32;
1166        let end_offset = scan_start as u32 + rich_end_offset as u32;
1167
1168        Ok(Some(RichHeader {
1169            key,
1170            data,
1171            padding_size,
1172            start_offset,
1173            end_offset,
1174        }))
1175    }
1176
1177    /// Returns [`RichMetadataIterator`] iterator for [`RichMetadata`]
1178    pub fn metadatas(&self) -> RichMetadataIterator<'a> {
1179        RichMetadataIterator {
1180            key: self.key,
1181            data: &self.data[self.padding_size..],
1182        }
1183    }
1184}
1185
1186/// The TE header is a reduced PE32/PE32+ header containing only fields
1187/// required for execution in the Platform Initialization
1188/// ([PI](https://uefi.org/specs/PI/1.8/V1_Introduction.html)) architecture.
1189/// The TE header is described in this specification:
1190/// <https://uefi.org/specs/PI/1.8/V1_TE_Image.html#te-header>
1191#[cfg(feature = "te")]
1192#[repr(C)]
1193#[derive(Debug, Default, PartialEq, Copy, Clone, Pread, Pwrite)]
1194pub struct TeHeader {
1195    /// Te signature, always [TE_MAGIC]
1196    pub signature: u16,
1197    /// The machine type
1198    pub machine: u16,
1199    /// The number of sections
1200    pub number_of_sections: u8,
1201    /// The subsystem
1202    pub subsystem: u8,
1203    /// the amount of bytes stripped from the header when converting from a
1204    /// PE32/PE32+ header to a TE header. Used to resolve addresses
1205    pub stripped_size: u16,
1206    /// The entry point of the binary
1207    pub entry_point: u32,
1208    /// The base of the code section
1209    pub base_of_code: u32,
1210    /// The image base
1211    pub image_base: u64,
1212    /// The size and address of the relocation directory
1213    pub reloc_dir: data_directories::DataDirectory,
1214    /// The size and address of the debug directory
1215    pub debug_dir: data_directories::DataDirectory,
1216}
1217
1218#[cfg(feature = "te")]
1219#[doc(alias("IMAGE_TE_SIGNATURE"))]
1220pub const TE_MAGIC: u16 = 0x5a56;
1221
1222#[cfg(feature = "te")]
1223impl TeHeader {
1224    /// Parse the TE header from the given bytes.
1225    pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result<Self> {
1226        const HEADER_SIZE: usize = core::mem::size_of::<TeHeader>();
1227        let mut header: TeHeader = bytes.gread_with(offset, scroll::LE)?;
1228        let stripped_size = header.stripped_size as u32;
1229        let adj_offset = stripped_size
1230            .checked_sub(HEADER_SIZE as u32)
1231            .ok_or_else(|| {
1232                error::Error::Malformed(format!(
1233                    "Stripped size ({stripped_size:#x}) is smaller than TE header size ({HEADER_SIZE:#x})",
1234                ))
1235            })?;
1236        header.fixup_header(adj_offset);
1237        Ok(header)
1238    }
1239
1240    /// Parse the sections from the TE header.
1241    pub fn sections(
1242        &self,
1243        bytes: &[u8],
1244        offset: &mut usize,
1245    ) -> error::Result<Vec<section_table::SectionTable>> {
1246        let adj_offset = self.stripped_size as u32 - core::mem::size_of::<TeHeader>() as u32;
1247        let nsections = self.number_of_sections as usize;
1248
1249        // a section table is at least 40 bytes
1250        if nsections > bytes.len() / 40 {
1251            return Err(error::Error::BufferTooShort(nsections, "sections"));
1252        }
1253
1254        let mut sections = Vec::with_capacity(nsections);
1255        for i in 0..nsections {
1256            let mut section = section_table::SectionTable::parse(bytes, offset, 0)?;
1257            TeHeader::fixup_section(&mut section, adj_offset);
1258            debug!("({}) {:#?}", i, section);
1259            sections.push(section);
1260        }
1261        Ok(sections)
1262    }
1263
1264    // Adjust addresses in the header to account for the stripped size
1265    fn fixup_header(&mut self, adj_offset: u32) {
1266        debug!(
1267            "Entry point fixed up from: 0x{:x} to 0x{:X}",
1268            self.entry_point,
1269            self.entry_point.wrapping_sub(adj_offset)
1270        );
1271        self.entry_point = self.entry_point.wrapping_sub(adj_offset);
1272
1273        debug!(
1274            "Base of code fixed up from: 0x{:x} to 0x{:X}",
1275            self.base_of_code,
1276            self.base_of_code.wrapping_sub(adj_offset)
1277        );
1278        self.base_of_code = self.base_of_code.wrapping_sub(adj_offset);
1279
1280        debug!(
1281            "Relocation Directory fixed up from: 0x{:x} to 0x{:X}",
1282            self.reloc_dir.virtual_address,
1283            self.reloc_dir.virtual_address.wrapping_sub(adj_offset)
1284        );
1285        self.reloc_dir.virtual_address = self.reloc_dir.virtual_address.wrapping_sub(adj_offset);
1286
1287        debug!(
1288            "Debug Directory fixed up from: 0x{:x} to 0x{:X}",
1289            self.debug_dir.virtual_address,
1290            self.debug_dir.virtual_address.wrapping_sub(adj_offset)
1291        );
1292        self.debug_dir.virtual_address = self.debug_dir.virtual_address.wrapping_sub(adj_offset);
1293    }
1294
1295    // Adjust addresses in the section to account for the stripped size
1296    fn fixup_section(section: &mut section_table::SectionTable, adj_offset: u32) {
1297        debug!(
1298            "Section virtual address fixed up from: 0x{:X} to 0x{:X}",
1299            section.virtual_address,
1300            section.virtual_address.wrapping_sub(adj_offset)
1301        );
1302        section.virtual_address = section.virtual_address.wrapping_sub(adj_offset);
1303
1304        if section.pointer_to_linenumbers > 0 {
1305            debug!(
1306                "Section pointer to line numbers fixed up from: 0x{:X} to 0x{:X}",
1307                section.pointer_to_linenumbers,
1308                section.pointer_to_linenumbers.wrapping_sub(adj_offset)
1309            );
1310            section.pointer_to_linenumbers =
1311                section.pointer_to_linenumbers.wrapping_sub(adj_offset);
1312        }
1313
1314        if section.pointer_to_raw_data > 0 {
1315            debug!(
1316                "Section pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
1317                section.pointer_to_raw_data,
1318                section.pointer_to_raw_data.wrapping_sub(adj_offset)
1319            );
1320            section.pointer_to_raw_data = section.pointer_to_raw_data.wrapping_sub(adj_offset);
1321        }
1322
1323        if section.pointer_to_relocations > 0 {
1324            debug!(
1325                "Section pointer to relocations fixed up from: 0x{:X} to 0x{:X}",
1326                section.pointer_to_relocations,
1327                section.pointer_to_relocations.wrapping_sub(adj_offset)
1328            );
1329            section.pointer_to_relocations =
1330                section.pointer_to_relocations.wrapping_sub(adj_offset);
1331        }
1332    }
1333}
1334
1335/// Convert machine to str representation. Any case of "COFF_UNKNOWN"
1336/// should be expected to change to a more specific value.
1337pub fn machine_to_str(machine: u16) -> &'static str {
1338    // TODO: generate the branches with a macro
1339    match machine {
1340        COFF_MACHINE_UNKNOWN => "UNKNOWN",
1341        COFF_MACHINE_ALPHA => "ALPHA",
1342        COFF_MACHINE_ALPHA64 => "ALPHA64",
1343        COFF_MACHINE_AM33 => "AM33",
1344        // This is an outlier. In the C header, it's IMAGE_FILE_MACHINE_AMD64
1345        COFF_MACHINE_X86_64 => "X86_64",
1346        COFF_MACHINE_ARM => "ARM",
1347        COFF_MACHINE_ARM64 => "ARM64",
1348        COFF_MACHINE_ARMNT => "ARM_NT",
1349        COFF_MACHINE_EBC => "EBC",
1350        // This is an outlier. In the C header, it's IMAGE_FILE_MACHINE_I386
1351        COFF_MACHINE_X86 => "X86",
1352        COFF_MACHINE_IA64 => "IA64",
1353        COFF_MACHINE_LOONGARCH32 => "LOONGARCH32",
1354        COFF_MACHINE_LOONGARCH64 => "LOONGARCH64",
1355        COFF_MACHINE_M32R => "M32R",
1356        COFF_MACHINE_MIPS16 => "MIPS_16",
1357        COFF_MACHINE_MIPSFPU => "MIPS_FPU",
1358        COFF_MACHINE_MIPSFPU16 => "MIPS_FPU_16",
1359        COFF_MACHINE_POWERPC => "POWERPC",
1360        COFF_MACHINE_POWERPCFP => "POWERCFP",
1361        COFF_MACHINE_R4000 => "R4000",
1362        COFF_MACHINE_RISCV32 => "RISC-V_32",
1363        COFF_MACHINE_RISCV64 => "RISC-V_64",
1364        COFF_MACHINE_RISCV128 => "RISC-V_128",
1365        COFF_MACHINE_SH3 => "SH3",
1366        COFF_MACHINE_SH3DSP => "SH3DSP",
1367        COFF_MACHINE_SH4 => "SH4",
1368        COFF_MACHINE_SH5 => "SH5",
1369        COFF_MACHINE_THUMB => "THUMB",
1370        COFF_MACHINE_WCEMIPSV2 => "WCE_MIPS_V2",
1371        _ => "COFF_UNKNOWN",
1372    }
1373}
1374
1375#[cfg(test)]
1376mod tests {
1377    use crate::{
1378        error,
1379        pe::{
1380            header::{DosStub, TeHeader},
1381            Coff,
1382        },
1383    };
1384
1385    use super::{
1386        machine_to_str, DosHeader, Header, RichHeader, RichMetadata, COFF_MACHINE_X86, DOS_MAGIC,
1387        PE_MAGIC,
1388    };
1389
1390    const CRSS_HEADER: [u8; 688] = [
1391        0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
1392        0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1393        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1394        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1395        0xd0, 0x00, 0x00, 0x00, 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01,
1396        0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d,
1397        0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20,
1398        0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a,
1399        0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x4a, 0xc3, 0xeb, 0xee, 0x2b, 0xad,
1400        0xb8, 0xee, 0x2b, 0xad, 0xb8, 0xee, 0x2b, 0xad, 0xb8, 0xee, 0x2b, 0xac, 0xb8, 0xfe, 0x2b,
1401        0xad, 0xb8, 0x33, 0xd4, 0x66, 0xb8, 0xeb, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x63, 0xb8, 0xea,
1402        0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x7a, 0xb8, 0xed, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x64, 0xb8,
1403        0xef, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x61, 0xb8, 0xef, 0x2b, 0xad, 0xb8, 0x52, 0x69, 0x63,
1404        0x68, 0xee, 0x2b, 0xad, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45,
1405        0x00, 0x00, 0x4c, 0x01, 0x05, 0x00, 0xd9, 0x8f, 0x15, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00,
1406        0x00, 0x00, 0x00, 0xe0, 0x00, 0x02, 0x01, 0x0b, 0x01, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00,
1407        0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00,
1408        0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02,
1409        0x00, 0x00, 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00,
1410        0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xe4, 0xab, 0x00, 0x00,
1411        0x01, 0x00, 0x40, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x10,
1412        0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
1413        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00,
1414        0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1415        0x00, 0x1a, 0x00, 0x00, 0xb8, 0x22, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x38, 0x00, 0x00,
1416        0x00, 0x10, 0x10, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1417        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1418        0x00, 0x00, 0x00, 0x68, 0x10, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1419        0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1420        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1421        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x24,
1422        0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
1423        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
1424        0x60, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x00, 0x20,
1425        0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1426        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xc0, 0x2e, 0x69, 0x64, 0x61,
1427        0x74, 0x61, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00,
1428        0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1429        0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x2e, 0x72, 0x73, 0x72, 0x63, 0x00, 0x00, 0x00, 0x00,
1430        0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
1431        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
1432        0x42, 0x2e, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x50,
1433        0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1434        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00,
1435        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1436        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1437    ];
1438
1439    const NO_RICH_HEADER: [u8; 262] = [
1440        0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00,
1441        0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00,
1442        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1443        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1444        0x00, 0x01, 0x00, 0x00, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
1445        0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72,
1446        0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
1447        0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37,
1448        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1449        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1450        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1451        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1452        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1453        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1454        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1455        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1456        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1457        0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86,
1458    ];
1459
1460    const NO_RICH_HEADER_INVALID_PE_POINTER: [u8; 304] = [
1461        0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
1462        0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1463        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1464        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1465        0x3C, 0xFF, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
1466        0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
1467        0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
1468        0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
1469        0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xC7, 0xEA, 0x07, 0xC9, 0xA6, 0x84,
1470        0x54, 0xC9, 0xA6, 0x84, 0x54, 0xC9, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x81, 0x55, 0xCB, 0xA6,
1471        0x84, 0x54, 0xC0, 0xDE, 0x17, 0x54, 0xC1, 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x80, 0x55, 0xC7,
1472        0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x87, 0x55, 0xC1, 0xA6, 0x84, 0x54, 0xDD, 0xCD, 0x81, 0x55,
1473        0x7E, 0xA6, 0x84, 0x54, 0xB9, 0x27, 0x85, 0x55, 0xCA, 0xA6, 0x84, 0x54, 0xC9, 0xA6, 0x85,
1474        0x54, 0x08, 0xA6, 0x84, 0x54, 0xA5, 0xD2, 0x81, 0x55, 0xE8, 0xA6, 0x84, 0x54, 0xA5, 0xD2,
1475        0x80, 0x55, 0xD9, 0xA6, 0x84, 0x54, 0xA5, 0xD2, 0x87, 0x55, 0xC0, 0xA6, 0x84, 0x54, 0x10,
1476        0xD2, 0x80, 0x55, 0x49, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x84, 0x55, 0xC8, 0xA6, 0x84, 0x54,
1477        0x10, 0xD2, 0x7B, 0x54, 0xC8, 0xA6, 0x84, 0x54, 0x10, 0xD2, 0x86, 0x55, 0xC8, 0xA6, 0x84,
1478        0x54, 0x52, 0x69, 0x63, 0x68, 0xC9, 0xA6, 0x84, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1479        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1480        0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x07, 0x00, 0xEC, 0xA5, 0x5B, 0x66,
1481        0x00, 0x00, 0x00, 0x00,
1482    ];
1483
1484    const CORRECT_RICH_HEADER: [u8; 256] = [
1485        0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
1486        0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1487        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1488        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1489        0xF8, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
1490        0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
1491        0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
1492        0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
1493        0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x4C, 0x5B, 0xB1, 0x37, 0x2D, 0x35,
1494        0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x31, 0xE3, 0x3D, 0x2D,
1495        0x35, 0xE2, 0x44, 0x4F, 0x36, 0xE3, 0x32, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x30, 0xE3, 0x48,
1496        0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x36, 0xE3, 0x3E, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x30, 0xE3,
1497        0x14, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x31, 0xE3, 0x25, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x34,
1498        0xE3, 0x3C, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x34, 0xE2, 0xAF, 0x2D, 0x35, 0xE2, 0x37, 0x2D,
1499        0x35, 0xE2, 0x23, 0x2D, 0x35, 0xE2, 0xFC, 0x4E, 0x37, 0xE3, 0x36, 0x2D, 0x35, 0xE2, 0x52,
1500        0x69, 0x63, 0x68, 0x37, 0x2D, 0x35, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1501        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05,
1502        0x00,
1503    ];
1504
1505    const CORRUPTED_RICH_HEADER: [u8; 256] = [
1506        0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
1507        0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1508        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1509        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1510        0xF8, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
1511        0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D,
1512        0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
1513        0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
1514        0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x4C, 0x5B, 0xB1, 0x37, 0x2D, 0x35,
1515        0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x31, 0xE3, 0x3D, 0x2D,
1516        0x35, 0xE2, 0x44, 0x4F, 0x36, 0xE3, 0x32, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x30, 0xE3, 0x48,
1517        0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x36, 0xE3, 0x3E, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x30, 0xE3,
1518        0x14, 0x2D, 0x35, 0xE2, 0xEE, 0x4F, 0x31, 0xE3, 0x25, 0x2D, 0x35, 0xE2, 0x44, 0x4F, 0x34,
1519        0xE3, 0x3C, 0x2D, 0x35, 0xE2, 0x37, 0x2D, 0x34, 0xE2, 0xAF, 0x2D, 0x35, 0xE2, 0x37, 0x2D,
1520        0x35, 0xE2, 0x23, 0x2D, 0x35, 0xE2, 0xFC, 0x4E, 0x37, 0xE3, 0x36, 0x2D, 0x35, 0xE2, 0x52,
1521        0x69, 0x63, 0x68, 0x37, 0x2D, 0x35, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1522        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x05,
1523        0x00,
1524    ];
1525
1526    const BORLAND_PE32_VALID_NO_RICH_HEADER: [u8; 528] = [
1527        0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00,
1528        0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00,
1529        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1530        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1531        0x00, 0x02, 0x00, 0x00, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
1532        0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72,
1533        0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
1534        0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37,
1535        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1536        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1537        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1538        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1539        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1540        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1541        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1542        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1543        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1544        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1545        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1546        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1547        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1548        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1549        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1550        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1551        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1552        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1553        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1554        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1555        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1556        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1557        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1558        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1559        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1560        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1561        0x00, 0x00, // PE
1562        0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x08, 0x00, 0xC0, 0x9C, 0x07, 0x67, 0x00, 0x00, 0x00,
1563        0x00,
1564    ];
1565
1566    /// Malformed very small TE with valid TE magic.
1567    ///
1568    /// https://github.com/m4b/goblin/issues/450
1569    const MALFORMED_SMALL_TE: [u8; 58] = [
1570        0x56, 0x5A, 0x52, 0x5A, 0x50, 0x00, 0x17, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00,
1571        0x10, 0x86, 0x02, 0x0C, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x1B, 0x01, 0x01, 0x00, 0x00,
1572        0xFF, 0xB5, 0x00, 0x00, 0x00, 0x04, 0x34, 0x00, 0x00, 0xFF, 0xB5, 0x00, 0x00, 0x00, 0x04,
1573        0x34, 0x15, 0x40, 0x13, 0x41, 0x0E, 0x10, 0x15, 0x40, 0x13, 0x41, 0x0E, 0x10,
1574    ];
1575
1576    /// An invalid small COFF object file
1577    ///
1578    /// https://github.com/m4b/goblin/issues/450
1579    const INVALID_COFF_OBJECT: [u8; 20] = [
1580        0x4C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1581        0x00, 0x0F, 0x00, 0xFF, 0x80,
1582    ];
1583
1584    const BIN: &[u8] = include_bytes!("../../tests/bins/pe/well_formed_import.exe.bin");
1585
1586    #[test]
1587    fn crss_header() {
1588        let header = Header::parse(&&CRSS_HEADER[..]).unwrap();
1589        assert!(header.dos_header.signature == DOS_MAGIC);
1590        assert!(header.signature == PE_MAGIC);
1591        assert!(header.coff_header.machine == COFF_MACHINE_X86);
1592        assert!(machine_to_str(header.coff_header.machine) == "X86");
1593        println!("header: {:?}", &header);
1594    }
1595
1596    #[test]
1597    fn parse_without_dos() {
1598        let result = std::panic::catch_unwind(|| {
1599            let _ = Header::parse_without_dos(&BORLAND_PE32_VALID_NO_RICH_HEADER).unwrap();
1600        });
1601        assert!(result.is_err(), "Expected panic, got {result:?}");
1602        if let Err(err) = result {
1603            if let Some(s) = err.downcast_ref::<&str>() {
1604                assert_eq!(
1605                    *s, "Buf should not contain DOS header and stub",
1606                    "Panic message did not match"
1607                );
1608            } else {
1609                panic!("Unexpected panic type");
1610            }
1611        }
1612
1613        // Get a PE pointer (e_lfanew)
1614        let dos_header = DosHeader::parse(&BIN).unwrap();
1615        // Skip DOS header and DOS stub
1616        let buf = &BIN[dos_header.pe_pointer as usize..];
1617        Header::parse_without_dos(buf).unwrap();
1618    }
1619
1620    #[test]
1621    fn parse_borland_weird_dos_stub() {
1622        let dos_stub = DosStub::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER, 0x200).unwrap();
1623        assert_ne!(dos_stub.data, BORLAND_PE32_VALID_NO_RICH_HEADER.to_vec());
1624    }
1625
1626    #[test]
1627    fn parse_borland_no_rich_header() {
1628        let header = RichHeader::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER).unwrap();
1629        assert_eq!(header, None);
1630    }
1631
1632    #[test]
1633    fn parse_no_rich_header() {
1634        let header = RichHeader::parse(&NO_RICH_HEADER).unwrap();
1635        assert_eq!(header, None);
1636    }
1637
1638    #[test]
1639    fn parse_no_rich_header_invalid_pe_pointer() {
1640        let header = RichHeader::parse(&NO_RICH_HEADER_INVALID_PE_POINTER);
1641        assert_eq!(header.is_err(), true);
1642        if let Err(error::Error::Malformed(msg)) = header {
1643            assert_eq!(msg, "cannot parse PE header signature (offset 0xff3c)");
1644        } else {
1645            panic!("Expected a Malformed error but got {:?}", header);
1646        }
1647    }
1648
1649    #[test]
1650    fn parse_correct_rich_header() {
1651        let header = RichHeader::parse(&CORRECT_RICH_HEADER).unwrap();
1652        assert_ne!(header, None);
1653        let header = header.unwrap();
1654        let expected = vec![
1655            RichMetadata {
1656                build: 25203,
1657                product: 260,
1658                use_count: 10,
1659            },
1660            RichMetadata {
1661                build: 25203,
1662                product: 259,
1663                use_count: 5,
1664            },
1665            RichMetadata {
1666                build: 25203,
1667                product: 261,
1668                use_count: 127,
1669            },
1670            RichMetadata {
1671                build: 25305,
1672                product: 259,
1673                use_count: 9,
1674            },
1675            RichMetadata {
1676                build: 25305,
1677                product: 261,
1678                use_count: 35,
1679            },
1680            RichMetadata {
1681                build: 25305,
1682                product: 260,
1683                use_count: 18,
1684            },
1685            RichMetadata {
1686                build: 25203,
1687                product: 257,
1688                use_count: 11,
1689            },
1690            RichMetadata {
1691                build: 0,
1692                product: 1,
1693                use_count: 152,
1694            },
1695            RichMetadata {
1696                build: 0,
1697                product: 0,
1698                use_count: 20,
1699            },
1700            RichMetadata {
1701                build: 25547,
1702                product: 258,
1703                use_count: 1,
1704            },
1705        ];
1706        assert_eq!(
1707            header
1708                .metadatas()
1709                .filter_map(Result::ok)
1710                .collect::<Vec<RichMetadata>>(),
1711            expected
1712        );
1713    }
1714
1715    #[test]
1716    fn parse_corrupted_rich_header() {
1717        let header_result = RichHeader::parse(&CORRUPTED_RICH_HEADER);
1718        assert_eq!(header_result.is_err(), true);
1719    }
1720
1721    #[test]
1722    fn parse_invalid_small_coff() {
1723        let header = Coff::parse(&INVALID_COFF_OBJECT);
1724        assert_eq!(header.is_err(), true);
1725        if let Err(error::Error::Malformed(msg)) = header {
1726            assert_eq!(
1727                msg,
1728                "COFF length field size (0x4) is larger than the parsed length value"
1729            );
1730        } else {
1731            panic!("Expected a Malformed error but got {:?}", header);
1732        }
1733    }
1734
1735    #[test]
1736    fn parse_malformed_small_te() {
1737        let mut offset = 0;
1738        let header = TeHeader::parse(&MALFORMED_SMALL_TE, &mut offset);
1739        assert_eq!(header.is_err(), true);
1740        if let Err(error::Error::Malformed(msg)) = header {
1741            assert_eq!(
1742                msg,
1743                "Stripped size (0x17) is smaller than TE header size (0x28)"
1744            );
1745        } else {
1746            panic!("Expected a Malformed error but got {:?}", header);
1747        }
1748    }
1749}