pe-assembler 0.1.1

PE/COFF assembler for Windows instruction sets - strongly typed, object-oriented, zero-dependency core
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
#![doc = include_str!("readme.md")]

pub use self::{
    dos::DosHeader,
    nt::NtHeader,
    tables::{ExportTable, ImportEntry, ImportTable},
};
use gaia_binary::{LittleEndian, ReadBytesExt, WriteBytesExt};
use gaia_types::helpers::Architecture;
use serde::{Deserialize, Serialize};
use std::{
    fmt::{Display, Formatter},
    io::{Read, Write},
};

pub mod coff;
mod dos;
mod nt;
pub mod tables;

pub use coff::*;
use gaia_types::GaiaError;

/// PE subsystem type enum
///
/// Defines the various subsystem types that a Windows PE file can use,
/// which determine the runtime environment and dependencies of the program.
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum SubsystemType {
    /// Console application, runs in a console window
    Console,
    /// Windows GUI application, has a graphical interface
    Windows,
    /// Native driver, runs in kernel mode
    Native,
    /// POSIX subsystem application
    Posix,
    /// Windows CE subsystem
    WindowsCe,
    /// EFI application
    Efi,
    /// EFI boot service driver
    EfiBootServiceDriver,
    /// EFI runtime driver
    EfiRuntimeDriver,
    /// EFI ROM image
    EfiRom,
    /// Xbox application
    Xbox,
    /// Windows boot application
    WindowsBootApplication,
}

impl Display for SubsystemType {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            SubsystemType::Console => write!(f, "Console application"),
            SubsystemType::Windows => write!(f, "Windows GUI application"),
            SubsystemType::Native => write!(f, "Native driver"),
            SubsystemType::Posix => write!(f, "POSIX subsystem application"),
            SubsystemType::WindowsCe => write!(f, "Windows CE subsystem"),
            SubsystemType::Efi => write!(f, "EFI application"),
            SubsystemType::EfiBootServiceDriver => write!(f, "EFI boot service driver"),
            SubsystemType::EfiRuntimeDriver => write!(f, "EFI runtime driver"),
            SubsystemType::EfiRom => write!(f, "EFI ROM image"),
            SubsystemType::Xbox => write!(f, "Xbox application"),
            SubsystemType::WindowsBootApplication => write!(f, "Windows boot application"),
        }
    }
}

impl From<u16> for SubsystemType {
    /// Create subsystem type from u16 value
    ///
    /// # Arguments
    /// * `value` - Numeric value of the subsystem type
    ///
    /// # Returns
    /// Returns the corresponding subsystem type, or Console for unknown types
    fn from(value: u16) -> Self {
        match value {
            1 => SubsystemType::Native,
            2 => SubsystemType::Windows,
            3 => SubsystemType::Console,
            7 => SubsystemType::Posix,
            9 => SubsystemType::WindowsCe,
            10 => SubsystemType::Efi,
            11 => SubsystemType::EfiBootServiceDriver,
            12 => SubsystemType::EfiRuntimeDriver,
            13 => SubsystemType::EfiRom,
            14 => SubsystemType::Xbox,
            16 => SubsystemType::WindowsBootApplication,
            _ => SubsystemType::Console, // Default value
        }
    }
}

impl Default for DataDirectory {
    fn default() -> Self {
        Self { virtual_address: 0, size: 0 }
    }
}

