use super::allocator::MappedMemory;
use super::parser::ParsedPe;
use crate::error::{Result, WraithError};
use crate::structures::pe::SectionHeader;
const PAGE_NOACCESS: u32 = 0x01;
const PAGE_READONLY: u32 = 0x02;
const PAGE_READWRITE: u32 = 0x04;
const PAGE_EXECUTE: u32 = 0x10;
const PAGE_EXECUTE_READ: u32 = 0x20;
const PAGE_EXECUTE_READWRITE: u32 = 0x40;
pub fn map_sections(pe: &ParsedPe, memory: &mut MappedMemory) -> Result<()> {
let header_size = pe.size_of_headers();
let raw_data = pe.raw_data();
if header_size > raw_data.len() {
return Err(WraithError::MappingFailed {
section: "headers".into(),
reason: "header size exceeds file size".into(),
});
}
memory.write_at(0, &raw_data[..header_size])?;
for section in pe.sections() {
map_section(pe, memory, section)?;
}
Ok(())
}
fn map_section(pe: &ParsedPe, memory: &mut MappedMemory, section: &SectionHeader) -> Result<()> {
let virtual_address = section.virtual_address as usize;
let virtual_size = section.virtual_size as usize;
let raw_data_ptr = section.pointer_to_raw_data as usize;
let raw_data_size = section.size_of_raw_data as usize;
if raw_data_ptr == 0 || raw_data_size == 0 {
return Ok(());
}
let raw_data = pe.raw_data();
if raw_data_ptr + raw_data_size > raw_data.len() {
return Err(WraithError::MappingFailed {
section: section.name_str().to_string(),
reason: "raw data exceeds file size".into(),
});
}
if virtual_address + virtual_size > memory.size() {
return Err(WraithError::MappingFailed {
section: section.name_str().to_string(),
reason: "virtual size exceeds allocated memory".into(),
});
}
let copy_size = raw_data_size.min(virtual_size);
let section_data = &raw_data[raw_data_ptr..raw_data_ptr + copy_size];
memory.write_at(virtual_address, section_data)?;
Ok(())
}
pub fn set_section_protections(pe: &ParsedPe, memory: &MappedMemory) -> Result<()> {
let header_size = align_up(pe.size_of_headers(), 0x1000);
if header_size > 0 {
memory.protect(0, header_size, PAGE_READONLY)?;
}
for section in pe.sections() {
let protection = section_to_protection(section);
let virtual_address = section.virtual_address as usize;
let size = align_up(section.virtual_size as usize, 0x1000);
if size > 0 && virtual_address + size <= memory.size() {
memory.protect(virtual_address, size, protection)?;
}
}
Ok(())
}
fn section_to_protection(section: &SectionHeader) -> u32 {
let r = section.is_readable();
let w = section.is_writable();
let x = section.is_executable();
match (r, w, x) {
(false, false, false) => PAGE_NOACCESS,
(true, false, false) => PAGE_READONLY,
(true, true, false) => PAGE_READWRITE,
(false, false, true) => PAGE_EXECUTE,
(true, false, true) => PAGE_EXECUTE_READ,
(true, true, true) | (false, true, true) => PAGE_EXECUTE_READWRITE,
(false, true, false) => PAGE_READWRITE, }
}
pub fn find_section<'a>(pe: &'a ParsedPe, name: &str) -> Option<&'a SectionHeader> {
pe.sections().iter().find(|s| s.name_str() == name)
}
pub fn get_text_section(pe: &ParsedPe) -> Option<&SectionHeader> {
find_section(pe, ".text")
}
pub fn get_data_section(pe: &ParsedPe) -> Option<&SectionHeader> {
find_section(pe, ".data")
}
pub fn get_rdata_section(pe: &ParsedPe) -> Option<&SectionHeader> {
find_section(pe, ".rdata")
}
fn align_up(value: usize, alignment: usize) -> usize {
(value + alignment - 1) & !(alignment - 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::manipulation::manual_map::allocator::allocate_anywhere;
#[test]
fn test_align_up() {
assert_eq!(align_up(0, 0x1000), 0);
assert_eq!(align_up(1, 0x1000), 0x1000);
assert_eq!(align_up(0x1000, 0x1000), 0x1000);
assert_eq!(align_up(0x1001, 0x1000), 0x2000);
}
#[test]
fn test_map_self() {
let exe_path = std::env::current_exe().unwrap();
let data = std::fs::read(&exe_path).unwrap();
let pe = ParsedPe::parse(&data).unwrap();
let mut mem = allocate_anywhere(pe.size_of_image()).unwrap();
map_sections(&pe, &mut mem).expect("should map sections");
let dos_sig = mem.read_at::<u16>(0).unwrap();
assert_eq!(dos_sig, 0x5A4D); }
}