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};
#[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<DataDirectory>,
section_header_num: u16,
sections: Vec<Section>,
symbol_offset: u32,
symbol_num: u32,
reloc_blocks: Vec<RelocBlock>,
relocs: Vec<U16<LE>>,
reloc_offset: u32,
}
impl<'a> Writer<'a> {
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,
}
}
pub fn virtual_len(&self) -> u32 {
self.virtual_len
}
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
}
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);
}
pub fn reserved_len(&self) -> u32 {
self.len
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.buffer.len()
}
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
}
pub fn reserve_file(&mut self, len: u32) -> u32 {
self.reserve(len, self.file_alignment)
}
pub fn write(&mut self, data: &[u8]) {
self.buffer.write_bytes(data);
}
pub fn reserve_align(&mut self, align_start: u32) {
self.len = util::align_u32(self.len, align_start);
}
pub fn write_align(&mut self, align_start: u32) {
util::write_align(self.buffer, align_start as usize);
}
pub fn write_file_align(&mut self) {
self.write_align(self.file_alignment);
}
pub fn reserve_until(&mut self, offset: u32) {
debug_assert!(self.len <= offset);
self.len = offset;
}
pub fn pad_until(&mut self, offset: u32) {
debug_assert!(self.buffer.len() <= offset as usize);
self.buffer.resize(offset as usize);
}
pub fn reserve_dos_header(&mut self) {
debug_assert_eq!(self.len, 0);
self.reserve(mem::size_of::<pe::ImageDosHeader>() as u32, 1);
}
pub fn write_custom_dos_header(&mut self, dos_header: &pe::ImageDosHeader) -> Result<()> {
debug_assert_eq!(self.buffer.len(), 0);
self.buffer
.reserve(self.len as usize)
.map_err(|_| Error(String::from("Cannot allocate buffer")))?;
self.buffer.write(dos_header);
Ok(())
}
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),
})
}
pub fn reserve_dos_header_and_stub(&mut self) {
self.reserve_dos_header();
self.reserve(64, 1);
}
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::<pe::ImageNtHeaders64>() as u32
} else {
mem::size_of::<pe::ImageNtHeaders32>() as u32
}
}
fn optional_header_size(&self) -> u32 {
let size = if self.is_64 {
mem::size_of::<pe::ImageOptionalHeader64>() as u32
} else {
mem::size_of::<pe::ImageOptionalHeader32>() as u32
};
size + self.data_directories.len() as u32 * mem::size_of::<pe::ImageDataDirectory>() as u32
}
pub fn nt_headers_offset(&self) -> u32 {
self.nt_headers_offset
}
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::<pe::ImageDataDirectory>() as u32,
1,
);
}
pub fn set_data_directory(&mut self, index: usize, virtual_address: u32, size: u32) {
self.data_directories[index] = DataDirectory {
virtual_address,
size,
}
}
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),
})
}
}
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::<pe::ImageSectionHeader>() as u32,
1,
);
self.reserve_align(self.file_alignment);
self.headers_len = self.len;
self.reserve_virtual(self.len);
}
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);
}
}
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);
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
};
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
}
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);
}
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,
)
}
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,
)
}
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,
)
}
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,
)
}
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
}
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
}
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
}
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,
)
}
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
}
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;
}
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,
});
}
pub fn has_relocs(&mut self) -> bool {
!self.relocs.is_empty()
}
pub fn reserve_reloc_section(&mut self) -> SectionRange {
if let Some(block) = self.reloc_blocks.last_mut() {
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
}
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);
}
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,
};
}
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);
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct NtHeaders {
pub machine: u16,
pub time_date_stamp: u32,
pub characteristics: u16,
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,
}
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Section {
pub name: [u8; pe::IMAGE_SIZEOF_SHORT_NAME],
pub characteristics: u32,
pub range: SectionRange,
}
#[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::<pe::ImageBaseRelocation>() as u32 + self.count * mem::size_of::<u16>() as u32
}
}