//! Helper for writing PE files. use alloc::string::String; use alloc::vec::Vec; use core::mem; use crate::endian::{LittleEndian as LE, *}; use crate::pe; use crate::write::util; use crate::write::{Error, Result, WritableBuffer}; /// A helper for writing PE files. /// /// Writing uses a two phase approach. The first phase reserves file ranges and virtual /// address ranges for everything in the order that they will be written. /// /// The second phase writes everything out in order. Thus the caller must ensure writing /// is in the same order that file ranges were reserved. #[allow(missing_debug_implementations)] pub struct Writer<'a> { is_64: bool, section_alignment: u32, file_alignment: u32, buffer: &'a mut dyn WritableBuffer, len: u32, virtual_len: u32, headers_len: u32, code_address: u32, data_address: u32, code_len: u32, data_len: u32, bss_len: u32, nt_headers_offset: u32, data_directories: Vec, section_header_num: u16, sections: Vec
, symbol_offset: u32, symbol_num: u32, reloc_blocks: Vec, relocs: Vec>, reloc_offset: u32, } impl<'a> Writer<'a> { /// Create a new `Writer`. pub fn new( is_64: bool, section_alignment: u32, file_alignment: u32, buffer: &'a mut dyn WritableBuffer, ) -> Self { Writer { is_64, section_alignment, file_alignment, buffer, len: 0, virtual_len: 0, headers_len: 0, code_address: 0, data_address: 0, code_len: 0, data_len: 0, bss_len: 0, nt_headers_offset: 0, data_directories: Vec::new(), section_header_num: 0, sections: Vec::new(), symbol_offset: 0, symbol_num: 0, reloc_blocks: Vec::new(), relocs: Vec::new(), reloc_offset: 0, } } /// Return the current virtual address size that has been reserved. /// /// This is only valid after section headers have been reserved. pub fn virtual_len(&self) -> u32 { self.virtual_len } /// Reserve a virtual address range with the given size. /// /// The reserved length will be increased to match the section alignment. /// /// Returns the aligned offset of the start of the range. pub fn reserve_virtual(&mut self, len: u32) -> u32 { let offset = self.virtual_len; self.virtual_len += len; self.virtual_len = util::align_u32(self.virtual_len, self.section_alignment); offset } /// Reserve up to the given virtual address. /// /// The reserved length will be increased to match the section alignment. pub fn reserve_virtual_until(&mut self, address: u32) { debug_assert!(self.virtual_len <= address); self.virtual_len = util::align_u32(address, self.section_alignment); } /// Return the current file length that has been reserved. pub fn reserved_len(&self) -> u32 { self.len } /// Return the current file length that has been written. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.buffer.len() } /// Reserve a file range with the given size and starting alignment. /// /// Returns the aligned offset of the start of the range. pub fn reserve(&mut self, len: u32, align_start: u32) -> u32 { if len == 0 { return self.len; } self.reserve_align(align_start); let offset = self.len; self.len += len; offset } /// Reserve a file range with the given size and using the file alignment. /// /// Returns the aligned offset of the start of the range. pub fn reserve_file(&mut self, len: u32) -> u32 { self.reserve(len, self.file_alignment) } /// Write data. pub fn write(&mut self, data: &[u8]) { self.buffer.write_bytes(data); } /// Reserve alignment padding bytes. pub fn reserve_align(&mut self, align_start: u32) { self.len = util::align_u32(self.len, align_start); } /// Write alignment padding bytes. pub fn write_align(&mut self, align_start: u32) { util::write_align(self.buffer, align_start as usize); } /// Write padding up to the next multiple of file alignment. pub fn write_file_align(&mut self) { self.write_align(self.file_alignment); } /// Reserve the file range up to the given file offset. pub fn reserve_until(&mut self, offset: u32) { debug_assert!(self.len <= offset); self.len = offset; } /// Write padding up to the given file offset. pub fn pad_until(&mut self, offset: u32) { debug_assert!(self.buffer.len() <= offset as usize); self.buffer.resize(offset as usize); } /// Reserve the range for the DOS header. /// /// This must be at the start of the file. /// /// When writing, you may use `write_custom_dos_header` or `write_empty_dos_header`. pub fn reserve_dos_header(&mut self) { debug_assert_eq!(self.len, 0); self.reserve(mem::size_of::() as u32, 1); } /// Write a custom DOS header. /// /// This must be at the start of the file. pub fn write_custom_dos_header(&mut self, dos_header: &pe::ImageDosHeader) -> Result<()> { debug_assert_eq!(self.buffer.len(), 0); // Start writing. self.buffer .reserve(self.len as usize) .map_err(|_| Error(String::from("Cannot allocate buffer")))?; self.buffer.write(dos_header); Ok(()) } /// Write the DOS header for a file without a stub. /// /// This must be at the start of the file. /// /// Uses default values for all fields. pub fn write_empty_dos_header(&mut self) -> Result<()> { self.write_custom_dos_header(&pe::ImageDosHeader { e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE), e_cblp: U16::new(LE, 0), e_cp: U16::new(LE, 0), e_crlc: U16::new(LE, 0), e_cparhdr: U16::new(LE, 0), e_minalloc: U16::new(LE, 0), e_maxalloc: U16::new(LE, 0), e_ss: U16::new(LE, 0), e_sp: U16::new(LE, 0), e_csum: U16::new(LE, 0), e_ip: U16::new(LE, 0), e_cs: U16::new(LE, 0), e_lfarlc: U16::new(LE, 0), e_ovno: U16::new(LE, 0), e_res: [U16::new(LE, 0); 4], e_oemid: U16::new(LE, 0), e_oeminfo: U16::new(LE, 0), e_res2: [U16::new(LE, 0); 10], e_lfanew: U32::new(LE, self.nt_headers_offset), }) } /// Reserve a fixed DOS header and stub. /// /// Use `reserve_dos_header` and `reserve` if you need a custom stub. pub fn reserve_dos_header_and_stub(&mut self) { self.reserve_dos_header(); self.reserve(64, 1); } /// Write a fixed DOS header and stub. /// /// Use `write_custom_dos_header` and `write` if you need a custom stub. pub fn write_dos_header_and_stub(&mut self) -> Result<()> { self.write_custom_dos_header(&pe::ImageDosHeader { e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE), e_cblp: U16::new(LE, 0x90), e_cp: U16::new(LE, 3), e_crlc: U16::new(LE, 0), e_cparhdr: U16::new(LE, 4), e_minalloc: U16::new(LE, 0), e_maxalloc: U16::new(LE, 0xffff), e_ss: U16::new(LE, 0), e_sp: U16::new(LE, 0xb8), e_csum: U16::new(LE, 0), e_ip: U16::new(LE, 0), e_cs: U16::new(LE, 0), e_lfarlc: U16::new(LE, 0x40), e_ovno: U16::new(LE, 0), e_res: [U16::new(LE, 0); 4], e_oemid: U16::new(LE, 0), e_oeminfo: U16::new(LE, 0), e_res2: [U16::new(LE, 0); 10], e_lfanew: U32::new(LE, self.nt_headers_offset), })?; #[rustfmt::skip] self.buffer.write_bytes(&[ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); Ok(()) } fn nt_headers_size(&self) -> u32 { if self.is_64 { mem::size_of::() as u32 } else { mem::size_of::() as u32 } } fn optional_header_size(&self) -> u32 { let size = if self.is_64 { mem::size_of::() as u32 } else { mem::size_of::() as u32 }; size + self.data_directories.len() as u32 * mem::size_of::() as u32 } /// Return the offset of the NT headers, if reserved. pub fn nt_headers_offset(&self) -> u32 { self.nt_headers_offset } /// Reserve the range for the NT headers. pub fn reserve_nt_headers(&mut self, data_directory_num: usize) { debug_assert_eq!(self.nt_headers_offset, 0); self.nt_headers_offset = self.reserve(self.nt_headers_size(), 8); self.data_directories = vec![DataDirectory::default(); data_directory_num]; self.reserve( data_directory_num as u32 * mem::size_of::() as u32, 1, ); } /// Set the virtual address and size of a data directory. pub fn set_data_directory(&mut self, index: usize, virtual_address: u32, size: u32) { self.data_directories[index] = DataDirectory { virtual_address, size, } } /// Write the NT headers. pub fn write_nt_headers(&mut self, nt_headers: NtHeaders) { self.pad_until(self.nt_headers_offset); self.buffer.write(&U32::new(LE, pe::IMAGE_NT_SIGNATURE)); let file_header = pe::ImageFileHeader { machine: U16::new(LE, nt_headers.machine), number_of_sections: U16::new(LE, self.section_header_num), time_date_stamp: U32::new(LE, nt_headers.time_date_stamp), pointer_to_symbol_table: U32::new(LE, self.symbol_offset), number_of_symbols: U32::new(LE, self.symbol_num), size_of_optional_header: U16::new(LE, self.optional_header_size() as u16), characteristics: U16::new(LE, nt_headers.characteristics), }; self.buffer.write(&file_header); if self.is_64 { let optional_header = pe::ImageOptionalHeader64 { magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC), major_linker_version: nt_headers.major_linker_version, minor_linker_version: nt_headers.minor_linker_version, size_of_code: U32::new(LE, self.code_len), size_of_initialized_data: U32::new(LE, self.data_len), size_of_uninitialized_data: U32::new(LE, self.bss_len), address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point), base_of_code: U32::new(LE, self.code_address), image_base: U64::new(LE, nt_headers.image_base), section_alignment: U32::new(LE, self.section_alignment), file_alignment: U32::new(LE, self.file_alignment), major_operating_system_version: U16::new( LE, nt_headers.major_operating_system_version, ), minor_operating_system_version: U16::new( LE, nt_headers.minor_operating_system_version, ), major_image_version: U16::new(LE, nt_headers.major_image_version), minor_image_version: U16::new(LE, nt_headers.minor_image_version), major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version), minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version), win32_version_value: U32::new(LE, 0), size_of_image: U32::new(LE, self.virtual_len), size_of_headers: U32::new(LE, self.headers_len), check_sum: U32::new(LE, 0), subsystem: U16::new(LE, nt_headers.subsystem), dll_characteristics: U16::new(LE, nt_headers.dll_characteristics), size_of_stack_reserve: U64::new(LE, nt_headers.size_of_stack_reserve), size_of_stack_commit: U64::new(LE, nt_headers.size_of_stack_commit), size_of_heap_reserve: U64::new(LE, nt_headers.size_of_heap_reserve), size_of_heap_commit: U64::new(LE, nt_headers.size_of_heap_commit), loader_flags: U32::new(LE, 0), number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32), }; self.buffer.write(&optional_header); } else { let optional_header = pe::ImageOptionalHeader32 { magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC), major_linker_version: nt_headers.major_linker_version, minor_linker_version: nt_headers.minor_linker_version, size_of_code: U32::new(LE, self.code_len), size_of_initialized_data: U32::new(LE, self.data_len), size_of_uninitialized_data: U32::new(LE, self.bss_len), address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point), base_of_code: U32::new(LE, self.code_address), base_of_data: U32::new(LE, self.data_address), image_base: U32::new(LE, nt_headers.image_base as u32), section_alignment: U32::new(LE, self.section_alignment), file_alignment: U32::new(LE, self.file_alignment), major_operating_system_version: U16::new( LE, nt_headers.major_operating_system_version, ), minor_operating_system_version: U16::new( LE, nt_headers.minor_operating_system_version, ), major_image_version: U16::new(LE, nt_headers.major_image_version), minor_image_version: U16::new(LE, nt_headers.minor_image_version), major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version), minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version), win32_version_value: U32::new(LE, 0), size_of_image: U32::new(LE, self.virtual_len), size_of_headers: U32::new(LE, self.headers_len), check_sum: U32::new(LE, 0), subsystem: U16::new(LE, nt_headers.subsystem), dll_characteristics: U16::new(LE, nt_headers.dll_characteristics), size_of_stack_reserve: U32::new(LE, nt_headers.size_of_stack_reserve as u32), size_of_stack_commit: U32::new(LE, nt_headers.size_of_stack_commit as u32), size_of_heap_reserve: U32::new(LE, nt_headers.size_of_heap_reserve as u32), size_of_heap_commit: U32::new(LE, nt_headers.size_of_heap_commit as u32), loader_flags: U32::new(LE, 0), number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32), }; self.buffer.write(&optional_header); } for dir in &self.data_directories { self.buffer.write(&pe::ImageDataDirectory { virtual_address: U32::new(LE, dir.virtual_address), size: U32::new(LE, dir.size), }) } } /// Reserve the section headers. /// /// The number of reserved section headers must be the same as the number of sections that /// are later reserved. // TODO: change this to a maximum number of sections? pub fn reserve_section_headers(&mut self, section_header_num: u16) { debug_assert_eq!(self.section_header_num, 0); self.section_header_num = section_header_num; self.reserve( u32::from(section_header_num) * mem::size_of::() as u32, 1, ); // Padding before sections must be included in headers_len. self.reserve_align(self.file_alignment); self.headers_len = self.len; self.reserve_virtual(self.len); } /// Write the section headers. /// /// This uses information that was recorded when the sections were reserved. pub fn write_section_headers(&mut self) { debug_assert_eq!(self.section_header_num as usize, self.sections.len()); for section in &self.sections { let section_header = pe::ImageSectionHeader { name: section.name, virtual_size: U32::new(LE, section.range.virtual_size), virtual_address: U32::new(LE, section.range.virtual_address), size_of_raw_data: U32::new(LE, section.range.file_size), pointer_to_raw_data: U32::new(LE, section.range.file_offset), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new(LE, section.characteristics), }; self.buffer.write(§ion_header); } } /// Reserve a section. /// /// Returns the file range and virtual address range that are reserved /// for the section. pub fn reserve_section( &mut self, name: [u8; 8], characteristics: u32, virtual_size: u32, data_size: u32, ) -> SectionRange { let virtual_address = self.reserve_virtual(virtual_size); // Padding after section must be included in section file size. let file_size = util::align_u32(data_size, self.file_alignment); let file_offset = if file_size != 0 { self.reserve(file_size, self.file_alignment) } else { 0 }; // Sizes in optional header use the virtual size with the file alignment. let aligned_virtual_size = util::align_u32(virtual_size, self.file_alignment); if characteristics & pe::IMAGE_SCN_CNT_CODE != 0 { if self.code_address == 0 { self.code_address = virtual_address; } self.code_len += aligned_virtual_size; } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { if self.data_address == 0 { self.data_address = virtual_address; } self.data_len += aligned_virtual_size; } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { if self.data_address == 0 { self.data_address = virtual_address; } self.bss_len += aligned_virtual_size; } let range = SectionRange { virtual_address, virtual_size, file_offset, file_size, }; self.sections.push(Section { name, characteristics, range, }); range } /// Write the data for a section. pub fn write_section(&mut self, offset: u32, data: &[u8]) { if data.is_empty() { return; } self.pad_until(offset); self.write(data); self.write_align(self.file_alignment); } /// Reserve a `.text` section. /// /// Contains executable code. pub fn reserve_text_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".text\0\0\0", pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE | pe::IMAGE_SCN_MEM_READ, size, size, ) } /// Reserve a `.data` section. /// /// Contains initialized data. /// /// May also contain uninitialized data if `virtual_size` is greater than `data_size`. pub fn reserve_data_section(&mut self, virtual_size: u32, data_size: u32) -> SectionRange { self.reserve_section( *b".data\0\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, virtual_size, data_size, ) } /// Reserve a `.rdata` section. /// /// Contains read-only initialized data. pub fn reserve_rdata_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".rdata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ) } /// Reserve a `.bss` section. /// /// Contains uninitialized data. pub fn reserve_bss_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".bss\0\0\0\0", pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, size, 0, ) } /// Reserve an `.idata` section. /// /// Contains import tables. Note that it is permissible to store import tables in a different /// section. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_IMPORT` data directory. pub fn reserve_idata_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".idata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_IMPORT]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Reserve an `.edata` section. /// /// Contains export tables. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXPORT` data directory. pub fn reserve_edata_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".edata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXPORT]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Reserve a `.pdata` section. /// /// Contains exception information. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION` data directory. pub fn reserve_pdata_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".pdata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Reserve a `.xdata` section. /// /// Contains exception information. pub fn reserve_xdata_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".xdata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ) } /// Reserve a `.rsrc` section. /// /// Contains the resource directory. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_RESOURCE` data directory. pub fn reserve_rsrc_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".rsrc\0\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_RESOURCE]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Add a base relocation. /// /// `typ` must be one of the `IMAGE_REL_BASED_*` constants. pub fn add_reloc(&mut self, mut virtual_address: u32, typ: u16) { let reloc = U16::new(LE, typ << 12 | (virtual_address & 0xfff) as u16); virtual_address &= !0xfff; if let Some(block) = self.reloc_blocks.last_mut() { if block.virtual_address == virtual_address { self.relocs.push(reloc); block.count += 1; return; } // Blocks must have an even number of relocations. if block.count & 1 != 0 { self.relocs.push(U16::new(LE, 0)); block.count += 1; } debug_assert!(block.virtual_address < virtual_address); } self.relocs.push(reloc); self.reloc_blocks.push(RelocBlock { virtual_address, count: 1, }); } /// Return true if a base relocation has been added. pub fn has_relocs(&mut self) -> bool { !self.relocs.is_empty() } /// Reserve a `.reloc` section. /// /// This contains the base relocations that were added with `add_reloc`. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_BASERELOC` data directory. pub fn reserve_reloc_section(&mut self) -> SectionRange { if let Some(block) = self.reloc_blocks.last_mut() { // Blocks must have an even number of relocations. if block.count & 1 != 0 { self.relocs.push(U16::new(LE, 0)); block.count += 1; } } let size = self.reloc_blocks.iter().map(RelocBlock::size).sum(); let range = self.reserve_section( *b".reloc\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_DISCARDABLE, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_BASERELOC]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; self.reloc_offset = range.file_offset; range } /// Write a `.reloc` section. /// /// This contains the base relocations that were added with `add_reloc`. pub fn write_reloc_section(&mut self) { if self.reloc_offset == 0 { return; } self.pad_until(self.reloc_offset); let mut total = 0; for block in &self.reloc_blocks { self.buffer.write(&pe::ImageBaseRelocation { virtual_address: U32::new(LE, block.virtual_address), size_of_block: U32::new(LE, block.size()), }); self.buffer .write_slice(&self.relocs[total..][..block.count as usize]); total += block.count as usize; } debug_assert_eq!(total, self.relocs.len()); self.write_align(self.file_alignment); } /// Reserve the certificate table. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_SECURITY` data directory. // TODO: reserve individual certificates pub fn reserve_certificate_table(&mut self, size: u32) { let size = util::align_u32(size, 8); let offset = self.reserve(size, 8); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: offset, size, }; } /// Write the certificate table. // TODO: write individual certificates pub fn write_certificate_table(&mut self, data: &[u8]) { let dir = self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY]; self.pad_until(dir.virtual_address); self.write(data); self.pad_until(dir.virtual_address + dir.size); } } /// Information required for writing [`pe::ImageNtHeaders32`] or [`pe::ImageNtHeaders64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct NtHeaders { // ImageFileHeader pub machine: u16, pub time_date_stamp: u32, pub characteristics: u16, // ImageOptionalHeader pub major_linker_version: u8, pub minor_linker_version: u8, pub address_of_entry_point: u32, pub image_base: u64, pub major_operating_system_version: u16, pub minor_operating_system_version: u16, pub major_image_version: u16, pub minor_image_version: u16, pub major_subsystem_version: u16, pub minor_subsystem_version: u16, pub subsystem: u16, pub dll_characteristics: u16, pub size_of_stack_reserve: u64, pub size_of_stack_commit: u64, pub size_of_heap_reserve: u64, pub size_of_heap_commit: u64, } #[derive(Default, Clone, Copy)] struct DataDirectory { virtual_address: u32, size: u32, } /// Information required for writing [`pe::ImageSectionHeader`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Section { pub name: [u8; pe::IMAGE_SIZEOF_SHORT_NAME], pub characteristics: u32, pub range: SectionRange, } /// The file range and virtual address range for a section. #[allow(missing_docs)] #[derive(Debug, Default, Clone, Copy)] pub struct SectionRange { pub virtual_address: u32, pub virtual_size: u32, pub file_offset: u32, pub file_size: u32, } struct RelocBlock { virtual_address: u32, count: u32, } impl RelocBlock { fn size(&self) -> u32 { mem::size_of::() as u32 + self.count * mem::size_of::() as u32 } }