Skip to main content

pe_assembler/types/
mod.rs

1#![doc = include_str!("readme.md")]
2
3pub use self::{
4    dos::DosHeader,
5    nt::NtHeader,
6    tables::{ExportTable, ImportEntry, ImportTable},
7};
8use byteorder::{LittleEndian, ReadBytesExt};
9use gaia_types::helpers::Architecture;
10use serde::{Deserialize, Serialize};
11use std::{
12    fmt::{Display, Formatter},
13    io::{Read, Write},
14};
15
16pub mod coff;
17mod dos;
18mod nt;
19pub mod tables;
20
21pub use coff::*;
22use gaia_types::GaiaError;
23
24/// PE subsystem type enum
25///
26/// Defines the various subsystem types that a Windows PE file can use,
27/// which determine the runtime environment and dependencies of the program.
28#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
29pub enum SubsystemType {
30    /// Console application, runs in a console window
31    Console,
32    /// Windows GUI application, has a graphical interface
33    Windows,
34    /// Native driver, runs in kernel mode
35    Native,
36    /// POSIX subsystem application
37    Posix,
38    /// Windows CE subsystem
39    WindowsCe,
40    /// EFI application
41    Efi,
42    /// EFI boot service driver
43    EfiBootServiceDriver,
44    /// EFI runtime driver
45    EfiRuntimeDriver,
46    /// EFI ROM image
47    EfiRom,
48    /// Xbox application
49    Xbox,
50    /// Windows boot application
51    WindowsBootApplication,
52}
53
54impl Display for SubsystemType {
55    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56        match self {
57            SubsystemType::Console => write!(f, "Console application"),
58            SubsystemType::Windows => write!(f, "Windows GUI application"),
59            SubsystemType::Native => write!(f, "Native driver"),
60            SubsystemType::Posix => write!(f, "POSIX subsystem application"),
61            SubsystemType::WindowsCe => write!(f, "Windows CE subsystem"),
62            SubsystemType::Efi => write!(f, "EFI application"),
63            SubsystemType::EfiBootServiceDriver => write!(f, "EFI boot service driver"),
64            SubsystemType::EfiRuntimeDriver => write!(f, "EFI runtime driver"),
65            SubsystemType::EfiRom => write!(f, "EFI ROM image"),
66            SubsystemType::Xbox => write!(f, "Xbox application"),
67            SubsystemType::WindowsBootApplication => write!(f, "Windows boot application"),
68        }
69    }
70}
71
72impl From<u16> for SubsystemType {
73    /// Create subsystem type from u16 value
74    ///
75    /// # Arguments
76    /// * `value` - Numeric value of the subsystem type
77    ///
78    /// # Returns
79    /// Returns the corresponding subsystem type, or Console for unknown types
80    fn from(value: u16) -> Self {
81        match value {
82            1 => SubsystemType::Native,
83            2 => SubsystemType::Windows,
84            3 => SubsystemType::Console,
85            7 => SubsystemType::Posix,
86            9 => SubsystemType::WindowsCe,
87            10 => SubsystemType::Efi,
88            11 => SubsystemType::EfiBootServiceDriver,
89            12 => SubsystemType::EfiRuntimeDriver,
90            13 => SubsystemType::EfiRom,
91            14 => SubsystemType::Xbox,
92            16 => SubsystemType::WindowsBootApplication,
93            _ => SubsystemType::Console, // Default value
94        }
95    }
96}
97
98impl Default for DataDirectory {
99    fn default() -> Self {
100        Self { virtual_address: 0, size: 0 }
101    }
102}
103
104/// Optional header structure
105///
106/// Contains loading and runtime information for the PE file, such as entry point address,
107/// memory layout, version info, etc. This structure is crucial for the Windows loader
108/// to correctly load and execute the program.
109#[derive(Clone, Debug, Serialize, Deserialize)]
110pub struct OptionalHeader {
111    /// Magic number identifying PE32 or PE32+ format
112    pub magic: u16,
113    /// Major version of the linker
114    pub major_linker_version: u8,
115    /// Minor version of the linker
116    pub minor_linker_version: u8,
117    /// Total size of the code section in bytes
118    pub size_of_code: u32,
119    /// Total size of initialized data
120    pub size_of_initialized_data: u32,
121    /// Total size of uninitialized data
122    pub size_of_uninitialized_data: u32,
123    /// RVA (Relative Virtual Address) of the program entry point
124    pub address_of_entry_point: u32,
125    /// Starting RVA of the code section
126    pub base_of_code: u32,
127    /// Starting RVA of the data section, only valid for PE32
128    pub base_of_data: Option<u32>, // Only for PE32
129    /// Preferred loading address of the image
130    pub image_base: u64,
131    /// Alignment granularity of sections in memory
132    pub section_alignment: u32,
133    /// Alignment granularity of sections in the file
134    pub file_alignment: u32,
135    /// Major version of the required operating system
136    pub major_operating_system_version: u16,
137    /// Minor version of the required operating system
138    pub minor_operating_system_version: u16,
139    /// Major version of the image
140    pub major_image_version: u16,
141    /// Minor version of the image
142    pub minor_image_version: u16,
143    /// Major version of the subsystem
144    pub major_subsystem_version: u16,
145    /// Minor version of the subsystem
146    pub minor_subsystem_version: u16,
147    /// Reserved field, must be 0
148    pub win32_version_value: u32,
149    /// Total size of the image, including all headers and sections
150    pub size_of_image: u32,
151    /// Total size of all headers
152    pub size_of_headers: u32,
153    /// Checksum of the image, used for kernel mode and system DLLs
154    pub checksum: u32,
155    /// Subsystem type, defining the runtime environment
156    pub subsystem: SubsystemType,
157    /// DLL characteristics flags, describing various properties of the DLL
158    pub dll_characteristics: u16,
159    /// Virtual memory size reserved for the thread stack
160    pub size_of_stack_reserve: u64,
161    /// Virtual memory size committed for the thread stack
162    pub size_of_stack_commit: u64,
163    /// Virtual memory size reserved for the process heap
164    pub size_of_heap_reserve: u64,
165    /// Virtual memory size committed for the process heap
166    pub size_of_heap_commit: u64,
167    /// Reserved field, must be 0
168    pub loader_flags: u32,
169    /// Number of entries in the data directory table
170    pub number_of_rva_and_sizes: u32,
171    /// Data directory table, containing info about various data directories
172    pub data_directories: Vec<DataDirectory>,
173}
174
175impl OptionalHeader {
176    /// Create a standard optional header suitable for .NET programs
177    pub fn new(
178        entry_point: u32,
179        image_base: u64,
180        size_of_code: u32,
181        size_of_headers: u32,
182        size_of_image: u32,
183        subsystem: SubsystemType,
184    ) -> Self {
185        let mut data_directories = Vec::with_capacity(16);
186        // Initialize 16 standard data directories
187        for _ in 0..16 {
188            data_directories.push(DataDirectory::default());
189        }
190
191        Self {
192            magic: 0x020B, // PE32+
193            major_linker_version: 14,
194            minor_linker_version: 0,
195            size_of_code,
196            size_of_initialized_data: 0,
197            size_of_uninitialized_data: 0,
198            address_of_entry_point: entry_point,
199            base_of_code: 0x2000,
200            base_of_data: None, // Not used in PE32+
201            image_base,
202            section_alignment: 0x2000,
203            file_alignment: 0x200,
204            major_operating_system_version: 6,
205            minor_operating_system_version: 0,
206            major_image_version: 0,
207            minor_image_version: 0,
208            major_subsystem_version: 6,
209            minor_subsystem_version: 0,
210            win32_version_value: 0,
211            size_of_image,
212            size_of_headers,
213            checksum: 0,
214            subsystem,
215            // Enable DYNAMIC_BASE and other security features
216            // NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE | DYNAMIC_BASE
217            dll_characteristics: 0x8540, // NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE | DYNAMIC_BASE
218            size_of_stack_reserve: 0x100000,
219            size_of_stack_commit: 0x1000,
220            size_of_heap_reserve: 0x100000,
221            size_of_heap_commit: 0x1000,
222            loader_flags: 0,
223            number_of_rva_and_sizes: 16,
224            data_directories,
225        }
226    }
227
228    /// Create optional header based on architecture
229    pub fn new_for_architecture(
230        architecture: &Architecture,
231        entry_point: u32,
232        image_base: u64,
233        size_of_code: u32,
234        size_of_headers: u32,
235        size_of_image: u32,
236        subsystem: SubsystemType,
237    ) -> Self {
238        let mut data_directories = Vec::with_capacity(16);
239        // Initialize 16 standard data directories
240        for _ in 0..16 {
241            data_directories.push(DataDirectory::default());
242        }
243
244        let (magic, base_of_data) = match architecture {
245            Architecture::X86 => (0x010B, Some(0x2000)), // PE32
246            Architecture::X86_64 => (0x020B, None),      // PE32+
247            _ => (0x010B, Some(0x2000)),                 // Default to PE32
248        };
249
250        Self {
251            magic,
252            major_linker_version: 14,
253            minor_linker_version: 0,
254            size_of_code,
255            size_of_initialized_data: 0,
256            size_of_uninitialized_data: 0,
257            address_of_entry_point: entry_point,
258            base_of_code: 0x1000,
259            base_of_data,
260            image_base,
261            section_alignment: 0x1000,
262            file_alignment: 0x200,
263            major_operating_system_version: 6,
264            minor_operating_system_version: 0,
265            major_image_version: 0,
266            minor_image_version: 0,
267            major_subsystem_version: 6,
268            minor_subsystem_version: 0,
269            win32_version_value: 0,
270            size_of_image,
271            size_of_headers,
272            checksum: 0,
273            subsystem,
274            dll_characteristics: 0x8160, // DYNAMIC_BASE | NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE
275            size_of_stack_reserve: 0x100000,
276            size_of_stack_commit: 0x1000,
277            size_of_heap_reserve: 0x100000,
278            size_of_heap_commit: 0x1000,
279            loader_flags: 0,
280            number_of_rva_and_sizes: 16,
281            data_directories,
282        }
283    }
284}
285
286/// PE header structure
287///
288/// Combines all header information for a PE file, including DOS, NT, COFF, and optional headers.
289/// This structure provides complete metadata for the PE file.
290#[derive(Clone, Debug, Serialize, Deserialize)]
291pub struct PeHeader {
292    /// DOS header, contains DOS compatibility info
293    pub dos_header: DosHeader,
294    /// NT header, contains PE signature
295    pub nt_header: NtHeader,
296    /// COFF header, contains object file info
297    pub coff_header: CoffHeader,
298    /// Optional header, contains loading and runtime info
299    pub optional_header: OptionalHeader,
300}
301
302/// PE section structure
303///
304/// Contains detailed information for each section in the PE file, including name, virtual address, size, etc.
305/// Sections are the basic organizational units in a PE file, containing different types of data (code, data, resources, etc.).
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct PeSection {
308    /// Section name, up to 8 characters
309    pub name: String,
310    /// Virtual size of the section in memory
311    pub virtual_size: u32,
312    /// Virtual address of the section in memory
313    pub virtual_address: u32,
314    /// Size of raw data in the file
315    pub size_of_raw_data: u32,
316    /// Pointer to raw data in the file
317    pub pointer_to_raw_data: u32,
318    /// Pointer to the relocation table
319    pub pointer_to_relocations: u32,
320    /// Pointer to the line numbers table
321    pub pointer_to_line_numbers: u32,
322    /// Number of relocation entries
323    pub number_of_relocations: u16,
324    /// Number of line numbers
325    pub number_of_line_numbers: u16,
326    /// Section characteristics flags
327    pub characteristics: u32,
328    /// Raw data of the section
329    #[serde(skip_serializing_if = "Vec::is_empty")]
330    pub data: Vec<u8>,
331}
332
333/// PE file reading configuration
334///
335/// Controls the behavior of the PE file parsing process, allowing selective parsing of different parts.
336/// Adjusting these settings balances performance and functionality.
337#[derive(Debug, Clone, Copy)]
338pub struct ReadConfig {
339    /// Whether to include section data; if false, only headers are parsed
340    pub include_sections: bool,
341    /// Whether to validate the checksum; validation increases parsing time
342    pub validate_checksum: bool,
343    /// Whether to parse the import table, which contains dependency DLL info
344    pub parse_imports: bool,
345    /// Whether to parse the export table, which contains info about provided functions
346    pub parse_exports: bool,
347}
348
349impl Default for ReadConfig {
350    /// Create default reading configuration
351    ///
352    /// Default configuration includes section data, parses import and export tables, but doesn't validate checksum.
353    /// This provides a good balance of performance and functionality for most cases.
354    fn default() -> Self {
355        Self { include_sections: true, validate_checksum: false, parse_imports: true, parse_exports: true }
356    }
357}
358
359/// PE program structure
360///
361/// Represents a complete PE (Portable Executable) program, including all headers and section data.
362/// This is the highest level of abstraction for a PE file, containing its full parsed content.
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct PeProgram {
365    /// PE header information, containing DOS, NT, COFF, and optional headers
366    pub header: PeHeader,
367    /// Collection of all sections, containing code, data, resources, etc.
368    pub sections: Vec<PeSection>,
369    /// Import table, containing external functions and libraries the program depends on
370    pub imports: ImportTable,
371    /// Export table, containing functions and symbols the program provides to others
372    pub exports: ExportTable,
373}
374
375impl PeProgram {
376    /// Create a simple executable PE program from raw machine code
377    pub fn create_executable(machine_code: Vec<u8>) -> Self {
378        let arch = Architecture::X86_64;
379        let machine = 0x8664; // X86_64
380        let section_count = 1;
381
382        // Optional Header
383        let entry_point = 0x1000;
384        let image_base = 0x400000;
385        let size_of_code = machine_code.len() as u32;
386        let size_of_headers = 0x200; // Minimal alignment
387        let size_of_image = 0x2000; // Headers + Section
388
389        let optional_header = OptionalHeader::new_for_architecture(
390            &arch,
391            entry_point,
392            image_base,
393            size_of_code,
394            size_of_headers,
395            size_of_image,
396            SubsystemType::Console,
397        );
398
399        let coff_header = CoffHeader::new(machine, section_count)
400            .with_optional_header_size(if arch == Architecture::X86_64 { 240 } else { 224 })
401            .with_characteristics(0x0022); // EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE
402
403        let header =
404            PeHeader { dos_header: DosHeader::default(), nt_header: NtHeader::default(), coff_header, optional_header };
405
406        let section = PeSection {
407            name: ".text".to_string(),
408            virtual_size: size_of_code,
409            virtual_address: 0x1000,
410            size_of_raw_data: (size_of_code + 0x1FF) & !0x1FF, // Align to 512
411            pointer_to_raw_data: 0x200,
412            pointer_to_relocations: 0,
413            pointer_to_line_numbers: 0,
414            number_of_relocations: 0,
415            number_of_line_numbers: 0,
416            characteristics: 0x60000020, // CODE | EXECUTE | READ
417            data: machine_code,
418        };
419
420        Self { header, sections: vec![section], imports: ImportTable::default(), exports: ExportTable::default() }
421    }
422
423    /// Add import table to program and automatically create .idata section
424    pub fn with_imports(mut self, imports: ImportTable) -> Self {
425        if imports.entries.is_empty() {
426            return self;
427        }
428
429        self.imports = imports;
430
431        let is_64bit = self.header.optional_header.magic == 0x020B;
432        let pointer_size = if is_64bit { 8 } else { 4 };
433
434        // 1. Calculate layout and RVAs
435        let base_rva = self.sections.last().map(|s| s.virtual_address + ((s.virtual_size + 0xFFF) & !0xFFF)).unwrap_or(0x1000);
436        let last_section_offset = self.sections.last().map(|s| s.pointer_to_raw_data + s.size_of_raw_data).unwrap_or(0x200);
437
438        // Import Descriptor Table (IDT) size
439        let idt_size = (self.imports.entries.len() + 1) * 20;
440
441        let mut current_offset = idt_size;
442
443        // Relative offsets for DLL names
444        let mut dll_name_offsets = Vec::new();
445        for entry in &self.imports.entries {
446            dll_name_offsets.push(current_offset);
447            current_offset += entry.dll_name.len() + 1;
448        }
449
450        // Align to 2 bytes
451        if current_offset % 2 != 0 {
452            current_offset += 1;
453        }
454
455        // Relative offsets for Hint/Name
456        let mut function_name_offsets = Vec::new();
457        for entry in &self.imports.entries {
458            let mut entry_offsets = Vec::new();
459            for func in &entry.functions {
460                entry_offsets.push(current_offset);
461                // Hint (2 bytes) + Name + Null terminator (1 byte)
462                current_offset += 2 + func.len() + 1;
463                // Align to 2 bytes
464                if current_offset % 2 != 0 {
465                    current_offset += 1;
466                }
467            }
468            function_name_offsets.push(entry_offsets);
469        }
470
471        // Align to pointer size
472        current_offset = (current_offset + pointer_size - 1) & !(pointer_size - 1);
473
474        // INT relative offsets
475        let mut int_offsets = Vec::new();
476        for entry in &self.imports.entries {
477            int_offsets.push(current_offset);
478            current_offset += (entry.functions.len() + 1) * pointer_size;
479        }
480
481        // IAT relative offsets
482        let mut iat_offsets = Vec::new();
483        for entry in &self.imports.entries {
484            iat_offsets.push(current_offset);
485            current_offset += (entry.functions.len() + 1) * pointer_size;
486        }
487
488        let idata_size = current_offset;
489        let idata_raw_size = (idata_size as u32 + 0x1FF) & !0x1FF;
490
491        // 2. Serialize data
492        let mut data = vec![0u8; idata_raw_size as usize];
493        {
494            use byteorder::{LittleEndian, WriteBytesExt};
495            let mut cursor = std::io::Cursor::new(&mut data);
496
497            // Write IDT
498            for (i, _entry) in self.imports.entries.iter().enumerate() {
499                cursor.write_u32::<LittleEndian>(base_rva + int_offsets[i] as u32).unwrap(); // OriginalFirstThunk
500                cursor.write_u32::<LittleEndian>(0).unwrap(); // TimeDateStamp
501                cursor.write_u32::<LittleEndian>(0).unwrap(); // ForwarderChain
502                cursor.write_u32::<LittleEndian>(base_rva + dll_name_offsets[i] as u32).unwrap(); // Name
503                cursor.write_u32::<LittleEndian>(base_rva + iat_offsets[i] as u32).unwrap();
504                // FirstThunk (IAT)
505            }
506            // IDT terminator guaranteed by vec![0]
507
508            // Write DLL names
509            for (i, entry) in self.imports.entries.iter().enumerate() {
510                cursor.set_position(dll_name_offsets[i] as u64);
511                cursor.write_all(entry.dll_name.as_bytes()).unwrap();
512                cursor.write_u8(0).unwrap();
513            }
514
515            // Write Hint/Name
516            for (i, entry) in self.imports.entries.iter().enumerate() {
517                for (j, func) in entry.functions.iter().enumerate() {
518                    cursor.set_position(function_name_offsets[i][j] as u64);
519                    cursor.write_u16::<LittleEndian>(0).unwrap(); // Hint
520                    cursor.write_all(func.as_bytes()).unwrap();
521                    cursor.write_u8(0).unwrap();
522                }
523            }
524
525            // Write INT and IAT (initially the same, both pointing to Hint/Name)
526            for (i, entry) in self.imports.entries.iter().enumerate() {
527                for (j, _) in entry.functions.iter().enumerate() {
528                    let name_rva = base_rva + function_name_offsets[i][j] as u32;
529
530                    // INT
531                    cursor.set_position((int_offsets[i] + j * pointer_size) as u64);
532                    if is_64bit {
533                        cursor.write_u64::<LittleEndian>(name_rva as u64).unwrap();
534                    }
535                    else {
536                        cursor.write_u32::<LittleEndian>(name_rva).unwrap();
537                    }
538
539                    // IAT
540                    cursor.set_position((iat_offsets[i] + j * pointer_size) as u64);
541                    if is_64bit {
542                        cursor.write_u64::<LittleEndian>(name_rva as u64).unwrap();
543                    }
544                    else {
545                        cursor.write_u32::<LittleEndian>(name_rva).unwrap();
546                    }
547                }
548            }
549        }
550
551        let idata_section = PeSection {
552            name: ".idata".to_string(),
553            virtual_size: idata_size as u32,
554            virtual_address: base_rva,
555            size_of_raw_data: idata_raw_size,
556            pointer_to_raw_data: last_section_offset,
557            pointer_to_relocations: 0,
558            pointer_to_line_numbers: 0,
559            number_of_relocations: 0,
560            number_of_line_numbers: 0,
561            characteristics: 0xC0000040, // INITIALIZED_DATA | READ | WRITE
562            data,
563        };
564
565        self.sections.push(idata_section);
566        self.header.coff_header.number_of_sections = self.sections.len() as u16;
567
568        // 更新数据目录
569        if self.header.optional_header.data_directories.len() >= 2 {
570            self.header.optional_header.data_directories[1].virtual_address = base_rva;
571            self.header.optional_header.data_directories[1].size = idata_size as u32;
572        }
573
574        // IAT 目录 (索引 12)
575        if self.header.optional_header.data_directories.len() >= 13 {
576            // 简单起见,我们将整个 IAT 区域作为 IAT 目录。
577            // 实际上每个 DLL 都有自己的 IAT 范围。
578            let iat_start_offset = iat_offsets[0];
579            let iat_total_size = idata_size - iat_start_offset;
580            self.header.optional_header.data_directories[12].virtual_address = base_rva + iat_start_offset as u32;
581            self.header.optional_header.data_directories[12].size = iat_total_size as u32;
582        }
583
584        // 更新映像大小
585        self.header.optional_header.size_of_image = base_rva + ((idata_raw_size + 0xFFF) & !0xFFF);
586
587        self
588    }
589}
590
591/// PE 信息结构
592///
593/// 提供 PE 文件的摘要信息,包含关键属性和统计信息。
594/// 这个结构用于快速获取文件的基本信息,而无需解析完整的头部结构。
595#[derive(Debug, Clone, Serialize, Deserialize)]
596pub struct PeInfo {
597    /// 目标架构类型,如 x86、x64、ARM 等
598    pub target_arch: Architecture,
599    /// 子系统类型,定义程序运行环境
600    pub subsystem: SubsystemType,
601    /// 程序入口点的 RVA(相对虚拟地址)
602    pub entry_point: u32,
603    /// 映像的首选加载地址
604    pub image_base: u64,
605    /// 文件中节的数量
606    pub section_count: u16,
607    /// 文件的总大小(以字节为单位)
608    pub file_size: u64,
609}
610
611/// 数据目录结构
612///
613/// 包含 PE 文件中各种数据目录的信息,如导入表、导出表、
614/// 资源表等。每个数据目录项包含一个RVA和大小。
615#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
616pub struct DataDirectory {
617    /// 数据目录的相对虚拟地址(RVA)
618    pub virtual_address: u32,
619    /// 数据目录的大小(以字节为单位)
620    pub size: u32,
621}
622
623impl DataDirectory {
624    /// 从 ExeReader 读取数据目录
625    pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
626        Ok(DataDirectory { virtual_address: reader.read_u32::<LittleEndian>()?, size: reader.read_u32::<LittleEndian>()? })
627    }
628}
629
630impl OptionalHeader {
631    /// 从 ExeReader 读取可选头
632    pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
633        let magic = reader.read_u16::<LittleEndian>()?;
634        let is_pe32_plus = magic == 0x020B;
635
636        let major_linker_version = reader.read_u8()?;
637        let minor_linker_version = reader.read_u8()?;
638        let size_of_code = reader.read_u32::<LittleEndian>()?;
639        let size_of_initialized_data = reader.read_u32::<LittleEndian>()?;
640        let size_of_uninitialized_data = reader.read_u32::<LittleEndian>()?;
641        let address_of_entry_point = reader.read_u32::<LittleEndian>()?;
642        let base_of_code = reader.read_u32::<LittleEndian>()?;
643
644        let (base_of_data, image_base) = if is_pe32_plus {
645            (None, reader.read_u64::<LittleEndian>()?)
646        }
647        else {
648            (Some(reader.read_u32::<LittleEndian>()?), reader.read_u32::<LittleEndian>()? as u64)
649        };
650
651        let section_alignment = reader.read_u32::<LittleEndian>()?;
652        let file_alignment = reader.read_u32::<LittleEndian>()?;
653        let major_operating_system_version = reader.read_u16::<LittleEndian>()?;
654        let minor_operating_system_version = reader.read_u16::<LittleEndian>()?;
655        let major_image_version = reader.read_u16::<LittleEndian>()?;
656        let minor_image_version = reader.read_u16::<LittleEndian>()?;
657        let major_subsystem_version = reader.read_u16::<LittleEndian>()?;
658        let minor_subsystem_version = reader.read_u16::<LittleEndian>()?;
659        let win32_version_value = reader.read_u32::<LittleEndian>()?;
660        let size_of_image = reader.read_u32::<LittleEndian>()?;
661        let size_of_headers = reader.read_u32::<LittleEndian>()?;
662        let checksum = reader.read_u32::<LittleEndian>()?;
663        let subsystem = reader.read_u16::<LittleEndian>()?.into();
664        let dll_characteristics = reader.read_u16::<LittleEndian>()?;
665
666        let (size_of_stack_reserve, size_of_stack_commit, size_of_heap_reserve, size_of_heap_commit) = if is_pe32_plus {
667            (
668                reader.read_u64::<LittleEndian>()?,
669                reader.read_u64::<LittleEndian>()?,
670                reader.read_u64::<LittleEndian>()?,
671                reader.read_u64::<LittleEndian>()?,
672            )
673        }
674        else {
675            (
676                reader.read_u32::<LittleEndian>()? as u64,
677                reader.read_u32::<LittleEndian>()? as u64,
678                reader.read_u32::<LittleEndian>()? as u64,
679                reader.read_u32::<LittleEndian>()? as u64,
680            )
681        };
682
683        let loader_flags = reader.read_u32::<LittleEndian>()?;
684        let number_of_rva_and_sizes = reader.read_u32::<LittleEndian>()?;
685
686        let mut data_directories = Vec::new();
687        for _ in 0..number_of_rva_and_sizes {
688            data_directories.push(DataDirectory::read(&mut reader)?);
689        }
690
691        Ok(OptionalHeader {
692            magic,
693            major_linker_version,
694            minor_linker_version,
695            size_of_code,
696            size_of_initialized_data,
697            size_of_uninitialized_data,
698            address_of_entry_point,
699            base_of_code,
700            base_of_data,
701            image_base,
702            section_alignment,
703            file_alignment,
704            major_operating_system_version,
705            minor_operating_system_version,
706            major_image_version,
707            minor_image_version,
708            major_subsystem_version,
709            minor_subsystem_version,
710            win32_version_value,
711            size_of_image,
712            size_of_headers,
713            checksum,
714            subsystem,
715            dll_characteristics,
716            size_of_stack_reserve,
717            size_of_stack_commit,
718            size_of_heap_reserve,
719            size_of_heap_commit,
720            loader_flags,
721            number_of_rva_and_sizes,
722            data_directories,
723        })
724    }
725}