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}