/// Optional header structure
///
/// Contains loading and runtime information for the PE file, such as entry point address,
/// memory layout, version info, etc. This structure is crucial for the Windows loader
/// to correctly load and execute the program.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OptionalHeader {
    /// Magic number identifying PE32 or PE32+ format
    pub magic: u16,
    /// Major version of the linker
    pub major_linker_version: u8,
    /// Minor version of the linker
    pub minor_linker_version: u8,
    /// Total size of the code section in bytes
    pub size_of_code: u32,
    /// Total size of initialized data
    pub size_of_initialized_data: u32,
    /// Total size of uninitialized data
    pub size_of_uninitialized_data: u32,
    /// RVA (Relative Virtual Address) of the program entry point
    pub address_of_entry_point: u32,
    /// Starting RVA of the code section
    pub base_of_code: u32,
    /// Starting RVA of the data section, only valid for PE32
    pub base_of_data: Option<u32>, // Only for PE32
    /// Preferred loading address of the image
    pub image_base: u64,
    /// Alignment granularity of sections in memory
    pub section_alignment: u32,
    /// Alignment granularity of sections in the file
    pub file_alignment: u32,
    /// Major version of the required operating system
    pub major_operating_system_version: u16,
    /// Minor version of the required operating system
    pub minor_operating_system_version: u16,
    /// Major version of the image
    pub major_image_version: u16,
    /// Minor version of the image
    pub minor_image_version: u16,
    /// Major version of the subsystem
    pub major_subsystem_version: u16,
    /// Minor version of the subsystem
    pub minor_subsystem_version: u16,
    /// Reserved field, must be 0
    pub win32_version_value: u32,
    /// Total size of the image, including all headers and sections
    pub size_of_image: u32,
    /// Total size of all headers
    pub size_of_headers: u32,
    /// Checksum of the image, used for kernel mode and system DLLs
    pub checksum: u32,
    /// Subsystem type, defining the runtime environment
    pub subsystem: SubsystemType,
    /// DLL characteristics flags, describing various properties of the DLL
    pub dll_characteristics: u16,
    /// Virtual memory size reserved for the thread stack
    pub size_of_stack_reserve: u64,
    /// Virtual memory size committed for the thread stack
    pub size_of_stack_commit: u64,
    /// Virtual memory size reserved for the process heap
    pub size_of_heap_reserve: u64,
    /// Virtual memory size committed for the process heap
    pub size_of_heap_commit: u64,
    /// Reserved field, must be 0
    pub loader_flags: u32,
    /// Number of entries in the data directory table
    pub number_of_rva_and_sizes: u32,
    /// Data directory table, containing info about various data directories
    pub data_directories: Vec<DataDirectory>,
}

impl OptionalHeader {
    /// Create a standard optional header suitable for .NET programs
    pub fn new(
        entry_point: u32,
        image_base: u64,
        size_of_code: u32,
        size_of_headers: u32,
        size_of_image: u32,
        subsystem: SubsystemType,
    ) -> Self {
        let mut data_directories = Vec::with_capacity(16);
        // Initialize 16 standard data directories
        for _ in 0..16 {
            data_directories.push(DataDirectory::default());
        }

        Self {
            magic: 0x020B, // PE32+
            major_linker_version: 14,
            minor_linker_version: 0,
            size_of_code,
            size_of_initialized_data: 0,
            size_of_uninitialized_data: 0,
            address_of_entry_point: entry_point,
            base_of_code: 0x2000,
            base_of_data: None, // Not used in PE32+
            image_base,
            section_alignment: 0x2000,
            file_alignment: 0x200,
            major_operating_system_version: 6,
            minor_operating_system_version: 0,
            major_image_version: 0,
            minor_image_version: 0,
            major_subsystem_version: 6,
            minor_subsystem_version: 0,
            win32_version_value: 0,
            size_of_image,
            size_of_headers,
            checksum: 0,
            subsystem,
            // Enable DYNAMIC_BASE and other security features
            // NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE | DYNAMIC_BASE
            dll_characteristics: 0x8540, // NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE | DYNAMIC_BASE
            size_of_stack_reserve: 0x100000,
            size_of_stack_commit: 0x1000,
            size_of_heap_reserve: 0x100000,
            size_of_heap_commit: 0x1000,
            loader_flags: 0,
            number_of_rva_and_sizes: 16,
            data_directories,
        }
    }

