use crate::pe::Image;
use crate::runtime::{Sandbox, DLL_PROCESS_ATTACH};
use crate::Error;
pub struct Guest {
sandbox: Sandbox,
image: Image,
}
impl Guest {
pub fn load(name: &str, bytes: &[u8]) -> Result<Self, Error> {
Self::load_into(Sandbox::new(), name, bytes)
}
pub fn load_into(mut sandbox: Sandbox, name: &str, bytes: &[u8]) -> Result<Self, Error> {
let image = sandbox.load(name, bytes)?;
sandbox.call_dll_main(&image, DLL_PROCESS_ATTACH)?;
Ok(Self { sandbox, image })
}
pub fn load_raw(name: &str, bytes: &[u8]) -> Result<Self, Error> {
Self::load_raw_into(Sandbox::new(), name, bytes)
}
pub fn load_raw_into(mut sandbox: Sandbox, name: &str, bytes: &[u8]) -> Result<Self, Error> {
let image = sandbox.load(name, bytes)?;
Ok(Self { sandbox, image })
}
pub fn run_dll_main(&mut self) -> Result<u32, Error> {
self.sandbox.call_dll_main(&self.image, DLL_PROCESS_ATTACH)
}
pub fn call<A, R>(&mut self, export: &str, args: A) -> Result<R, Error>
where
A: CallArgs,
R: FromRet,
{
let dwords = args.into_dwords();
let eax = self.sandbox.call_export(&self.image, export, &dwords)?;
Ok(R::from_eax(eax))
}
#[must_use]
pub fn has_export(&self, name: &str) -> bool {
self.image.export(name).is_some()
}
#[must_use]
pub fn export_addr(&self, name: &str) -> Option<u32> {
self.image.export(name)
}
pub fn alloc(&mut self, bytes: &[u8]) -> Result<u32, Error> {
let len = u32::try_from(bytes.len()).map_err(|_| {
Error::Win32(crate::win32::Win32Error::InvalidArgument {
stub: "Guest::alloc",
reason: "allocation larger than 4 GiB".into(),
})
})?;
let ptr = self
.sandbox
.host
.arena_alloc(len.max(1))
.map_err(Error::Win32)?;
self.sandbox.mmu.write(ptr, bytes).map_err(Error::Trap)?;
Ok(ptr)
}
pub fn alloc_cstr(&mut self, s: &str) -> Result<u32, Error> {
let mut buf = s.as_bytes().to_vec();
buf.push(0);
self.alloc(&buf)
}
pub fn read(&self, ptr: u32, len: usize) -> Result<Vec<u8>, Error> {
self.sandbox.mmu.read(ptr, len).map_err(Error::Trap)
}
pub fn write(&mut self, ptr: u32, data: &[u8]) -> Result<(), Error> {
self.sandbox.mmu.write(ptr, data).map_err(Error::Trap)
}
#[must_use]
pub fn sandbox(&self) -> &Sandbox {
&self.sandbox
}
pub fn sandbox_mut(&mut self) -> &mut Sandbox {
&mut self.sandbox
}
#[must_use]
pub fn image(&self) -> &Image {
&self.image
}
}
pub trait Dword {
fn to_dword(self) -> u32;
}
impl Dword for u32 {
fn to_dword(self) -> u32 {
self
}
}
impl Dword for i32 {
fn to_dword(self) -> u32 {
self as u32
}
}
impl Dword for u16 {
fn to_dword(self) -> u32 {
u32::from(self)
}
}
impl Dword for u8 {
fn to_dword(self) -> u32 {
u32::from(self)
}
}
impl Dword for bool {
fn to_dword(self) -> u32 {
u32::from(self)
}
}
pub trait CallArgs {
fn into_dwords(self) -> Vec<u32>;
}
impl CallArgs for () {
fn into_dwords(self) -> Vec<u32> {
Vec::new()
}
}
macro_rules! impl_call_args {
( $( $name:ident ),+ ) => {
impl< $( $name: Dword ),+ > CallArgs for ( $( $name, )+ ) {
#[allow(non_snake_case)]
fn into_dwords(self) -> Vec<u32> {
let ( $( $name, )+ ) = self;
vec![ $( $name.to_dword() ),+ ]
}
}
};
}
impl_call_args!(A);
impl_call_args!(A, B);
impl_call_args!(A, B, C);
impl_call_args!(A, B, C, D);
impl_call_args!(A, B, C, D, E);
impl_call_args!(A, B, C, D, E, F);
impl_call_args!(A, B, C, D, E, F, G);
impl_call_args!(A, B, C, D, E, F, G, H);
pub trait FromRet {
fn from_eax(eax: u32) -> Self;
}
impl FromRet for u32 {
fn from_eax(eax: u32) -> Self {
eax
}
}
impl FromRet for i32 {
fn from_eax(eax: u32) -> Self {
eax as i32
}
}
impl FromRet for bool {
fn from_eax(eax: u32) -> Self {
eax != 0
}
}
impl FromRet for () {
fn from_eax(_eax: u32) -> Self {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn call_args_lower_in_declaration_order() {
assert_eq!(().into_dwords(), Vec::<u32>::new());
assert_eq!((1u32,).into_dwords(), vec![1]);
assert_eq!((1u32, 2u32, 3u32).into_dwords(), vec![1, 2, 3]);
}
#[test]
fn dword_conversions() {
assert_eq!((-1i32,).into_dwords(), vec![0xFFFF_FFFF]);
assert_eq!((true, false).into_dwords(), vec![1, 0]);
assert_eq!((0x1234u16, 0xABu8).into_dwords(), vec![0x1234, 0xAB]);
}
#[test]
fn from_ret_reinterprets() {
assert_eq!(u32::from_eax(0xFFFF_FFFF), 0xFFFF_FFFF);
assert_eq!(i32::from_eax(0xFFFF_FFFF), -1);
assert!(bool::from_eax(1));
assert!(!bool::from_eax(0));
}
}