Skip to main content

goblin/pe/
header.rs

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