use crate::{
containers::Table,
error::Result,
io::{WriteData, Writer},
pe::{
coff::{CoffFileHeader, ImageFileCharacteristics, ImageFileMachine},
dos::ImageDosHeader,
optional_header::{self, data_directories::SectionName},
sections::{SectionFlags, SectionTableRow, Sections},
},
string::String,
vec::Vec,
PEFile,
};
pub struct PEImageDef {
pub dos_header: ImageDosHeader,
pub file_characteristics: ImageFileCharacteristics,
pub optional_header: optional_header::OptionalHeader,
pub sections: SectionDefinitions,
}
impl PEImageDef {
pub fn from_pe_file(pe_file: PEFile) -> Self {
let PEFile {
dos_header,
coff_header,
optional_header,
sections,
..
} = pe_file;
let sections = {
let Sections(Table(section_rows)) = sections;
let mapped_sections = section_rows.into_iter().map(|s| SectionHeap {
data: Vec::from(s.data),
name: s.row.name_str().into(),
characteristics: s.row.characteristics,
virtual_address: s.row.virtual_address,
virtual_size: s.row.virtual_size,
});
let mut sections = SectionDefinitions::default();
for s in mapped_sections {
match s.name.as_str() {
".text" => sections.text = Some(s),
".data" => sections.data = Some(s),
".rdata" => sections.rdata = Some(s),
".reloc" => sections.reloc = Some(s),
".pdata" => sections.pdata = Some(s),
_ => sections.other.push(s),
}
}
sections
};
Self {
dos_header,
optional_header: optional_header.unwrap_or_default(),
sections,
file_characteristics: coff_header.characteristics,
}
}
pub fn new_section(
&mut self,
name: impl Into<String>,
characteristics: SectionFlags,
) -> &mut SectionHeap {
let section = SectionHeap {
name: name.into(),
virtual_address: self.sections.next_virtual_address() as u32,
data: Vec::new(),
characteristics,
virtual_size: 0,
};
self.sections.other.push(section);
self.sections.other.last_mut().unwrap()
}
pub fn fix_headers(&mut self) {
let file_alignment = self.optional_header.windows_specific_fields.file_alignment() as usize;
let allign_section = |addr: usize| {
(addr + file_alignment) & (!(file_alignment - 1))
};
self.optional_header.windows_specific_fields.set_size_of_headers(
allign_section(self.dos_header.e_lfanew as usize
+ PEFile::SIGNATURE.len()
+ CoffFileHeader::SIZE
+ self.optional_header.size()
+ (SectionTableRow::SIZE * self.sections.count())) as u32
);
self.optional_header.windows_specific_fields.set_size_of_image(self.output_size() as u32);
let number_of_data_directories =
SectionName::ALL
.iter()
.enumerate()
.fold(0, |acc, (i, dir)| {
if self
.optional_header
.data_directories
.get_directory(*dir)
.is_null()
{
(i+1) as u32
} else {
acc
}
});
self.optional_header
.windows_specific_fields
.set_number_of_rva_and_sizes(
self.optional_header
.windows_specific_fields
.number_of_rva_and_sizes()
.max(number_of_data_directories),
);
}
pub fn output_size(&self) -> usize {
let file_alignment = self.optional_header.windows_specific_fields.section_alignment() as usize;
let allign_section = |addr: usize| {
(addr + file_alignment) & (!(file_alignment - 1))
};
self.optional_header.windows_specific_fields.size_of_headers() as usize
+ self
.sections
.iter_sections()
.map(|s| {
if (s.data.len() & (file_alignment-1)) != 0 {
allign_section( s.data.len())
}else {
s.data.len()
}
})
.sum::<usize>()
}
pub fn write_file(&mut self) -> Result<crate::vec::Vec<u8>> {
self.fix_headers();
self.write_no_fix()
}
pub fn write_no_fix(&self) -> Result<Vec<u8>> {
let mut buffer = crate::vec::Vec::with_capacity(self.output_size());
self.dos_header.write_to(&mut buffer)?;
while buffer.len() < self.dos_header.e_lfanew as usize {
buffer.write(0u8)?;
}
PEFile::SIGNATURE.write_to(&mut buffer)?;
CoffFileHeader {
machine: ImageFileMachine::Amd64,
characteristics: self.file_characteristics,
size_of_optional_header: self.optional_header.size() as u16,
number_of_sections: self.sections.count() as u16,
..Default::default()
}
.write_to(&mut buffer)?;
self.optional_header.write_to(&mut buffer)?;
let file_alignment = self.optional_header.windows_specific_fields.file_alignment() as usize;
let allign_section = |addr: usize| {
(addr + file_alignment) & (!(file_alignment - 1))
};
let mut data_offset = allign_section(buffer.len() + (SectionTableRow::SIZE * self.sections.count()));
let sections: Vec<_> = self
.sections
.iter_sections()
.map(|sec| {
(&sec.data, {
let name = {
let mut name_buffer = [0u8; 8];
for (c, buff) in sec.name.chars().take(8).zip(name_buffer.iter_mut()) {
*buff = c as u8;
}
name_buffer
};
let pointer_to_raw_data = data_offset as u32;
data_offset += sec.data.len();
if (data_offset & (file_alignment-1)) != 0 {
data_offset = allign_section( data_offset);
}
SectionTableRow {
name,
virtual_size: sec.virtual_size.max(sec.data.len() as u32),
virtual_address: sec.virtual_address,
characteristics: sec.characteristics,
pointer_to_raw_data,
size_of_raw_data: sec.data.len() as u32,
..Default::default()
}
})
})
.collect();
for (_, sec) in §ions {
sec.write_to(&mut buffer)?;
}
for (sec_data, sec) in sections {
while buffer.len() < sec.pointer_to_raw_data as usize {
buffer.write(0u8)?;
}
buffer.write_slice(&sec_data)?;
}
Ok(buffer)
}
}
#[derive(Debug, Clone, Default)]
pub struct SectionDefinitions {
pub text: Option<SectionHeap>,
pub data: Option<SectionHeap>,
pub rdata: Option<SectionHeap>,
pub pdata: Option<SectionHeap>,
pub reloc: Option<SectionHeap>,
pub other: Vec<SectionHeap>,
}
impl SectionDefinitions {
pub const VIRTUAL_ADDRESS_ALIGNMENT: usize = 0x1000;
pub fn allign_to_next_section(addr: usize) -> usize {
(addr + Self::VIRTUAL_ADDRESS_ALIGNMENT) & (!(Self::VIRTUAL_ADDRESS_ALIGNMENT - 1))
}
pub fn iter_sections<'a>(&'a self) -> impl Iterator<Item = &SectionHeap> + 'a {
[&self.text, &self.rdata, &self.data,&self.pdata, &self.reloc]
.into_iter()
.flat_map(|f| f.as_ref())
.chain(self.other.iter())
.filter(|heap| heap.virtual_address != 0)
}
pub fn count(&self) -> usize {
self.iter_sections().count()
}
#[inline(always)]
pub fn find_rva(&self, virtual_address: usize) -> Option<&SectionHeap> {
if virtual_address == 0 {
return None;
}
self.iter_sections().find(|heap| {
virtual_address >= (heap.virtual_address as usize)
&& virtual_address < (heap.virtual_address as usize + heap.data.len() as usize)
})
}
#[inline]
pub fn has_overlapping_sections(&self) -> bool {
for section in self.iter_sections() {
if let Some(overlapping_section) = self.find_rva(section.virtual_address as usize) {
if !core::ptr::eq(section, overlapping_section) {
return true;
}
}
}
false
}
pub fn add_section(&mut self, mut section: SectionHeap) -> usize {
if section.virtual_address == 0 {
section.virtual_address = self.next_virtual_address() as u32;
}
let vaddr = section.virtual_address;
self.other.push(section);
vaddr as usize
}
pub fn next_virtual_address(&self) -> usize {
let v_addr = self.iter_sections().fold(0, |accumulator, heap| {
let heap_end_addr = heap.virtual_address as usize + heap.data.len();
if heap_end_addr > accumulator {
heap_end_addr
} else {
accumulator
}
});
Self::allign_to_next_section(v_addr)
}
}
#[derive(Debug, Clone, Default)]
pub struct SectionHeap {
pub name: String,
pub virtual_address: u32,
pub virtual_size: u32,
pub characteristics: SectionFlags,
pub data: Vec<u8>,
}
impl SectionHeap {
pub fn add_data(&mut self, data: &[u8]) -> usize {
let addr: usize = self.virtual_address as usize + self.data.len();
self.data.extend_from_slice(data);
addr
}
pub fn virtual_size(&self) -> usize {
SectionDefinitions::allign_to_next_section(self.data.len())
}
pub fn remaining_section_size(&self) -> usize {
self.virtual_size() - self.data.len()
}
}