Skip to main content

goblin_experimental/pe/
optional_header.rs

1//! The module for the PE optional header ([`OptionalHeader`]) and related items.
2
3use crate::container;
4use crate::error;
5
6use crate::pe::data_directories;
7
8use scroll::{ctx, Endian, LE};
9use scroll::{Pread, Pwrite, SizeWith};
10
11/// Standard 32-bit COFF fields (for `PE32`).
12///
13/// In `winnt.h`, this is a subset of [`IMAGE_OPTIONAL_HEADER32`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32).
14///
15/// * For 64-bit version, see [`StandardFields64`].
16/// * For unified version, see [`StandardFields`].
17#[repr(C)]
18#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
19pub struct StandardFields32 {
20    /// See docs for [`StandardFields::magic`](crate::pe::optional_header::StandardFields::magic).
21    pub magic: u16,
22    /// See docs for [`StandardFields::major_linker_version`].
23    pub major_linker_version: u8,
24    /// See docs for [`StandardFields::minor_linker_version`].
25    pub minor_linker_version: u8,
26    /// See docs for [`StandardFields::size_of_code`].
27    pub size_of_code: u32,
28    /// See docs for [`StandardFields::size_of_initialized_data`].
29    pub size_of_initialized_data: u32,
30    /// See docs for [`StandardFields::size_of_uninitialized_data`].
31    pub size_of_uninitialized_data: u32,
32    /// See docs for [`StandardFields::address_of_entry_point`].
33    pub address_of_entry_point: u32,
34    /// See docs for [`StandardFields::base_of_code`].
35    pub base_of_code: u32,
36    /// See docs for [`StandardFields::base_of_data`].
37    pub base_of_data: u32,
38}
39
40/// Convenience constant for `core::mem::size_of::<StandardFields32>()`.
41pub const SIZEOF_STANDARD_FIELDS_32: usize = 28;
42
43/// Standard 64-bit COFF fields (for `PE32+`).
44///
45/// In `winnt.h`, this is a subset of [`IMAGE_OPTIONAL_HEADER64`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64).
46///
47/// * For 32-bit version, see [`StandardFields32`].
48/// * For unified version, see [`StandardFields`].
49#[repr(C)]
50#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
51pub struct StandardFields64 {
52    /// See docs for [`StandardFields::magic`](crate::pe::optional_header::StandardFields::magic).
53    pub magic: u16,
54    /// See docs for [`StandardFields::major_linker_version`].
55    pub major_linker_version: u8,
56    /// See docs for [`StandardFields::minor_linker_version`].
57    pub minor_linker_version: u8,
58    /// See docs for [`StandardFields::size_of_code`].
59    pub size_of_code: u32,
60    /// See docs for [`StandardFields::size_of_initialized_data`].
61    pub size_of_initialized_data: u32,
62    /// See docs for [`StandardFields::size_of_uninitialized_data`].
63    pub size_of_uninitialized_data: u32,
64    /// See docs for [`StandardFields::address_of_entry_point`].
65    pub address_of_entry_point: u32,
66    /// See docs for [`StandardFields::base_of_code`].
67    pub base_of_code: u32,
68}
69
70/// Convenience constant for `core::mem::size_of::<StandardFields64>()`.
71pub const SIZEOF_STANDARD_FIELDS_64: usize = 24;
72
73/// Unified 32/64-bit standard COFF fields (for `PE32` and `PE32+`).
74///
75/// Notably, a value of this type is a member of
76/// [`goblin::pe::optional_header::OptionalHeader`](crate::pe::optional_header::OptionalHeader),
77/// which in turn represents either
78/// * [`IMAGE_OPTIONAL_HEADER32`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32); or
79/// * [`IMAGE_OPTIONAL_HEADER64`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64)
80///
81/// from `winnt.h`, depending on the value of [`StandardFields::magic`].
82///
83/// ## Position in PE binary
84///
85/// Standard COFF fields are located at the beginning of the [`OptionalHeader`] and before the
86/// [`WindowsFields`].
87///
88/// ## Related structures
89///
90/// * For 32-bit version, see [`StandardFields32`].
91/// * For 64-bit version, see [`StandardFields64`].
92#[derive(Debug, PartialEq, Copy, Clone, Default)]
93pub struct StandardFields {
94    /// The state of the image file. This member can be one of the following values:
95    ///
96    /// * [`IMAGE_NT_OPTIONAL_HDR32_MAGIC`].
97    /// * [`IMAGE_NT_OPTIONAL_HDR64_MAGIC`].
98    /// * [`IMAGE_ROM_OPTIONAL_HDR_MAGIC`].
99    #[doc(alias = "Magic")]
100    pub magic: u16,
101    /// The major version number of the linker.
102    #[doc(alias = "MajorLinkerVersion")]
103    pub major_linker_version: u8,
104    /// The minor version number of the linker.
105    #[doc(alias = "MinorLinkerVersion")]
106    pub minor_linker_version: u8,
107    /// The size of the code section (.text), in bytes, or the sum of all such sections if there are multiple code sections.
108    #[doc(alias = "SizeOfCode")]
109    pub size_of_code: u64,
110    /// The size of the initialized data section (.data), in bytes, or the sum of all such sections if there are multiple initialized data sections.
111    #[doc(alias = "SizeOfInitializedData")]
112    pub size_of_initialized_data: u64,
113    /// The size of the uninitialized data section (.bss), in bytes, or the sum of all such sections if there are multiple uninitialized data sections.
114    #[doc(alias = "SizeOfUninitializedData")]
115    pub size_of_uninitialized_data: u64,
116    /// A pointer to the entry point function, relative to the image base address.
117    ///
118    /// * For executable files, this is the starting address.
119    /// * For device drivers, this is the address of the initialization function.
120    ///
121    /// The entry point function is optional for DLLs. When no entry point is present, this member is zero.
122    pub address_of_entry_point: u32,
123    /// A pointer to the beginning of the code section (.text), relative to the image base.
124    pub base_of_code: u64,
125    /// A pointer to the beginning of the data section (.data), relative to the image base. Absent in 64-bit PE32+.
126    ///
127    /// In other words, it is a Relative virtual address (RVA) of the start of the data (.data) section when the PE
128    /// is loaded into memory.
129    // Q (JohnScience): Why is this a u32 and not an Option<u32>?
130    pub base_of_data: u32,
131}
132
133impl From<StandardFields32> for StandardFields {
134    fn from(fields: StandardFields32) -> Self {
135        StandardFields {
136            magic: fields.magic,
137            major_linker_version: fields.major_linker_version,
138            minor_linker_version: fields.minor_linker_version,
139            size_of_code: u64::from(fields.size_of_code),
140            size_of_initialized_data: u64::from(fields.size_of_initialized_data),
141            size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data),
142            address_of_entry_point: fields.address_of_entry_point,
143            base_of_code: u64::from(fields.base_of_code),
144            base_of_data: fields.base_of_data,
145        }
146    }
147}
148
149impl From<StandardFields> for StandardFields32 {
150    fn from(fields: StandardFields) -> Self {
151        StandardFields32 {
152            magic: fields.magic,
153            major_linker_version: fields.major_linker_version,
154            minor_linker_version: fields.minor_linker_version,
155            size_of_code: fields.size_of_code as u32,
156            size_of_initialized_data: fields.size_of_initialized_data as u32,
157            size_of_uninitialized_data: fields.size_of_uninitialized_data as u32,
158            address_of_entry_point: fields.address_of_entry_point as u32,
159            base_of_code: fields.base_of_code as u32,
160            base_of_data: fields.base_of_data,
161        }
162    }
163}
164
165impl From<StandardFields64> for StandardFields {
166    fn from(fields: StandardFields64) -> Self {
167        StandardFields {
168            magic: fields.magic,
169            major_linker_version: fields.major_linker_version,
170            minor_linker_version: fields.minor_linker_version,
171            size_of_code: u64::from(fields.size_of_code),
172            size_of_initialized_data: u64::from(fields.size_of_initialized_data),
173            size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data),
174            address_of_entry_point: fields.address_of_entry_point,
175            base_of_code: u64::from(fields.base_of_code),
176            base_of_data: 0,
177        }
178    }
179}
180
181impl From<StandardFields> for StandardFields64 {
182    fn from(fields: StandardFields) -> Self {
183        StandardFields64 {
184            magic: fields.magic,
185            major_linker_version: fields.major_linker_version,
186            minor_linker_version: fields.minor_linker_version,
187            size_of_code: fields.size_of_code as u32,
188            size_of_initialized_data: fields.size_of_initialized_data as u32,
189            size_of_uninitialized_data: fields.size_of_uninitialized_data as u32,
190            address_of_entry_point: fields.address_of_entry_point as u32,
191            base_of_code: fields.base_of_code as u32,
192        }
193    }
194}
195
196/// Standard fields magic number for 32-bit binary (`PE32`).
197pub const MAGIC_32: u16 = 0x10b;
198/// Standard fields magic number for 64-bit binary (`PE32+`).
199pub const MAGIC_64: u16 = 0x20b;
200
201/// Windows specific fields for 32-bit binary (`PE32`). They're also known as "NT additional fields".
202///
203/// In `winnt.h`, this is a subset of [`IMAGE_OPTIONAL_HEADER32`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32).
204///
205/// * For 64-bit version, see [`WindowsFields64`].
206/// * For unified version, see [`WindowsFields`].
207#[repr(C)]
208#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
209pub struct WindowsFields32 {
210    /// See docs for [`WindowsFields::image_base`].
211    pub image_base: u32,
212    /// See docs for [`WindowsFields::section_alignment`].
213    pub section_alignment: u32,
214    /// See docs for [`WindowsFields::file_alignment`].
215    pub file_alignment: u32,
216    /// See docs for [`WindowsFields::major_operating_system_version`].
217    pub major_operating_system_version: u16,
218    /// See docs for [`WindowsFields::minor_operating_system_version`].
219    pub minor_operating_system_version: u16,
220    /// See docs for [`WindowsFields::major_image_version`].
221    pub major_image_version: u16,
222    /// See docs for [`WindowsFields::minor_image_version`].
223    pub minor_image_version: u16,
224    /// See docs for [`WindowsFields::major_subsystem_version`].
225    pub major_subsystem_version: u16,
226    /// See docs for [`WindowsFields::minor_subsystem_version`].
227    pub minor_subsystem_version: u16,
228    /// See docs for [`WindowsFields::win32_version_value`].
229    pub win32_version_value: u32,
230    /// See docs for [`WindowsFields::size_of_image`].
231    pub size_of_image: u32,
232    /// See docs for [`WindowsFields::size_of_headers`].
233    pub size_of_headers: u32,
234    /// See docs for [`WindowsFields::check_sum`].
235    pub check_sum: u32,
236    /// See docs for [`WindowsFields::subsystem`].
237    pub subsystem: u16,
238    /// See docs for [`WindowsFields::dll_characteristics`].
239    pub dll_characteristics: u16,
240    /// See docs for [`WindowsFields::size_of_stack_reserve`].
241    pub size_of_stack_reserve: u32,
242    /// See docs for [`WindowsFields::size_of_stack_commit`].
243    pub size_of_stack_commit: u32,
244    /// See docs for [`WindowsFields::size_of_heap_reserve`].
245    pub size_of_heap_reserve: u32,
246    /// See docs for [`WindowsFields::size_of_heap_commit`].
247    pub size_of_heap_commit: u32,
248    /// See docs for [`WindowsFields::loader_flags`].
249    pub loader_flags: u32,
250    /// See docs for [`WindowsFields::number_of_rva_and_sizes`].
251    pub number_of_rva_and_sizes: u32,
252}
253
254/// Convenience constant for `core::mem::size_of::<WindowsFields32>()`.
255pub const SIZEOF_WINDOWS_FIELDS_32: usize = 68;
256/// Offset of the `check_sum` field in [`WindowsFields32`].
257pub const OFFSET_WINDOWS_FIELDS_32_CHECKSUM: usize = 36;
258
259/// Windows specific fields for 64-bit binary (`PE32+`). They're also known as "NT additional fields".
260///
261/// In `winnt.h`, this is a subset of [`IMAGE_OPTIONAL_HEADER64`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64).
262///
263/// *Note: at the moment of writing, [`WindowsFields`] is an alias for `WindowsFields64`. Though [nominally equivalent](https://en.wikipedia.org/wiki/Nominal_type_system),
264/// they're semantically distinct.*
265///
266/// * For 32-bit version, see [`WindowsFields32`].
267/// * For unified version, see [`WindowsFields`].
268#[repr(C)]
269#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
270pub struct WindowsFields64 {
271    /// The *preferred* yet rarely provided address of the first byte of image when loaded into memory; must be a
272    /// multiple of 64 K.
273    ///
274    /// This address is rarely used because Windows uses memory protection mechanisms like Address Space Layout
275    /// Randomization (ASLR). As a result, it’s rare to see an image mapped to the preferred address. Instead,
276    /// the Windows PE Loader maps the file to a different address with an unused memory range. This process
277    /// would create issues because some addresses that would have been constant are now changed. The Loader
278    /// addresses this via a process called PE relocation which fixes these constant addresses to work with the
279    /// new image base. The relocation section (.reloc) holds data essential to this relocation process.
280    /// [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/nt-headers/optional-header/).
281    ///
282    /// * The default address for DLLs is 0x10000000.
283    /// * The default for Windows CE EXEs is 0x00010000.
284    /// * The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.
285    ///
286    /// ## Position in PE binary
287    ///
288    /// Windows fields are located inside [`OptionalHeader`] after [`StandardFields`] and before the
289    /// [`DataDirectories`](data_directories::DataDirectories).
290    ///
291    /// ## Related structures
292    ///
293    /// * For 32-bit version, see [`WindowsFields32`].
294    /// * For unified version, see [`WindowsFields`], especially the note on nominal equivalence.
295    #[doc(alias = "ImageBase")]
296    pub image_base: u64,
297    /// Holds a byte value used for section alignment in memory.
298    ///
299    /// This value must be greater than or equal to
300    /// [`file_alignment`](WindowsFields64::file_alignment), which is the next field.
301    ///
302    /// When loaded into memory, sections are aligned in memory boundaries that are multiples of this value.
303    ///
304    /// If the value is less than the architecture’s page size, then the value should match
305    /// [`file_alignment`](WindowsFields64::file_alignment).
306    /// [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/nt-headers/optional-header/).
307    ///
308    /// The default value is the page size for the architecture.
309    #[doc(alias = "SectionAlignment")]
310    pub section_alignment: u32,
311    /// The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
312    ///
313    /// The value should be a power of 2 between 512 and 64 K, inclusive.
314    ///
315    /// If the [`section_alignment`](WindowsFields64::section_alignment) is less than the architecture's page size,
316    /// then [`file_alignment`](WindowsFields64::file_alignment) must match [`section_alignment`](WindowsFields64::section_alignment).
317    ///
318    /// If [`file_alignment`](WindowsFields64::file_alignment) is less than [`section_alignment`](WindowsFields64::section_alignment),
319    /// then remainder will be padded with zeroes in order to maintain the alignment boundaries.
320    /// [Source](https://offwhitesecurity.dev/malware-development/portable-executable-pe/nt-headers/optional-header/).
321    ///
322    /// The default value is 512.
323    #[doc(alias = "FileAlignment")]
324    pub file_alignment: u32,
325    /// The major version number of the required operating system.
326    #[doc(alias = "MajorOperatingSystemVersion")]
327    pub major_operating_system_version: u16,
328    /// The minor version number of the required operating system.
329    #[doc(alias = "MinorOperatingSystemVersion")]
330    pub minor_operating_system_version: u16,
331    /// The major version number of the image.
332    #[doc(alias = "MajorImageVersion")]
333    pub major_image_version: u16,
334    /// The minor version number of the image.
335    #[doc(alias = "MinorImageVersion")]
336    pub minor_image_version: u16,
337    /// The major version number of the subsystem.
338    #[doc(alias = "MajorSubsystemVersion")]
339    pub major_subsystem_version: u16,
340    /// The minor version number of the subsystem.
341    #[doc(alias = "MinorSubsystemVersion")]
342    pub minor_subsystem_version: u16,
343    /// Reserved, must be zero.
344    #[doc(alias = "Win32VersionValue")]
345    pub win32_version_value: u32,
346    /// The size (in bytes) of the image, including all headers, as the image is loaded in memory.
347    ///
348    /// It must be a multiple of the [`section_alignment`](WindowsFields64::section_alignment).
349    #[doc(alias = "SizeOfImage")]
350    pub size_of_image: u32,
351    /// The combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of
352    /// [`file_alignment`](WindowsFields64::file_alignment).
353    #[doc(alias = "SizeOfHeaders")]
354    pub size_of_headers: u32,
355    /// The image file checksum. The algorithm for computing the checksum is incorporated into IMAGHELP.DLL.
356    ///
357    /// The following are checked for validation at load time:
358    /// * all drivers,
359    /// * any DLL loaded at boot time, and
360    /// * any DLL that is loaded into a critical Windows process.
361    #[doc(alias = "CheckSum")]
362    pub check_sum: u32,
363    /// The subsystem that is required to run this image.
364    ///
365    /// The subsystem can be one of the values in the [`goblin::pe::subsystem`](crate::pe::subsystem) module.
366    #[doc(alias = "Subsystem")]
367    pub subsystem: u16,
368    /// DLL characteristics of the image.
369    ///
370    /// DLL characteristics can be one of the values in the
371    /// [`goblin::pe::dll_characteristic`](crate::pe::dll_characteristic) module.
372    #[doc(alias = "DllCharacteristics")]
373    pub dll_characteristics: u16,
374    /// The size of the stack to reserve. Only [`WindowsFields::size_of_stack_commit`] is committed;
375    /// the rest is made available one page at a time until the reserve size is reached.
376    ///
377    /// In the context of memory management in operating systems, "commit" refers to the act of allocating physical memory
378    /// to back a portion of the virtual memory space.
379    ///
380    /// When a program requests memory, the operating system typically allocates virtual memory space for it. However,
381    /// this virtual memory space doesn't immediately consume physical memory (RAM) resources. Instead, physical memory
382    /// is only allocated when the program actually uses (or accesses) that portion of the virtual memory space.
383    /// This allocation of physical memory to back virtual memory is called "committing" memory.
384    #[doc(alias = "SizeOfStackReserve")]
385    pub size_of_stack_reserve: u64,
386    /// The size of the stack to commit.
387    #[doc(alias = "SizeOfStackCommit")]
388    pub size_of_stack_commit: u64,
389    ///  The size of the local heap space to reserve. Only [`WindowsFields::size_of_heap_commit`] is committed; the rest
390    /// is made available one page at a time until the reserve size is reached.
391    #[doc(alias = "SizeOfHeapReserve")]
392    pub size_of_heap_reserve: u64,
393    /// The size of the local heap space to commit.
394    #[doc(alias = "SizeOfHeapCommit")]
395    pub size_of_heap_commit: u64,
396    /// Reserved, must be zero.
397    #[doc(alias = "LoaderFlags")]
398    pub loader_flags: u32,
399    /// The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
400    #[doc(alias = "NumberOfRvaAndSizes")]
401    pub number_of_rva_and_sizes: u32,
402}
403
404/// Convenience constant for `core::mem::size_of::<WindowsFields64>()`.
405pub const SIZEOF_WINDOWS_FIELDS_64: usize = 88;
406/// Offset of the `check_sum` field in [`WindowsFields64`].
407pub const OFFSET_WINDOWS_FIELDS_64_CHECKSUM: usize = 40;
408
409// /// Generic 32/64-bit Windows specific fields
410// #[derive(Debug, PartialEq, Copy, Clone, Default)]
411// pub struct WindowsFields {
412//     pub image_base: u64,
413//     pub section_alignment: u32,
414//     pub file_alignment: u32,
415//     pub major_operating_system_version: u16,
416//     pub minor_operating_system_version: u16,
417//     pub major_image_version: u16,
418//     pub minor_image_version: u16,
419//     pub major_subsystem_version: u16,
420//     pub minor_subsystem_version: u16,
421//     pub win32_version_value: u32,
422//     pub size_of_image: u32,
423//     pub size_of_headers: u32,
424//     pub check_sum: u32,
425//     pub subsystem: u16,
426//     pub dll_characteristics: u16,
427//     pub size_of_stack_reserve: u64,
428//     pub size_of_stack_commit:  u64,
429//     pub size_of_heap_reserve:  u64,
430//     pub size_of_heap_commit:   u64,
431//     pub loader_flags: u32,
432//     pub number_of_rva_and_sizes: u32,
433// }
434
435impl From<WindowsFields32> for WindowsFields {
436    fn from(windows: WindowsFields32) -> Self {
437        WindowsFields {
438            image_base: u64::from(windows.image_base),
439            section_alignment: windows.section_alignment,
440            file_alignment: windows.file_alignment,
441            major_operating_system_version: windows.major_operating_system_version,
442            minor_operating_system_version: windows.minor_operating_system_version,
443            major_image_version: windows.major_image_version,
444            minor_image_version: windows.minor_image_version,
445            major_subsystem_version: windows.major_subsystem_version,
446            minor_subsystem_version: windows.minor_subsystem_version,
447            win32_version_value: windows.win32_version_value,
448            size_of_image: windows.size_of_image,
449            size_of_headers: windows.size_of_headers,
450            check_sum: windows.check_sum,
451            subsystem: windows.subsystem,
452            dll_characteristics: windows.dll_characteristics,
453            size_of_stack_reserve: u64::from(windows.size_of_stack_reserve),
454            size_of_stack_commit: u64::from(windows.size_of_stack_commit),
455            size_of_heap_reserve: u64::from(windows.size_of_heap_reserve),
456            size_of_heap_commit: u64::from(windows.size_of_heap_commit),
457            loader_flags: windows.loader_flags,
458            number_of_rva_and_sizes: windows.number_of_rva_and_sizes,
459        }
460    }
461}
462
463impl TryFrom<WindowsFields64> for WindowsFields32 {
464    type Error = crate::error::Error;
465
466    fn try_from(value: WindowsFields64) -> Result<Self, Self::Error> {
467        Ok(WindowsFields32 {
468            image_base: value.image_base.try_into()?,
469            section_alignment: value.section_alignment,
470            file_alignment: value.file_alignment,
471            major_operating_system_version: value.major_operating_system_version,
472            minor_operating_system_version: value.minor_operating_system_version,
473            major_image_version: value.major_image_version,
474            minor_image_version: value.minor_image_version,
475            major_subsystem_version: value.major_subsystem_version,
476            minor_subsystem_version: value.minor_subsystem_version,
477            win32_version_value: value.win32_version_value,
478            size_of_image: value.size_of_image,
479            size_of_headers: value.size_of_headers,
480            check_sum: value.check_sum,
481            subsystem: value.subsystem,
482            dll_characteristics: value.dll_characteristics,
483            size_of_stack_reserve: value.size_of_stack_reserve.try_into()?,
484            size_of_stack_commit: value.size_of_stack_commit.try_into()?,
485            size_of_heap_reserve: value.size_of_heap_reserve.try_into()?,
486            size_of_heap_commit: value.size_of_heap_commit.try_into()?,
487            loader_flags: value.loader_flags,
488            number_of_rva_and_sizes: value.number_of_rva_and_sizes,
489        })
490    }
491}
492
493// impl From<WindowsFields32> for WindowsFields {
494//     fn from(windows: WindowsFields32) -> Self {
495//         WindowsFields {
496//             image_base: windows.image_base,
497//             section_alignment: windows.section_alignment,
498//             file_alignment: windows.file_alignment,
499//             major_operating_system_version: windows.major_operating_system_version,
500//             minor_operating_system_version: windows.minor_operating_system_version,
501//             major_image_version: windows.major_image_version,
502//             minor_image_version: windows.minor_image_version,
503//             major_subsystem_version: windows.major_subsystem_version,
504//             minor_subsystem_version: windows.minor_subsystem_version,
505//             win32_version_value: windows.win32_version_value,
506//             size_of_image: windows.size_of_image,
507//             size_of_headers: windows.size_of_headers,
508//             check_sum: windows.check_sum,
509//             subsystem: windows.subsystem,
510//             dll_characteristics: windows.dll_characteristics,
511//             size_of_stack_reserve: windows.size_of_stack_reserve,
512//             size_of_stack_commit: windows.size_of_stack_commit,
513//             size_of_heap_reserve: windows.size_of_heap_reserve,
514//             size_of_heap_commit: windows.size_of_heap_commit,
515//             loader_flags: windows.loader_flags,
516//             number_of_rva_and_sizes: windows.number_of_rva_and_sizes,
517//         }
518//     }
519// }
520
521/// Unified 32/64-bit Windows fields (for `PE32` and `PE32+`). Since 64-bit fields are a superset of 32-bit fields,
522/// `WindowsFields` is an alias for `WindowsFields64`.
523//
524// Opinion (JohnScience): even though they're structurally equivalent, it was a questionable idea to make
525// them nominally equivalent as well because they're not actually the same thing semantically. WindowsFields is meant to be
526// a unified type that can represent either 32-bit or 64-bit Windows fields.
527//
528// How do you document this effectively and forward-compatibly? `WindowsFields64` and `WindowsFields` need
529// different documentation.
530pub type WindowsFields = WindowsFields64;
531
532/// Unified 32/64-bit optional header (for `PE32` and `PE32+`).
533///
534/// Optional header is the most important of the [NT headers](https://offwhitesecurity.dev/malware-development/portable-executable-pe/nt-headers/).
535/// Although it's called "optional", it's actually required for PE image files.
536///
537/// It is meant to represent either
538///
539/// * [`IMAGE_OPTIONAL_HEADER32`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32); or
540/// * [`IMAGE_OPTIONAL_HEADER64`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64).
541///
542/// Whether it's 32 or 64-bit is determined by the [`StandardFields::magic`] and by the value
543/// [`CoffHeader::size_of_optional_header`](crate::pe::header::CoffHeader::size_of_optional_header).
544///
545/// ## Position in PE binary
546///
547/// The optional header is located after [`CoffHeader`](crate::pe::header::CoffHeader) and before
548/// section table.
549#[derive(Debug, PartialEq, Copy, Clone)]
550#[doc(alias = "IMAGE_OPTIONAL_HEADER32")]
551#[doc(alias = "IMAGE_OPTIONAL_HEADER64")]
552pub struct OptionalHeader {
553    /// Unified standard (COFF) fields. See [`StandardFields`] to learn more.
554    pub standard_fields: StandardFields,
555    /// Unified Windows fields. See [`WindowsFields`] to learn more.
556    pub windows_fields: WindowsFields,
557    /// Data directories. See [`DataDirectories`](data_directories::DataDirectories) to learn more.
558    pub data_directories: data_directories::DataDirectories,
559}
560
561/// Magic number for 32-bit binary (`PE32`).
562pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC: u16 = 0x10b;
563/// Magic number for 64-bit binary (`PE32+`).
564pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC: u16 = 0x20b;
565/// Magic number for a ROM image.
566///
567/// More info: <https://superuser.com/questions/156994/what-sort-of-program-has-its-pe-executable-header-set-to-rom-image>.
568pub const IMAGE_ROM_OPTIONAL_HDR_MAGIC: u16 = 0x107;
569
570impl OptionalHeader {
571    /// Returns the container type of the PE binary.
572    pub fn container(&self) -> error::Result<container::Container> {
573        match self.standard_fields.magic {
574            MAGIC_32 => Ok(container::Container::Little),
575            MAGIC_64 => Ok(container::Container::Big),
576            magic => Err(error::Error::BadMagic(u64::from(magic))),
577        }
578    }
579}
580
581impl<'a> ctx::TryFromCtx<'a, Endian> for OptionalHeader {
582    type Error = crate::error::Error;
583    fn try_from_ctx(bytes: &'a [u8], _: Endian) -> error::Result<(Self, usize)> {
584        let magic = bytes.pread_with::<u16>(0, LE)?;
585        let offset = &mut 0;
586        let (standard_fields, windows_fields): (StandardFields, WindowsFields) = match magic {
587            MAGIC_32 => {
588                let standard_fields = bytes.gread_with::<StandardFields32>(offset, LE)?.into();
589                let windows_fields = bytes.gread_with::<WindowsFields32>(offset, LE)?.into();
590                (standard_fields, windows_fields)
591            }
592            MAGIC_64 => {
593                let standard_fields = bytes.gread_with::<StandardFields64>(offset, LE)?.into();
594                let windows_fields = bytes.gread_with::<WindowsFields64>(offset, LE)?;
595                (standard_fields, windows_fields)
596            }
597            _ => return Err(error::Error::BadMagic(u64::from(magic))),
598        };
599        let data_directories = data_directories::DataDirectories::parse(
600            &bytes,
601            windows_fields.number_of_rva_and_sizes as usize,
602            offset,
603        )?;
604        Ok((
605            OptionalHeader {
606                standard_fields,
607                windows_fields,
608                data_directories,
609            },
610            0,
611        )) // TODO: FIXME
612    }
613}
614
615impl ctx::TryIntoCtx<scroll::Endian> for OptionalHeader {
616    type Error = error::Error;
617
618    fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
619        let offset = &mut 0;
620        match self.standard_fields.magic {
621            MAGIC_32 => {
622                bytes.gwrite_with::<StandardFields32>(self.standard_fields.into(), offset, ctx)?;
623                bytes.gwrite_with(WindowsFields32::try_from(self.windows_fields)?, offset, ctx)?;
624                bytes.gwrite_with(self.data_directories, offset, ctx)?;
625            }
626            MAGIC_64 => {
627                bytes.gwrite_with::<StandardFields64>(self.standard_fields.into(), offset, ctx)?;
628                bytes.gwrite_with(self.windows_fields, offset, ctx)?;
629                bytes.gwrite_with(self.data_directories, offset, ctx)?;
630            }
631            _ => panic!(),
632        }
633        Ok(*offset)
634    }
635}
636
637#[cfg(test)]
638mod tests {
639    use super::*;
640    #[test]
641    fn sizeof_standards32() {
642        assert_eq!(
643            ::std::mem::size_of::<StandardFields32>(),
644            SIZEOF_STANDARD_FIELDS_32
645        );
646    }
647    #[test]
648    fn sizeof_windows32() {
649        assert_eq!(
650            ::std::mem::size_of::<WindowsFields32>(),
651            SIZEOF_WINDOWS_FIELDS_32
652        );
653    }
654    #[test]
655    fn sizeof_standards64() {
656        assert_eq!(
657            ::std::mem::size_of::<StandardFields64>(),
658            SIZEOF_STANDARD_FIELDS_64
659        );
660    }
661    #[test]
662    fn sizeof_windows64() {
663        assert_eq!(
664            ::std::mem::size_of::<WindowsFields64>(),
665            SIZEOF_WINDOWS_FIELDS_64
666        );
667    }
668}