    /// Create optional header based on architecture
    pub fn new_for_architecture(
        architecture: &Architecture,
        entry_point: u32,
        image_base: u64,
        size_of_code: u32,
        size_of_headers: u32,
        size_of_image: u32,
        subsystem: SubsystemType,
    ) -> Self {
        let mut data_directories = Vec::with_capacity(16);
        // Initialize 16 standard data directories
        for _ in 0..16 {
            data_directories.push(DataDirectory::default());
        }

        let (magic, base_of_data) = match architecture {
            Architecture::X86 => (0x010B, Some(0x2000)), // PE32
            Architecture::X86_64 => (0x020B, None),      // PE32+
            _ => (0x010B, Some(0x2000)),                 // Default to PE32
        };

        Self {
            magic,
            major_linker_version: 14,
            minor_linker_version: 0,
            size_of_code,
            size_of_initialized_data: 0,
            size_of_uninitialized_data: 0,
            address_of_entry_point: entry_point,
            base_of_code: 0x1000,
            base_of_data,
            image_base,
            section_alignment: 0x1000,
            file_alignment: 0x200,
            major_operating_system_version: 6,
            minor_operating_system_version: 0,
            major_image_version: 0,
            minor_image_version: 0,
            major_subsystem_version: 6,
            minor_subsystem_version: 0,
            win32_version_value: 0,
            size_of_image,
            size_of_headers,
            checksum: 0,
            subsystem,
            dll_characteristics: 0x8160, // DYNAMIC_BASE | NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE
            size_of_stack_reserve: 0x100000,
            size_of_stack_commit: 0x1000,
            size_of_heap_reserve: 0x100000,
            size_of_heap_commit: 0x1000,
            loader_flags: 0,
            number_of_rva_and_sizes: 16,
            data_directories,
        }
    }
}

/// PE header structure
///
/// Combines all header information for a PE file, including DOS, NT, COFF, and optional headers.
/// This structure provides complete metadata for the PE file.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PeHeader {
    /// DOS header, contains DOS compatibility info
    pub dos_header: DosHeader,
    /// NT header, contains PE signature
    pub nt_header: NtHeader,
    /// COFF header, contains object file info
    pub coff_header: CoffHeader,
    /// Optional header, contains loading and runtime info
    pub optional_header: OptionalHeader,
}

/// PE section structure
///
/// Contains detailed information for each section in the PE file, including name, virtual address, size, etc.
/// Sections are the basic organizational units in a PE file, containing different types of data (code, data, resources, etc.).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeSection {
    /// Section name, up to 8 characters
    pub name: String,
    /// Virtual size of the section in memory
    pub virtual_size: u32,
    /// Virtual address of the section in memory
    pub virtual_address: u32,
    /// Size of raw data in the file
    pub size_of_raw_data: u32,
    /// Pointer to raw data in the file
    pub pointer_to_raw_data: u32,
    /// Pointer to the relocation table
    pub pointer_to_relocations: u32,
    /// Pointer to the line numbers table
    pub pointer_to_line_numbers: u32,
    /// Number of relocation entries
    pub number_of_relocations: u16,
    /// Number of line numbers
    pub number_of_line_numbers: u16,
    /// Section characteristics flags
    pub characteristics: u32,
    /// Raw data of the section
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub data: Vec<u8>,
}

/// PE file reading configuration
///
/// Controls the behavior of the PE file parsing process, allowing selective parsing of different parts.
/// Adjusting these settings balances performance and functionality.
#[derive(Debug, Clone, Copy)]
pub struct ReadConfig {
    /// Whether to include section data; if false, only headers are parsed
    pub include_sections: bool,
    /// Whether to validate the checksum; validation increases parsing time
    pub validate_checksum: bool,
    /// Whether to parse the import table, which contains dependency DLL info
    pub parse_imports: bool,
    /// Whether to parse the export table, which contains info about provided functions
    pub parse_exports: bool,
}

impl Default for ReadConfig {
    /// Create default reading configuration
    ///
    /// Default configuration includes section data, parses import and export tables, but doesn't validate checksum.
    /// This provides a good balance of performance and functionality for most cases.
    fn default() -> Self {
        Self { include_sections: true, validate_checksum: false, parse_imports: true, parse_exports: true }
    }
}

/// PE program structure
///
/// Represents a complete PE (Portable Executable) program, including all headers and section data.
/// This is the highest level of abstraction for a PE file, containing its full parsed content.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeProgram {
    /// PE header information, containing DOS, NT, COFF, and optional headers
    pub header: PeHeader,
    /// Collection of all sections, containing code, data, resources, etc.
    pub sections: Vec<PeSection>,
    /// Import table, containing external functions and libraries the program depends on
    pub imports: ImportTable,
    /// Export table, containing functions and symbols the program provides to others
    pub exports: ExportTable,
}

