pe_parser/
optional.rs

1use bytemuck::{Pod, Zeroable, checked::{try_from_bytes}};
2use num_derive::FromPrimitive;    
3use num_traits::FromPrimitive;
4use bitflags::bitflags;
5use core::{fmt, str};
6use crate::{prelude::*, Error};
7
8/// Magic values that determine if an Optional Header is 
9/// PE32 (32-bit) or PE32+ (64-bit)
10#[derive(FromPrimitive, Debug)]
11#[repr(u16)]
12pub enum Magic {
13    /// Magic value for 32-bit PEs
14    PE32 = 0x10b,
15    /// Magic value for 64-bit PEs
16    PE64 = 0x20b
17}
18
19/// Struct containing basic information (address and size) of each table. 
20#[derive(Copy, Clone, Pod, Zeroable, Default)]
21#[repr(C)]
22pub struct DataDirectories {
23    /// The export table (.edata) address and size. (Image Only)
24    pub export_table: DataDirectory,
25    /// The import table (.idata) address and size.
26    pub import_table: DataDirectory,
27    /// The resource table (.rsrc) address and size.
28    pub resource_table: DataDirectory,
29    /// The exception table (.pdata) address and size.
30    pub exception_table: DataDirectory,
31    /// The attribute certificate table address and size. (Image Only)
32    pub certificate_table: DataDirectory,
33    /// The base relocation table (.reloc) address and size. (Image Only)
34    pub base_relocation_table: DataDirectory,
35    /// The debug data (.debug) starting address and size.
36    pub debug: DataDirectory,
37    /// Reserved, must be 0.
38    pub architecture: DataDirectory,
39    /// The RVA of the value to be stored in the global pointer register.
40    /// The size member of this structure must be set to zero.
41    pub global_ptr: DataDirectory,
42    /// The thread local storage (TLS) table (.tls) address and size.
43    pub tls_table: DataDirectory,
44    /// The load configuration table address and size. (Image Only)
45    pub load_config_table: DataDirectory,
46    /// The bound import table address and size.
47    pub bound_import: DataDirectory,
48    /// The import address table address and size.
49    pub import_address_table: DataDirectory,
50    /// The delay import descriptor address and size. (Image Only)
51    pub delay_import_descriptor: DataDirectory,
52    /// The CLR runtime header (.cormeta) address and size. (Object Only
53    pub clr_runtime_header: DataDirectory,
54    /// Reserved, must be zero.
55    pub reserved: DataDirectory
56}
57
58impl fmt::Display for DataDirectories {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        writeln!(f, "Data Directories")?;
61        writeln!(f, "----------------")?;
62        writeln!(f, "Export Table:            {:#010x} ({})", self.export_table.virtual_address, self.export_table.size)?;
63        writeln!(f, "Import Table:            {:#010x} ({})", self.import_table.virtual_address, self.import_table.size)?;
64        writeln!(f, "Resource Table:          {:#010x} ({})", self.resource_table.virtual_address, self.resource_table.size)?;
65        writeln!(f, "Exception Table:         {:#010x} ({})", self.exception_table.virtual_address, self.exception_table.size)?;
66        writeln!(f, "Certificiate Table:      {:#010x} ({})", self.certificate_table.virtual_address, self.certificate_table.size)?;
67        writeln!(f, "Base Relocation Table:   {:#010x} ({})", self.base_relocation_table.virtual_address, self.base_relocation_table.size)?;
68        writeln!(f, "Debug:                   {:#010x} ({})", self.debug.virtual_address, self.debug.size)?;
69        writeln!(f, "Architecture:            {:#010x} ({})", self.architecture.virtual_address, self.architecture.size)?;
70        writeln!(f, "Global Pointer:          {:#010x} ({})", self.global_ptr.virtual_address, self.global_ptr.size)?;
71        writeln!(f, "TLS Table:               {:#010x} ({})", self.tls_table.virtual_address, self.tls_table.size)?;
72        writeln!(f, "Load Config Table:       {:#010x} ({})", self.load_config_table.virtual_address, self.load_config_table.size)?;
73        writeln!(f, "Bound Import:            {:#010x} ({})", self.bound_import.virtual_address, self.bound_import.size)?;
74        writeln!(f, "Import Address Table:    {:#010x} ({})", self.import_address_table.virtual_address, self.import_address_table.size)?;
75        writeln!(f, "Delay Import Descriptor: {:#010x} ({})", self.delay_import_descriptor.virtual_address, self.delay_import_descriptor.size)?;
76        writeln!(f, "CLR Runtime Header:      {:#010x} ({})", self.clr_runtime_header.virtual_address, self.clr_runtime_header.size)?;
77        writeln!(f, "Reserved:                {:#010x} ({})", self.reserved.virtual_address, self.reserved.size)?;
78
79        Ok(())
80    }
81}
82
83/// Each data directory gives the address and size of a table or string that Windows uses.
84/// These data directory entries are all loaded into memory so that the system can use them at run time.
85/// A data directory is an 8-byte field that has the following declaration:
86#[derive(Copy, Clone, Pod, Zeroable, Default)]
87#[repr(C)]
88pub struct DataDirectory {
89    /// RVA of the table. The RVA is the address of the table relative to the base address of the image when the table is loaded.
90    pub virtual_address: u32,
91    /// Size of the table in bytes.
92    pub size: u32
93}
94
95/// PE32 Optional Header (Image Only)
96#[derive(Copy, Clone, Pod, Zeroable, Default)]
97#[repr(C)]
98pub struct OptionalHeader32 {
99    /// The unsigned integer that identifies the state of the image file.
100    /// The most common number is 0x10B, which identifies it as a normal executable file.
101    /// 0x107 identifies it as a ROM image, and 0x20B identifies it as a PE32+ executable.
102    pub magic: u16,
103    /// The linker major version number.
104    pub major_linker_version: u8,
105    /// The linker minor version number.
106    pub minor_linker_version: u8,
107    /// The size of the code (text) section, or the sum of all code sections if there are multiple sections.
108    pub size_of_code: u32,
109    /// The size of the initialized data section, or the sum of all such sections if there are multiple data sections.
110    pub size_of_initialized_data: u32,
111    /// The size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections.
112    pub size_of_uninitialized_data: u32,
113    /// The address of the entry point relative to the image base when the executable file is loaded into memory.
114    /// For program images, this is the starting address.
115    /// For device drivers, this is the address of the initialization function.
116    /// An entry point is optional for DLLs. When no entry point is present, this field must be zero.
117    pub address_of_entry_point: u32,
118    /// The address that is relative to the image base of the beginning-of-code section when it is loaded into memory.
119    pub base_of_code: u32,
120    /// (PE32 Only) The address that is relative to the image base of the beginning-of-data section when it is loaded into memory.
121    pub base_of_data: u32,
122    /// The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K.
123    /// The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000.
124    /// The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.
125    pub image_base: u32,
126    /// The alignment (in bytes) of sections when they are loaded into memory.
127    /// It must be greater than or equal to `file_alignment`. The default is the page size for the architecture.
128    pub section_alignment: u32,
129    /// The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
130    /// The value should be a power of 2 between 512 and 64 K, inclusive. The default is 512.
131    /// If the `section_alignment` is less than the architecture's page size, then `file_alignment` must match `section_alignment`.
132    pub file_alignment: u32,
133    /// The major version number of the required operating system.
134    pub major_operating_system_version: u16,
135    /// The minor version number of the required operating system.
136    pub minor_operating_system_version: u16,
137    /// The major version number of the image.
138    pub major_image_version: u16,
139    /// The minor version number of the image.
140    pub minor_image_version: u16,
141    /// The major version number of the subsystem.
142    pub major_subsystem_version: u16,
143    /// The minor version number of the subsystem.
144    pub minor_subsystem_version: u16,
145    /// Reserved, must be zero.
146    pub win32_version_value: u32,
147    /// The size (in bytes) of the image, including all headers, as the image is loaded in memory. It must be a multiple of `section_alignment`.
148    pub size_of_image: u32,
149    /// The combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of `file_alignment`.
150    pub size_of_headers: u32,
151    /// The image file checksum. The algorithm for computing the checksum is incorporated into IMAGHELP.DLL. 
152    /// The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process.
153    pub check_sum: u32,
154    /// The subsystem that is required to run this image.
155    pub subsystem: u16,
156    /// Bitflag characteristics that describe how a DLL should be loaded.
157    pub dll_characteristics: u16,
158    /// The size of the stack to reserve. Only `size_of_stack_commit` is committed; the rest is made available one page at a time until the reserve size is reached.
159    pub size_of_stack_reserve: u32,
160    /// The size of the stack to commit.
161    pub size_of_stack_commit: u32,
162    /// The size of the local heap space to reserve. Only `size_of_heap_commit` is committed; the rest is made available one page at a time until the reserve size is reached.
163    pub size_of_heap_reserve: u32,
164    /// The size of the local heap space to commit.
165    pub size_of_heap_commit: u32,
166    /// Reserved, must be zero.
167    pub loader_flags: u32,
168    /// The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
169    pub number_of_rva_and_sizes: u32,
170    /// Struct containing basic information (address and size) of each table. 
171    pub data_directories: DataDirectories
172}
173
174impl fmt::Display for OptionalHeader32 {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        let subsystem = self.get_subsystem()
177            .expect("Failed to get subsystem");
178        let dll_characteristics = self.get_dll_characteristics()
179            .expect("Failed to get DLL characteristics");
180
181        writeln!(f, "Optional Header")?;
182        writeln!(f, "---------------")?;
183        writeln!(f, "Magic:                      PE32")?;
184        writeln!(f, "Linker Version:             {}.{}", self.major_linker_version, self.minor_linker_version)?;
185        writeln!(f, "Size of Code:               {}", self.size_of_code)?;
186        writeln!(f, "Size of Initialized Data:   {}", self.size_of_initialized_data)?;
187        writeln!(f, "Size of Uninitialized Data: {}", self.size_of_uninitialized_data)?;
188        writeln!(f, "Address of Entry Point:     {:#010x}", self.address_of_entry_point)?;
189        writeln!(f, "Base of Code:               {:#010x}", self.base_of_code)?;
190        writeln!(f, "Base of Data:               {:#010x}", self.base_of_data)?;
191        writeln!(f, "Image Base:                 {:#010x}", self.image_base)?;
192        writeln!(f, "Section Alignment:          {}", self.section_alignment)?;
193        writeln!(f, "File Alignment:             {}", self.file_alignment)?;
194        writeln!(f, "Operating System Version:   {}.{}", self.major_operating_system_version, self.minor_operating_system_version)?;
195        writeln!(f, "Image Version:              {}.{}", self.major_image_version, self.minor_image_version)?;
196        writeln!(f, "Subsystem Version:          {}.{}", self.major_subsystem_version, self.minor_linker_version)?;
197        writeln!(f, "Win32 Version Value:        {}", self.win32_version_value)?;
198        writeln!(f, "Size of Image:              {}", self.size_of_image)?;
199        writeln!(f, "Size of Headers:            {}", self.size_of_headers)?;
200        writeln!(f, "CheckSum:                   {}", self.check_sum)?;
201        writeln!(f, "Subsystem:                  {:?}", subsystem)?;
202        writeln!(f, "DLL Characteristics:        {}", dll_characteristics)?;
203        writeln!(f, "Size of Stack Reserve:      {}", self.size_of_stack_reserve)?;
204        writeln!(f, "Size of Stack Commit:       {}", self.size_of_stack_commit)?;
205        writeln!(f, "Size of Heap Reserve:       {}", self.size_of_heap_reserve)?;
206        writeln!(f, "Size of Heap Commit:        {}", self.size_of_heap_commit)?;
207        writeln!(f, "Loader Flags:               {}", self.loader_flags)?;
208        writeln!(f, "Number of RVA and Sizes:    {}", self.number_of_rva_and_sizes)?;
209        write!(f, "\n{}", self.data_directories)?;
210
211        Ok(())
212    }
213}
214
215/// PE32+ Optional Header (Image Only)
216#[derive(Copy, Clone, Pod, Zeroable, Default)]
217#[repr(C)]
218pub struct OptionalHeader64 {
219    /// The unsigned integer that identifies the state of the image file.
220    /// The most common number is 0x10B, which identifies it as a normal executable file.
221    /// 0x107 identifies it as a ROM image, and 0x20B identifies it as a PE32+ executable.
222    pub magic: u16,
223    /// The linker major version number.
224    pub major_linker_version: u8,
225    /// The linker minor version number.
226    pub minor_linker_version: u8,
227    /// The size of the code (text) section, or the sum of all code sections if there are multiple sections.
228    pub size_of_code: u32,
229    /// The size of the initialized data section, or the sum of all such sections if there are multiple data sections.
230    pub size_of_initialized_data: u32,
231    /// The size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections.
232    pub size_of_uninitialized_data: u32,
233    /// The address of the entry point relative to the image base when the executable file is loaded into memory.
234    /// For program images, this is the starting address.
235    /// For device drivers, this is the address of the initialization function.
236    /// An entry point is optional for DLLs. When no entry point is present, this field must be zero.
237    pub address_of_entry_point: u32,
238    /// The address that is relative to the image base of the beginning-of-code section when it is loaded into memory.
239    pub base_of_code: u32,
240    /// The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K.
241    /// The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000.
242    /// The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.
243    pub image_base: u64,
244    /// The alignment (in bytes) of sections when they are loaded into memory.
245    /// It must be greater than or equal to `file_alignment`. The default is the page size for the architecture.
246    pub section_alignment: u32,
247    /// The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
248    /// The value should be a power of 2 between 512 and 64 K, inclusive. The default is 512.
249    /// If the `section_alignment` is less than the architecture's page size, then `file_alignment` must match `section_alignment`.
250    pub file_alignment: u32,
251    /// The major version number of the required operating system.
252    pub major_operating_system_version: u16,
253    /// The minor version number of the required operating system.
254    pub minor_operating_system_version: u16,
255    /// The major version number of the image.
256    pub major_image_version: u16,
257    /// The minor version number of the image.
258    pub minor_image_version: u16,
259    /// The major version number of the subsystem.
260    pub major_subsystem_version: u16,
261    /// The minor version number of the subsystem.
262    pub minor_subsystem_version: u16,
263    /// Reserved, must be zero.
264    pub win32_version_value: u32,
265    /// The size (in bytes) of the image, including all headers, as the image is loaded in memory. It must be a multiple of `section_alignment`.
266    pub size_of_image: u32,
267    /// The combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of `file_alignment`.
268    pub size_of_headers: u32,
269    /// The image file checksum. The algorithm for computing the checksum is incorporated into IMAGHELP.DLL. 
270    /// The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process.
271    pub check_sum: u32,
272    /// The subsystem that is required to run this image.
273    pub subsystem: u16,
274    /// Bitflag characteristics that describe how a DLL should be loaded.
275    pub dll_characteristics: u16,
276    /// The size of the stack to reserve. Only `size_of_stack_commit` is committed; the rest is made available one page at a time until the reserve size is reached.
277    pub size_of_stack_reserve: u64,
278    /// The size of the stack to commit.
279    pub size_of_stack_commit: u64,
280    /// The size of the local heap space to reserve. Only `size_of_heap_commit` is committed; the rest is made available one page at a time until the reserve size is reached.
281    pub size_of_heap_reserve: u64,
282    /// The size of the local heap space to commit.
283    pub size_of_heap_commit: u64,
284    /// Reserved, must be zero.
285    pub loader_flags: u32,
286    /// The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
287    pub number_of_rva_and_sizes: u32,
288    /// Struct containing basic information (address and size) of each table. 
289    pub data_directories: DataDirectories
290}
291
292impl fmt::Display for OptionalHeader64 {
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294        let subsystem = self.get_subsystem()
295            .expect("Failed to get subsystem");
296        let dll_characteristics = self.get_dll_characteristics()
297            .expect("Failed to get DLL characteristics");
298
299        writeln!(f, "Optional Header")?;
300        writeln!(f, "---------------")?;
301        writeln!(f, "Magic:                      PE32+")?;
302        writeln!(f, "Linker Version:             {}.{}", self.major_linker_version, self.minor_linker_version)?;
303        writeln!(f, "Size of Code:               {}", self.size_of_code)?;
304        writeln!(f, "Size of Initialized Data:   {}", self.size_of_initialized_data)?;
305        writeln!(f, "Size of Uninitialized Data: {}", self.size_of_uninitialized_data)?;
306        writeln!(f, "Address of Entry Point:     {:#010x}", self.address_of_entry_point)?;
307        writeln!(f, "Base of Code:               {:#010x}", self.base_of_code)?;
308        writeln!(f, "Image Base:                 {:#010x}", self.image_base)?;
309        writeln!(f, "Section Alignment:          {}", self.section_alignment)?;
310        writeln!(f, "File Alignment:             {}", self.file_alignment)?;
311        writeln!(f, "Operating System Version:   {}.{}", self.major_operating_system_version, self.minor_operating_system_version)?;
312        writeln!(f, "Image Version:              {}.{}", self.major_image_version, self.minor_image_version)?;
313        writeln!(f, "Subsystem Version:          {}.{}", self.major_subsystem_version, self.minor_linker_version)?;
314        writeln!(f, "Win32 Version Value:        {}", self.win32_version_value)?;
315        writeln!(f, "Size of Image:              {}", self.size_of_image)?;
316        writeln!(f, "Size of Headers:            {}", self.size_of_headers)?;
317        writeln!(f, "CheckSum:                   {}", self.check_sum)?;
318        writeln!(f, "Subsystem:                  {:?}", subsystem)?;
319        writeln!(f, "DLL Characteristics:        {}", dll_characteristics)?;
320        writeln!(f, "Size of Stack Reserve:      {}", self.size_of_stack_reserve)?;
321        writeln!(f, "Size of Stack Commit:       {}", self.size_of_stack_commit)?;
322        writeln!(f, "Size of Heap Reserve:       {}", self.size_of_heap_reserve)?;
323        writeln!(f, "Size of Heap Commit:        {}", self.size_of_heap_commit)?;
324        writeln!(f, "Loader Flags:               {}", self.loader_flags)?;
325        writeln!(f, "Number of RVA and Sizes:    {}", self.number_of_rva_and_sizes)?;
326        write!(f, "\n{}", self.data_directories)?;
327
328        Ok(())
329    }
330}
331
332/// The following values defined for the Subsystem field of the optional header 
333/// determine which Windows subsystem (if any) is required to run the image.
334#[derive(FromPrimitive, Debug)]
335#[repr(u16)]
336pub enum Subsystem {
337    /// An unknown subsystem
338    Unknown = 0,
339    /// Device drivers and native Windows processes
340    Native = 1,
341    /// The Windows graphical user interface (GUI) subsystem
342    WindowsGUI = 2,
343    /// The Windows character subsystem
344    WindowsCUI = 3,
345    /// The OS/2 character subsystem
346    OS2CUI = 5,
347    /// The Posix character subsystem
348    PosixCUI = 7,
349    /// Native Win9x driver
350    NativeWindows = 8,
351    /// Windows CE
352    WindowsCEGUI = 9,
353    /// An Extensible Firmware Interface (EFI) application
354    EFIApplication = 10,
355    /// An EFI driver with boot services
356    EFIBootServiceDriver = 11,
357    /// An EFI driver with run-time services
358    EFIRuntimeDriver = 12,
359    /// An EFI ROM image
360    EFIROM = 13,
361    /// XBOX
362    XBOX = 14,
363    /// Windows boot application
364    WindowsBootApplication = 16
365}
366
367bitflags! {
368    /// Bitflags that contain various information about
369    /// how a given DLL should be loaded.
370    pub struct DLLCharacteristics: u16 {
371        /// Reserved, must be zero.
372        const IMAGE_DLLCHARACTERISTICS_RESERVED1 = 0x0001;
373        /// Reserved, must be zero.
374        const IMAGE_DLLCHARACTERISTICS_RESERVED2 = 0x0002;
375        /// Reserved, must be zero.
376        const IMAGE_DLLCHARACTERISTICS_RESERVED4 = 0x0004;
377        /// Reserved, must be zero.
378        const IMAGE_DLLCHARACTERISTICS_RESERVED8 = 0x0008;
379        /// Image can handle a high entropy 64-bit virtual address space.
380        const IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020;
381        /// DLL can be relocated at load time.
382        const IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040;
383        /// Code Integrity checks are enforced.
384        const IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080;
385        /// Image is NX compatible.
386        const IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100;
387        /// Isolation aware, but do not isolate the image.
388        const IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200;
389        /// Does not use structured exception (SE) handling. 
390        /// No SE handler may be called in this image.
391        const IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400;
392        /// Do not bind the image.
393        const IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800;
394        /// Image must execute in an AppContainer.
395        const IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000;
396        /// A WDM driver.
397        const IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000;
398        /// Image supports Control Flow Guard.
399        const IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000;
400        /// Terminal Server aware.
401        const IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000;
402    }
403}
404
405// Allow DLL Characteristics flags to be easily printed
406impl fmt::Debug for DLLCharacteristics {
407    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
408        fmt::Debug::fmt(&self.0, f)
409    }
410}
411
412impl fmt::Display for DLLCharacteristics {
413    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
414        fmt::Display::fmt(&self.0, f)
415    }
416}
417
418impl str::FromStr for DLLCharacteristics {
419    type Err = bitflags::parser::ParseError;
420
421    fn from_str(flags: &str) -> Result<Self, Self::Err> {
422        Ok(Self(flags.parse()?))
423    }
424}
425
426/// Helper functions for optional header structs
427pub trait Optional: Sized {
428    /// Returns the subsystem as an enum
429    fn get_subsystem(&self) -> Option<Subsystem>;
430    /// Returns the DLL Characteristics as bitflags
431    fn get_dll_characteristics(&self) -> Option<DLLCharacteristics>;
432    /// Parse optional header (either PE32, or PE32+) starting at
433    /// the given offset.
434    fn parse_optional_header(binary: &[u8], offset: &mut usize) -> Result<Self, Error>;
435}
436
437impl Optional for OptionalHeader32 {
438    fn get_subsystem(&self) -> Option<Subsystem> {
439        Subsystem::from_u16(self.subsystem)
440    }
441
442    fn get_dll_characteristics(&self) -> Option<DLLCharacteristics> {
443        DLLCharacteristics::from_bits(self.dll_characteristics)
444    }
445
446    fn parse_optional_header(binary: &[u8], offset: &mut usize) -> Result<Self, Error> {
447        let size = size_of::<Self>();
448        let slice = match binary.get(*offset..*offset+size) {
449            Some(slice) => slice,
450            None => {
451                return Err(Error::OffsetOutOfRange);
452            }
453        };
454
455        let optional_header = try_from_bytes::<OptionalHeader32>(slice);
456        *offset += size;
457
458        match optional_header.copied() {
459            Ok(header) => {
460                Ok(header)
461            }
462            Err(_) => {
463                Err(Error::BadOptionalHeader)
464            }
465        }
466    }
467}
468
469impl Optional for OptionalHeader64 {
470    fn get_subsystem(&self) -> Option<Subsystem> {
471        Subsystem::from_u16(self.subsystem)
472    }
473
474    fn get_dll_characteristics(&self) -> Option<DLLCharacteristics> {
475        DLLCharacteristics::from_bits(self.dll_characteristics)
476    }
477
478    fn parse_optional_header(binary: &[u8], offset: &mut usize) -> Result<Self, Error> {
479        let size = size_of::<Self>();
480        let slice = match binary.get(*offset..*offset+size) {
481            Some(slice) => slice,
482            None => {
483                return Err(Error::OffsetOutOfRange);
484            }
485        };
486    
487        let optional_header = try_from_bytes::<OptionalHeader64>(slice);
488        *offset += size;
489        match optional_header.copied() {
490            Ok(header) => {
491                Ok(header)
492            }
493            Err(_) => {
494                Err(Error::BadOptionalHeader)
495            }
496        }
497    }
498}