#![allow(clippy::nonstandard_macro_braces)]
use anyhow::Result;
use byteorder::{ByteOrder, LittleEndian};
use thiserror::Error;
use crate::{
arch::Arch,
pagemap::{PageMap, PageMapError},
RVA, VA,
};
#[derive(Debug, Error)]
pub enum AddressSpaceError {
#[error("String is too short")]
StringTooShort,
}
pub trait AddressSpace<T> {
fn read_into(&self, offset: T, buf: &mut [u8]) -> Result<()>;
fn read_u8(&self, offset: T) -> Result<u8> {
let mut buf = [0u8; 1];
self.read_into(offset, &mut buf)?;
Ok(buf[0])
}
fn read_u16(&self, offset: T) -> Result<u16> {
let mut buf = [0u8; 2];
self.read_into(offset, &mut buf)?;
Ok(LittleEndian::read_u16(&buf))
}
fn read_u32(&self, offset: T) -> Result<u32> {
let mut buf = [0u8; 4];
self.read_into(offset, &mut buf)?;
Ok(LittleEndian::read_u32(&buf))
}
fn read_u64(&self, offset: T) -> Result<u64> {
let mut buf = [0u8; 8];
self.read_into(offset, &mut buf)?;
Ok(LittleEndian::read_u64(&buf))
}
fn read_pointer(&self, arch: Arch, offset: T) -> Result<u64> {
match arch {
Arch::X32 => Ok(self.read_u32(offset)? as u64),
Arch::X64 => Ok(self.read_u64(offset)?),
}
}
fn read_bytes(&self, offset: T, length: usize) -> Result<Vec<u8>> {
let mut buf = vec![0u8; length];
self.read_into(offset, &mut buf)?;
Ok(buf)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>>;
fn read_ascii(&self, offset: T, minimum_length: usize) -> Result<String>;
}
pub trait WritableAddressSpace<T> {
fn write(&mut self, addr: T, v: &[u8]) -> Result<()>;
fn write_u16(&mut self, addr: T, v: u16) -> Result<()> {
let mut src = [0u8; std::mem::size_of::<u16>()];
LittleEndian::write_u16(&mut src, v);
self.write(addr, &src[..])
}
fn write_u32(&mut self, addr: T, v: u32) -> Result<()> {
let mut src = [0u8; std::mem::size_of::<u32>()];
LittleEndian::write_u32(&mut src, v);
self.write(addr, &src[..])
}
fn write_i32(&mut self, addr: T, v: i32) -> Result<()> {
let mut src = [0u8; std::mem::size_of::<i32>()];
LittleEndian::write_i32(&mut src, v);
self.write(addr, &src[..])
}
fn write_u64(&mut self, addr: T, v: u64) -> Result<()> {
let mut src = [0u8; std::mem::size_of::<u64>()];
LittleEndian::write_u64(&mut src, v);
self.write(addr, &src[..])
}
fn write_i64(&mut self, addr: T, v: i64) -> Result<()> {
let mut src = [0u8; std::mem::size_of::<i64>()];
LittleEndian::write_i64(&mut src, v);
self.write(addr, &src[..])
}
}
#[derive(Clone)]
pub struct RelativeAddressSpace {
pub(crate) map: PageMap<u8>,
}
impl RelativeAddressSpace {
pub fn into_absolute(self, base_address: VA) -> Result<AbsoluteAddressSpace> {
Ok(AbsoluteAddressSpace {
base_address,
relative: self,
})
}
pub fn with_capacity(size: u64) -> RelativeAddressSpace {
RelativeAddressSpace {
map: PageMap::with_capacity(size),
}
}
pub fn from_buf(buf: &[u8]) -> RelativeAddressSpace {
RelativeAddressSpace {
map: PageMap::from_items(buf),
}
}
}
impl AddressSpace<RVA> for RelativeAddressSpace {
fn read_into(&self, offset: RVA, buf: &mut [u8]) -> Result<()> {
self.map.slice_into(offset, buf)?;
Ok(())
}
fn read_ascii(&self, offset: RVA, minimum_length: usize) -> Result<String> {
const END_OF_ASCII: u8 = 0x7F;
const SPACE: u8 = 0x20;
const TAB: u8 = 0x9;
const NEWLINE: u8 = 0xA;
const LINEFEED: u8 = 0xD;
let buf: Vec<u8> = (offset..u64::MAX)
.map(|offset| self.map.get(offset))
.take_while(|c| c.is_some())
.map(|c| c.unwrap())
.take_while(|&c| c != 0)
.take_while(|&c| c < END_OF_ASCII && (c >= SPACE || c == TAB || c == NEWLINE || c == LINEFEED))
.collect();
if buf.len() < minimum_length {
return Err(AddressSpaceError::StringTooShort.into());
}
Ok(String::from_utf8(buf)?)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>> {
Ok(AddressSpaceSlice {
base_address: offset,
inner: Box::new(self),
})
}
}
pub const PAGE_SIZE: usize = 0x1000;
pub const PAGE_SHIFT: usize = 12;
pub const PAGE_MASK: u64 = 0xFFF;
pub fn is_page_aligned(va: VA) -> bool {
va & PAGE_MASK == 0x0
}
pub fn page_address(va: VA) -> u64 {
(va >> PAGE_SHIFT) << PAGE_SHIFT
}
pub fn page_offset(va: VA) -> usize {
(va & PAGE_MASK) as usize
}
impl WritableAddressSpace<RVA> for RelativeAddressSpace {
fn write(&mut self, addr: RVA, buf: &[u8]) -> Result<()> {
assert!(buf.len() <= PAGE_SIZE);
let end_addr = addr + buf.len() as u64;
if page_address(addr) != page_address(end_addr) && !is_page_aligned(end_addr) {
let write_size: usize = buf.len();
let page_offset = page_offset(addr);
let first_size = PAGE_SIZE - page_offset;
let second_size = write_size - first_size;
{
let mut page = [0u8; PAGE_SIZE];
self.map.slice_into(page_address(addr), &mut page[..])?;
let dst = &mut page[page_offset..];
dst.copy_from_slice(&buf[0..first_size]);
self.map.write(page_address(addr), &page)?;
}
{
let mut page = [0u8; PAGE_SIZE];
self.map
.slice_into(page_address(addr) + PAGE_SIZE as u64, &mut page[..])?;
let dst = &mut page[0..second_size];
dst.copy_from_slice(&buf[first_size..]);
self.map.write(page_address(addr) + PAGE_SIZE as u64, &page)?;
}
} else {
let mut page = [0u8; PAGE_SIZE];
self.map.slice_into(page_address(addr), &mut page[..])?;
let dst = &mut page[page_offset(addr)..page_offset(addr) + buf.len()];
dst.copy_from_slice(buf);
self.map.write(page_address(addr), &page)?;
}
Ok(())
}
}
impl AddressSpace<RVA> for &RelativeAddressSpace {
fn read_into(&self, offset: RVA, buf: &mut [u8]) -> Result<()> {
(*self).read_into(offset, buf)
}
fn read_ascii(&self, offset: RVA, minimum_length: usize) -> Result<String> {
(*self).read_ascii(offset, minimum_length)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>> {
(*self).slice(offset)
}
}
impl WritableAddressSpace<RVA> for &mut RelativeAddressSpace {
fn write(&mut self, addr: RVA, buf: &[u8]) -> Result<()> {
(*self).write(addr, buf)
}
}
#[derive(Clone)]
pub struct AbsoluteAddressSpace {
pub base_address: VA,
pub relative: RelativeAddressSpace,
}
impl AbsoluteAddressSpace {}
impl AddressSpace<VA> for AbsoluteAddressSpace {
fn read_into(&self, offset: VA, buf: &mut [u8]) -> Result<()> {
if offset < self.base_address {
return Err(PageMapError::NotMapped.into());
}
self.relative.read_into((offset - self.base_address) as RVA, buf)
}
fn read_ascii(&self, offset: VA, minimum_length: usize) -> Result<String> {
if offset < self.base_address {
return Err(PageMapError::NotMapped.into());
}
self.relative
.read_ascii((offset - self.base_address) as RVA, minimum_length)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>> {
Ok(AddressSpaceSlice {
base_address: offset,
inner: Box::new(self),
})
}
}
impl WritableAddressSpace<VA> for AbsoluteAddressSpace {
fn write(&mut self, addr: VA, buf: &[u8]) -> Result<()> {
if addr < self.base_address {
return Err(PageMapError::NotMapped.into());
}
self.relative.write((addr - self.base_address) as RVA, buf)
}
}
impl AddressSpace<VA> for &AbsoluteAddressSpace {
fn read_into(&self, offset: VA, buf: &mut [u8]) -> Result<()> {
(*self).read_into(offset, buf)
}
fn read_ascii(&self, offset: VA, minimum_length: usize) -> Result<String> {
(*self).read_ascii(offset, minimum_length)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>> {
(*self).slice(offset)
}
}
impl WritableAddressSpace<VA> for &mut AbsoluteAddressSpace {
fn write(&mut self, addr: VA, buf: &[u8]) -> Result<()> {
(*self).write(addr, buf)
}
}
pub struct AddressSpaceSlice<'a> {
base_address: RVA,
inner: Box<dyn AddressSpace<u64> + 'a>,
}
impl AddressSpace<RVA> for AddressSpaceSlice<'_> {
fn read_into(&self, offset: RVA, buf: &mut [u8]) -> Result<()> {
let offset = self.base_address + offset;
self.inner.read_into(offset, buf)
}
fn read_ascii(&self, offset: RVA, minimum_length: usize) -> Result<String> {
let offset = self.base_address + offset;
self.inner.read_ascii(offset, minimum_length)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>> {
Ok(AddressSpaceSlice {
base_address: offset,
inner: Box::new(self),
})
}
}
impl AddressSpace<RVA> for &AddressSpaceSlice<'_> {
fn read_into(&self, offset: RVA, buf: &mut [u8]) -> Result<()> {
(*self).read_into(offset, buf)
}
fn read_ascii(&self, offset: RVA, minimum_length: usize) -> Result<String> {
(*self).read_ascii(offset, minimum_length)
}
fn slice(&self, offset: RVA) -> Result<AddressSpaceSlice<'_>> {
(*self).slice(offset)
}
}