impl PeProgram {
    /// Create a simple executable PE program from raw machine code
    pub fn create_executable(machine_code: Vec<u8>) -> Self {
        let arch = Architecture::X86_64;
        let machine = 0x8664; // X86_64
        let section_count = 1;

        // Optional Header
        let entry_point = 0x1000;
        let image_base = 0x400000;
        let size_of_code = machine_code.len() as u32;
        let size_of_headers = 0x200; // Minimal alignment
        let size_of_image = 0x2000; // Headers + Section

        let optional_header = OptionalHeader::new_for_architecture(
            &arch,
            entry_point,
            image_base,
            size_of_code,
            size_of_headers,
            size_of_image,
            SubsystemType::Console,
        );

        let coff_header = CoffHeader::new(machine, section_count)
            .with_optional_header_size(if arch == Architecture::X86_64 { 240 } else { 224 })
            .with_characteristics(0x0022); // EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE

        let header =
            PeHeader { dos_header: DosHeader::default(), nt_header: NtHeader::default(), coff_header, optional_header };

        let section = PeSection {
            name: ".text".to_string(),
            virtual_size: size_of_code,
            virtual_address: 0x1000,
            size_of_raw_data: (size_of_code + 0x1FF) & !0x1FF, // Align to 512
            pointer_to_raw_data: 0x200,
            pointer_to_relocations: 0,
            pointer_to_line_numbers: 0,
            number_of_relocations: 0,
            number_of_line_numbers: 0,
            characteristics: 0x60000020, // CODE | EXECUTE | READ
            data: machine_code,
        };

        Self { header, sections: vec![section], imports: ImportTable::default(), exports: ExportTable::default() }
    }

