1#![doc = include_str!("readme.md")]
2
3pub use self::{
4 dos::DosHeader,
5 nt::NtHeader,
6 tables::{ExportTable, ImportEntry, ImportTable},
7};
8use gaia_binary::{LittleEndian, ReadBytesExt, WriteBytesExt};
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#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
29pub enum SubsystemType {
30 Console,
32 Windows,
34 Native,
36 Posix,
38 WindowsCe,
40 Efi,
42 EfiBootServiceDriver,
44 EfiRuntimeDriver,
46 EfiRom,
48 Xbox,
50 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 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, }
95 }
96}
97
98impl Default for DataDirectory {
99 fn default() -> Self {
100 Self { virtual_address: 0, size: 0 }
101 }
102}
103
104#[derive(Clone, Debug, Serialize, Deserialize)]
110pub struct OptionalHeader {
111 pub magic: u16,
113 pub major_linker_version: u8,
115 pub minor_linker_version: u8,
117 pub size_of_code: u32,
119 pub size_of_initialized_data: u32,
121 pub size_of_uninitialized_data: u32,
123 pub address_of_entry_point: u32,
125 pub base_of_code: u32,
127 pub base_of_data: Option<u32>, pub image_base: u64,
131 pub section_alignment: u32,
133 pub file_alignment: u32,
135 pub major_operating_system_version: u16,
137 pub minor_operating_system_version: u16,
139 pub major_image_version: u16,
141 pub minor_image_version: u16,
143 pub major_subsystem_version: u16,
145 pub minor_subsystem_version: u16,
147 pub win32_version_value: u32,
149 pub size_of_image: u32,
151 pub size_of_headers: u32,
153 pub checksum: u32,
155 pub subsystem: SubsystemType,
157 pub dll_characteristics: u16,
159 pub size_of_stack_reserve: u64,
161 pub size_of_stack_commit: u64,
163 pub size_of_heap_reserve: u64,
165 pub size_of_heap_commit: u64,
167 pub loader_flags: u32,
169 pub number_of_rva_and_sizes: u32,
171 pub data_directories: Vec<DataDirectory>,
173}
174
175impl OptionalHeader {
176 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 for _ in 0..16 {
188 data_directories.push(DataDirectory::default());
189 }
190
191 Self {
192 magic: 0x020B, 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, 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 dll_characteristics: 0x8540, 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 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 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)), Architecture::X86_64 => (0x020B, None), _ => (0x010B, Some(0x2000)), };
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, 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#[derive(Clone, Debug, Serialize, Deserialize)]
291pub struct PeHeader {
292 pub dos_header: DosHeader,
294 pub nt_header: NtHeader,
296 pub coff_header: CoffHeader,
298 pub optional_header: OptionalHeader,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct PeSection {
308 pub name: String,
310 pub virtual_size: u32,
312 pub virtual_address: u32,
314 pub size_of_raw_data: u32,
316 pub pointer_to_raw_data: u32,
318 pub pointer_to_relocations: u32,
320 pub pointer_to_line_numbers: u32,
322 pub number_of_relocations: u16,
324 pub number_of_line_numbers: u16,
326 pub characteristics: u32,
328 #[serde(skip_serializing_if = "Vec::is_empty")]
330 pub data: Vec<u8>,
331}
332
333#[derive(Debug, Clone, Copy)]
338pub struct ReadConfig {
339 pub include_sections: bool,
341 pub validate_checksum: bool,
343 pub parse_imports: bool,
345 pub parse_exports: bool,
347}
348
349impl Default for ReadConfig {
350 fn default() -> Self {
355 Self { include_sections: true, validate_checksum: false, parse_imports: true, parse_exports: true }
356 }
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct PeProgram {
365 pub header: PeHeader,
367 pub sections: Vec<PeSection>,
369 pub imports: ImportTable,
371 pub exports: ExportTable,
373}
374
375impl PeProgram {
376 pub fn create_executable(machine_code: Vec<u8>) -> Self {
378 let arch = Architecture::X86_64;
379 let machine = 0x8664; let section_count = 1;
381
382 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; let size_of_image = 0x2000; 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); 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, 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, data: machine_code,
418 };
419
420 Self { header, sections: vec![section], imports: ImportTable::default(), exports: ExportTable::default() }
421 }
422
423 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 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 let idt_size = (self.imports.entries.len() + 1) * 20;
440
441 let mut current_offset = idt_size;
442
443 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 if current_offset % 2 != 0 {
452 current_offset += 1;
453 }
454
455 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 current_offset += 2 + func.len() + 1;
463 if current_offset % 2 != 0 {
465 current_offset += 1;
466 }
467 }
468 function_name_offsets.push(entry_offsets);
469 }
470
471 current_offset = (current_offset + pointer_size - 1) & !(pointer_size - 1);
473
474 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 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 let mut data = vec![0u8; idata_raw_size as usize];
493 {
494 use gaia_binary::{LittleEndian, WriteBytesExt};
495 let mut cursor = std::io::Cursor::new(&mut data);
496
497 for (i, _entry) in self.imports.entries.iter().enumerate() {
499 cursor.write_u32::<LittleEndian>(base_rva + int_offsets[i] as u32).unwrap(); cursor.write_u32::<LittleEndian>(0).unwrap(); cursor.write_u32::<LittleEndian>(0).unwrap(); cursor.write_u32::<LittleEndian>(base_rva + dll_name_offsets[i] as u32).unwrap(); cursor.write_u32::<LittleEndian>(base_rva + iat_offsets[i] as u32).unwrap();
504 }
506 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 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(); cursor.write_all(func.as_bytes()).unwrap();
521 cursor.write_u8(0).unwrap();
522 }
523 }
524
525 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 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 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, data,
563 };
564
565 self.sections.push(idata_section);
566 self.header.coff_header.number_of_sections = self.sections.len() as u16;
567
568 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 if self.header.optional_header.data_directories.len() >= 13 {
576 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 self.header.optional_header.size_of_image = base_rva + ((idata_raw_size + 0xFFF) & !0xFFF);
586
587 self
588 }
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
596pub struct PeInfo {
597 pub target_arch: Architecture,
599 pub subsystem: SubsystemType,
601 pub entry_point: u32,
603 pub image_base: u64,
605 pub section_count: u16,
607 pub file_size: u64,
609}
610
611#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
616pub struct DataDirectory {
617 pub virtual_address: u32,
619 pub size: u32,
621}
622
623impl DataDirectory {
624 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 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}