use crate::Result;
use core::fmt::{Display, Formatter};
use winapi::shared::minwindef::{FALSE, LPCVOID, LPVOID};
use alloc::vec::Vec;
use super::macros::err;
use super::process::Process;
use winapi::um::memoryapi::{ReadProcessMemory, VirtualAllocEx, VirtualFreeEx, WriteProcessMemory};
use winapi::um::winnt::{
MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE,
PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE,
};
#[derive(Debug)]
pub struct MemPage<'a> {
proc: &'a Process,
addr: LPVOID,
size: usize,
exec: bool,
}
impl<'a> MemPage<'a> {
pub fn new(proc: &'a Process, size: usize, exec: bool) -> Result<Self> {
if size == 0 {
return Err(crate::error::CustomError::InvalidInput.into());
}
if !proc.has_perm(PROCESS_VM_OPERATION) {
return Err(crate::error::CustomError::PermissionDenied.into());
}
let addr = unsafe {
VirtualAllocEx(
proc.get_proc(),
core::ptr::null_mut(),
size,
MEM_COMMIT | MEM_RESERVE,
if exec {
PAGE_EXECUTE_READWRITE
} else {
PAGE_READWRITE
},
)
};
if addr.is_null() {
return Err(err(
"VirtualAllocEx failed to allocate the requested amount of bytes on a process",
));
}
Ok(MemPage {
proc,
addr,
size,
exec,
})
}
pub fn write<T>(&mut self, buffer: &[T]) -> Result<usize> {
let t_len: usize = core::mem::size_of::<T>();
if !self.proc.has_perm(PROCESS_VM_WRITE) {
return Err(crate::error::CustomError::PermissionDenied.into());
}
let mut n: usize = 0;
assert!(buffer.len() * t_len <= self.size);
if unsafe {
WriteProcessMemory(
self.proc.get_proc(),
self.addr,
buffer.as_ptr() as LPCVOID,
buffer.len() * t_len,
&mut n as *mut usize,
) == FALSE
} {
return Err(err("WriteProcessMemory"));
}
debug_assert!(n == buffer.len() * t_len);
Ok(n)
}
#[allow(unused)]
pub fn read(&self, size: usize) -> Result<Vec<u8>> {
if !self.proc.has_perm(PROCESS_VM_READ) {
return Err(crate::error::CustomError::PermissionDenied.into());
}
let mut buf = Vec::with_capacity(size);
let mut o = 0;
if unsafe {
ReadProcessMemory(
self.proc.get_proc(),
self.addr,
buf.as_mut_ptr() as LPVOID,
size,
&mut o as *mut usize,
)
} == FALSE
{
return Err(err("ReadProcessMemory"));
}
assert!(
o <= size,
"Buffer overflow occurred. Results may cause unknown code to execute. Aborting {}>{}",
o,
size
);
unsafe {
buf.set_len(o);
buf.shrink_to_fit();
}
Ok(buf)
}
#[must_use]
pub fn get_address(&self) -> LPVOID {
self.addr
}
#[must_use]
#[allow(dead_code)]
pub fn check_proc(&self, proc: &Process) -> bool {
self.proc.get_pid() == proc.get_pid()
}
}
impl<'a> Display for MemPage<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(
f,
"(proc:{:x}, addr:{:x}, size:{:x}, exec:{})",
self.proc.get_proc() as usize,
self.addr as usize,
self.size,
self.exec
)
}
}
impl<'a> Drop for MemPage<'a> {
fn drop(&mut self) {
crate::trace!("Releasing VirtualAlloc'd Memory");
if unsafe { VirtualFreeEx(self.proc.get_proc(), self.addr, 0, MEM_RELEASE) } == FALSE {
crate::error!("Error during cleanup! VirtualFreeEx with MEM_RELEASE should not fail according to doc, but did anyways. A memory page will stay allocated. Addr:{:x},size:{:x}",self.addr as usize,self.size);
err("VirtualFreeEx of VirtualAllocEx");
#[cfg(test)]
panic!("VirtualFreeEx resulted in an error");
}
}
}
#[cfg(test)]
mod test {
use crate::Result;
#[test]
fn zero_size_page() {
let proc = &super::super::process::Process::self_proc();
let m = super::MemPage::new(proc, 0, false);
assert!(
m.is_err(),
"A zero sized allocation is not sensible. Also windows does not allow it."
)
}
#[test]
fn check_proc() -> Result<()> {
let proc = &super::super::process::Process::self_proc();
let m = super::MemPage::new(proc, 1, false)?;
assert!(m.check_proc(proc));
Ok(())
}
#[test]
fn new_and_write() -> Result<()> {
let buf: alloc::vec::Vec<u8> = (0..255).collect();
let proc = &super::super::process::Process::self_proc();
let mut m = super::MemPage::new(proc, buf.len(), false)?;
let w = m.write(buf.as_slice())?;
assert!(w >= buf.len());
let rb: *const [u8; 255] = m.get_address() as *const [u8; 255];
let rb = unsafe { *rb };
assert_eq!(rb, buf.as_slice());
assert_eq!(buf, m.read(w)?);
Ok(())
}
#[test]
fn other_proc() -> Result<()> {
let buf: alloc::vec::Vec<u8> = (0..255).collect();
let (mut c, proc) = super::super::test::create_cmd();
let mut m = super::MemPage::new(&proc, buf.len(), false)?;
let w = m.write(buf.as_slice())?;
assert!(w >= buf.len());
assert_eq!(m.read(w)?, buf);
c.kill().unwrap();
Ok(())
}
}