    /// Add import table to program and automatically create .idata section
    pub fn with_imports(mut self, imports: ImportTable) -> Self {
        if imports.entries.is_empty() {
            return self;
        }

        self.imports = imports;

        let is_64bit = self.header.optional_header.magic == 0x020B;
        let pointer_size = if is_64bit { 8 } else { 4 };

        // 1. Calculate layout and RVAs
        let base_rva = self.sections.last().map(|s| s.virtual_address + ((s.virtual_size + 0xFFF) & !0xFFF)).unwrap_or(0x1000);
        let last_section_offset = self.sections.last().map(|s| s.pointer_to_raw_data + s.size_of_raw_data).unwrap_or(0x200);

        // Import Descriptor Table (IDT) size
        let idt_size = (self.imports.entries.len() + 1) * 20;

        let mut current_offset = idt_size;

        // Relative offsets for DLL names
        let mut dll_name_offsets = Vec::new();
        for entry in &self.imports.entries {
            dll_name_offsets.push(current_offset);
            current_offset += entry.dll_name.len() + 1;
        }

        // Align to 2 bytes
        if current_offset % 2 != 0 {
            current_offset += 1;
        }

        // Relative offsets for Hint/Name
        let mut function_name_offsets = Vec::new();
        for entry in &self.imports.entries {
            let mut entry_offsets = Vec::new();
            for func in &entry.functions {
                entry_offsets.push(current_offset);
                // Hint (2 bytes) + Name + Null terminator (1 byte)
                current_offset += 2 + func.len() + 1;
                // Align to 2 bytes
                if current_offset % 2 != 0 {
                    current_offset += 1;
                }
            }
            function_name_offsets.push(entry_offsets);
        }

        // Align to pointer size
        current_offset = (current_offset + pointer_size - 1) & !(pointer_size - 1);

        // INT relative offsets
        let mut int_offsets = Vec::new();
        for entry in &self.imports.entries {
            int_offsets.push(current_offset);
            current_offset += (entry.functions.len() + 1) * pointer_size;
        }

        // IAT relative offsets
        let mut iat_offsets = Vec::new();
        for entry in &self.imports.entries {
            iat_offsets.push(current_offset);
            current_offset += (entry.functions.len() + 1) * pointer_size;
        }

        let idata_size = current_offset;
        let idata_raw_size = (idata_size as u32 + 0x1FF) & !0x1FF;

        // 2. Serialize data
        let mut data = vec![0u8; idata_raw_size as usize];
        {
            use gaia_binary::{LittleEndian, WriteBytesExt};
            let mut cursor = std::io::Cursor::new(&mut data);

            // Write IDT
            for (i, _entry) in self.imports.entries.iter().enumerate() {
                cursor.write_u32::<LittleEndian>(base_rva + int_offsets[i] as u32).unwrap(); // OriginalFirstThunk
                cursor.write_u32::<LittleEndian>(0).unwrap(); // TimeDateStamp
                cursor.write_u32::<LittleEndian>(0).unwrap(); // ForwarderChain
                cursor.write_u32::<LittleEndian>(base_rva + dll_name_offsets[i] as u32).unwrap(); // Name
                cursor.write_u32::<LittleEndian>(base_rva + iat_offsets[i] as u32).unwrap();
                // FirstThunk (IAT)
            }
            // IDT terminator guaranteed by vec![0]

            // Write DLL names
            for (i, entry) in self.imports.entries.iter().enumerate() {
                cursor.set_position(dll_name_offsets[i] as u64);
                cursor.write_all(entry.dll_name.as_bytes()).unwrap();
                cursor.write_u8(0).unwrap();
            }

            // Write Hint/Name
            for (i, entry) in self.imports.entries.iter().enumerate() {
                for (j, func) in entry.functions.iter().enumerate() {
                    cursor.set_position(function_name_offsets[i][j] as u64);
                    cursor.write_u16::<LittleEndian>(0).unwrap(); // Hint
                    cursor.write_all(func.as_bytes()).unwrap();
                    cursor.write_u8(0).unwrap();
                }
            }

            // Write INT and IAT (initially the same, both pointing to Hint/Name)
            for (i, entry) in self.imports.entries.iter().enumerate() {
                for (j, _) in entry.functions.iter().enumerate() {
                    let name_rva = base_rva + function_name_offsets[i][j] as u32;

                    // INT
                    cursor.set_position((int_offsets[i] + j * pointer_size) as u64);
                    if is_64bit {
                        cursor.write_u64::<LittleEndian>(name_rva as u64).unwrap();
                    }
                    else {
                        cursor.write_u32::<LittleEndian>(name_rva).unwrap();
                    }

                    // IAT
                    cursor.set_position((iat_offsets[i] + j * pointer_size) as u64);
                    if is_64bit {
                        cursor.write_u64::<LittleEndian>(name_rva as u64).unwrap();
                    }
                    else {
                        cursor.write_u32::<LittleEndian>(name_rva).unwrap();
                    }
                }
            }
        }

        let idata_section = PeSection {
            name: ".idata".to_string(),
            virtual_size: idata_size as u32,
            virtual_address: base_rva,
            size_of_raw_data: idata_raw_size,
            pointer_to_raw_data: last_section_offset,
            pointer_to_relocations: 0,
            pointer_to_line_numbers: 0,
            number_of_relocations: 0,
            number_of_line_numbers: 0,
            characteristics: 0xC0000040, // INITIALIZED_DATA | READ | WRITE
            data,
        };

        self.sections.push(idata_section);
        self.header.coff_header.number_of_sections = self.sections.len() as u16;

        // 更新数据目录
        if self.header.optional_header.data_directories.len() >= 2 {
            self.header.optional_header.data_directories[1].virtual_address = base_rva;
            self.header.optional_header.data_directories[1].size = idata_size as u32;
        }

        // IAT 目录 (索引 12)
        if self.header.optional_header.data_directories.len() >= 13 {
            // 简单起见,我们将整个 IAT 区域作为 IAT 目录。
            // 实际上每个 DLL 都有自己的 IAT 范围。
            let iat_start_offset = iat_offsets[0];
            let iat_total_size = idata_size - iat_start_offset;
            self.header.optional_header.data_directories[12].virtual_address = base_rva + iat_start_offset as u32;
            self.header.optional_header.data_directories[12].size = iat_total_size as u32;
        }

        // 更新映像大小
        self.header.optional_header.size_of_image = base_rva + ((idata_raw_size + 0xFFF) & !0xFFF);

        self
    }
}

/// PE 信息结构
///
/// 提供 PE 文件的摘要信息,包含关键属性和统计信息。
/// 这个结构用于快速获取文件的基本信息,而无需解析完整的头部结构。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeInfo {
    /// 目标架构类型,如 x86、x64、ARM 等
    pub target_arch: Architecture,
    /// 子系统类型,定义程序运行环境
    pub subsystem: SubsystemType,
    /// 程序入口点的 RVA(相对虚拟地址)
    pub entry_point: u32,
    /// 映像的首选加载地址
    pub image_base: u64,
    /// 文件中节的数量
    pub section_count: u16,
    /// 文件的总大小(以字节为单位)
    pub file_size: u64,
}

/// 数据目录结构
///
/// 包含 PE 文件中各种数据目录的信息,如导入表、导出表、
/// 资源表等。每个数据目录项包含一个RVA和大小。
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct DataDirectory {
    /// 数据目录的相对虚拟地址(RVA)
    pub virtual_address: u32,
    /// 数据目录的大小(以字节为单位)
    pub size: u32,
}

impl DataDirectory {
    /// 从 ExeReader 读取数据目录
    pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
        Ok(DataDirectory { virtual_address: reader.read_u32::<LittleEndian>()?, size: reader.read_u32::<LittleEndian>()? })
    }
}

impl OptionalHeader {
    /// 从 ExeReader 读取可选头
    pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
        let magic = reader.read_u16::<LittleEndian>()?;
        let is_pe32_plus = magic == 0x020B;

        let major_linker_version = reader.read_u8()?;
        let minor_linker_version = reader.read_u8()?;
        let size_of_code = reader.read_u32::<LittleEndian>()?;
        let size_of_initialized_data = reader.read_u32::<LittleEndian>()?;
        let size_of_uninitialized_data = reader.read_u32::<LittleEndian>()?;
        let address_of_entry_point = reader.read_u32::<LittleEndian>()?;
        let base_of_code = reader.read_u32::<LittleEndian>()?;

        let (base_of_data, image_base) = if is_pe32_plus {
            (None, reader.read_u64::<LittleEndian>()?)
        }
        else {
            (Some(reader.read_u32::<LittleEndian>()?), reader.read_u32::<LittleEndian>()? as u64)
        };

        let section_alignment = reader.read_u32::<LittleEndian>()?;
        let file_alignment = reader.read_u32::<LittleEndian>()?;
        let major_operating_system_version = reader.read_u16::<LittleEndian>()?;
        let minor_operating_system_version = reader.read_u16::<LittleEndian>()?;
        let major_image_version = reader.read_u16::<LittleEndian>()?;
        let minor_image_version = reader.read_u16::<LittleEndian>()?;
        let major_subsystem_version = reader.read_u16::<LittleEndian>()?;
        let minor_subsystem_version = reader.read_u16::<LittleEndian>()?;
        let win32_version_value = reader.read_u32::<LittleEndian>()?;
        let size_of_image = reader.read_u32::<LittleEndian>()?;
        let size_of_headers = reader.read_u32::<LittleEndian>()?;
        let checksum = reader.read_u32::<LittleEndian>()?;
        let subsystem = reader.read_u16::<LittleEndian>()?.into();
        let dll_characteristics = reader.read_u16::<LittleEndian>()?;

        let (size_of_stack_reserve, size_of_stack_commit, size_of_heap_reserve, size_of_heap_commit) = if is_pe32_plus {
            (
                reader.read_u64::<LittleEndian>()?,
                reader.read_u64::<LittleEndian>()?,
                reader.read_u64::<LittleEndian>()?,
                reader.read_u64::<LittleEndian>()?,
            )
        }
        else {
            (
                reader.read_u32::<LittleEndian>()? as u64,
                reader.read_u32::<LittleEndian>()? as u64,
                reader.read_u32::<LittleEndian>()? as u64,
                reader.read_u32::<LittleEndian>()? as u64,
            )
        };

        let loader_flags = reader.read_u32::<LittleEndian>()?;
        let number_of_rva_and_sizes = reader.read_u32::<LittleEndian>()?;

        let mut data_directories = Vec::new();
        for _ in 0..number_of_rva_and_sizes {
            data_directories.push(DataDirectory::read(&mut reader)?);
        }

        Ok(OptionalHeader {
            magic,
            major_linker_version,
            minor_linker_version,
            size_of_code,
            size_of_initialized_data,
            size_of_uninitialized_data,
            address_of_entry_point,
            base_of_code,
            base_of_data,
            image_base,
            section_alignment,
            file_alignment,
            major_operating_system_version,
            minor_operating_system_version,
            major_image_version,
            minor_image_version,
            major_subsystem_version,
            minor_subsystem_version,
            win32_version_value,
            size_of_image,
            size_of_headers,
            checksum,
            subsystem,
            dll_characteristics,
            size_of_stack_reserve,
            size_of_stack_commit,
            size_of_heap_reserve,
            size_of_heap_commit,
            loader_flags,
            number_of_rva_and_sizes,
            data_directories,
        })
    }
}