use std::io::Error as IOError;
use std::io::IoSlice;
use std::io::Result as IOResult;
use std::io::Write;
use std::io::{ErrorKind, IoSliceMut, Read, Seek, SeekFrom};
use nix::libc::{
MAP_ANONYMOUS, MAP_FIXED, MAP_PRIVATE, MAP_SHARED, PROT_EXEC, PROT_READ, PROT_WRITE,
};
use nix::sys::uio::{process_vm_readv, process_vm_writev, RemoteIoVec};
use crate::error::{
CouldNotAllocate, CouldNotCreateAsyncReaderWriter, CouldNotCreateRemoteView,
CouldNotCreateSyncReaderWriter, CouldNotDeallocate, CouldNotRestorePermissions, MprotectError,
};
use crate::mapping::{memory_mapping, PermissionMatcher, Permissions};
use crate::process::{Pid, TargetController, TargetProcess};
use crate::run::Executing;
use crate::syscall;
pub trait Buffer {
fn address(&self) -> usize;
fn length(&self) -> usize;
fn pid(&self) -> Pid;
}
pub struct RemoteView {
address: usize,
length: usize,
pid: Pid,
}
impl Buffer for RemoteView {
fn address(&self) -> usize {
self.address
}
fn length(&self) -> usize {
self.length
}
fn pid(&self) -> Pid {
self.pid
}
}
pub fn remote_view(
process: &TargetProcess,
address: usize,
length: usize,
) -> Result<RemoteView, CouldNotCreateRemoteView> {
let mapping = memory_mapping(process)?;
let upper_bound = address + length - 1;
mapping
.find(|m| m.start <= address && upper_bound <= m.end)
.ok_or(CouldNotCreateRemoteView::InvalidAddressRange {
pid: process.pid(),
start: address,
end: upper_bound,
})
.map(|_| RemoteView {
address,
length,
pid: process.pid(),
})
}
#[allow(clippy::module_name_repetitions)]
pub struct AllocatedBuffer {
address: usize,
length: usize,
pid: Pid,
}
impl Buffer for AllocatedBuffer {
fn address(&self) -> usize {
self.address
}
fn length(&self) -> usize {
self.length
}
fn pid(&self) -> Pid {
self.pid
}
}
impl AllocatedBuffer {
pub fn deallocate<E>(self, ctrl: &mut TargetController<E>) -> Result<(), CouldNotDeallocate>
where
E: Executing,
{
let ret = syscall::munmap(ctrl, self.address as u64, self.length as u64)?;
if ret == 0 {
Ok(())
} else {
Err(CouldNotDeallocate::MunmapFailed {
pid: ctrl.process().pid(),
source: IOError::from_raw_os_error(ret as i32),
})
}
}
}
#[allow(clippy::module_name_repetitions)]
pub struct BufferBuilder {
address: Option<usize>,
length: usize,
permissions: i32,
flags: i32,
}
impl BufferBuilder {
#[must_use]
pub const fn new(length: usize) -> Self {
Self {
address: None,
length,
permissions: 0,
flags: MAP_ANONYMOUS,
}
}
#[must_use]
pub const fn at_address(self, address: usize) -> Self {
Self {
address: Some(address),
length: self.length,
permissions: self.permissions,
flags: self.flags,
}
}
#[must_use]
pub const fn readable(self) -> Self {
Self {
address: self.address,
length: self.length,
permissions: self.permissions | PROT_READ,
flags: self.flags,
}
}
#[must_use]
pub const fn writable(self) -> Self {
Self {
address: self.address,
length: self.length,
permissions: self.permissions | PROT_WRITE,
flags: self.flags,
}
}
#[must_use]
pub const fn executable(self) -> Self {
Self {
address: self.address,
length: self.length,
permissions: self.permissions | PROT_EXEC,
flags: self.flags,
}
}
#[must_use]
pub const fn private(self) -> Self {
Self {
address: self.address,
length: self.length,
permissions: self.permissions,
flags: (self.flags | MAP_PRIVATE) & !MAP_SHARED,
}
}
#[must_use]
pub const fn shared(self) -> Self {
Self {
address: self.address,
length: self.length,
permissions: self.permissions,
flags: (self.flags | MAP_SHARED) & !MAP_PRIVATE,
}
}
#[must_use]
pub const fn fixed(self) -> Self {
Self {
address: self.address,
length: self.length,
permissions: self.permissions,
flags: self.flags | MAP_FIXED,
}
}
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_possible_wrap)]
pub fn build<E>(
self,
ctrl: &mut TargetController<E>,
) -> Result<AllocatedBuffer, CouldNotAllocate>
where
E: Executing,
{
let pid = ctrl.process().pid();
let address = self.address.unwrap_or(0);
let length = self.length;
let ret = syscall::mmap(
ctrl,
address as u64,
length as u64,
self.permissions as u64,
self.flags as u64,
-1_i64 as u64,
0,
)? as i64;
if -4096 < ret && ret < 0 {
let errno = -ret as i32;
Err(CouldNotAllocate::MmapFailed {
pid,
source: IOError::from_raw_os_error(errno),
})
} else {
let address = ret as usize;
Ok(AllocatedBuffer {
address,
length,
pid,
})
}
}
}
#[allow(clippy::module_name_repetitions)]
pub fn allocate_buffer<E>(
ctrl: &mut TargetController<E>,
length: usize,
permissions: i32,
) -> Result<AllocatedBuffer, CouldNotAllocate>
where
E: Executing,
{
let mut builder = BufferBuilder::new(length).private();
builder.permissions = permissions;
builder.build(ctrl)
}
fn common_buffer_read<B>(remote_buffer: &B, offset: &mut usize, buf: &mut [u8]) -> IOResult<usize>
where
B: Buffer,
{
let buffer_len = buf.len();
let local_iov = IoSliceMut::new(buf);
let remote_iov = RemoteIoVec {
base: remote_buffer.address() + *offset,
len: buffer_len,
};
let count = process_vm_readv(remote_buffer.pid().into(), &mut [local_iov], &[remote_iov])?;
*offset += count;
Ok(count)
}
fn common_buffer_write<B>(remote_buffer: &B, offset: &mut usize, buf: &[u8]) -> IOResult<usize>
where
B: Buffer,
{
let buffer_len = buf.len();
let local_iov = IoSlice::new(buf);
let remote_iov = RemoteIoVec {
base: remote_buffer.address() + *offset,
len: buffer_len,
};
let count = process_vm_writev(remote_buffer.pid().into(), &[local_iov], &[remote_iov])?;
*offset += count;
Ok(count)
}
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_possible_wrap)]
fn common_buffer_seek<B>(buffer: &B, offset: &mut usize, pos: SeekFrom) -> IOResult<u64>
where
B: Buffer,
{
let new_offset = match pos {
SeekFrom::Start(off) => off as i64,
SeekFrom::End(off) => buffer.length() as i64 + off,
SeekFrom::Current(off) => *offset as i64 + off,
};
if new_offset < 0 || (buffer.length() as i64) < new_offset {
Err(ErrorKind::InvalidData.into())
} else {
*offset = new_offset as usize;
Ok(*offset as u64)
}
}
pub struct UnsafeReader<'buffer, B>
where
B: Buffer,
{
offset: usize,
buffer: &'buffer B,
}
impl<'buffer, B> Read for UnsafeReader<'buffer, B>
where
B: Buffer,
{
fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
common_buffer_read(self.buffer, &mut self.offset, buf)
}
}
impl<'buffer, B> Seek for UnsafeReader<'buffer, B>
where
B: Buffer,
{
fn seek(&mut self, pos: SeekFrom) -> IOResult<u64> {
common_buffer_seek(self.buffer, &mut self.offset, pos)
}
}
impl<'buffer, B> UnsafeReader<'buffer, B>
where
B: Buffer,
{
pub const unsafe fn new(buf: &'buffer B) -> Self {
UnsafeReader {
offset: 0,
buffer: buf,
}
}
}
pub struct UnsafeWriter<'buffer, B>
where
B: Buffer,
{
offset: usize,
buffer: &'buffer mut B,
}
impl<'buffer, B> Write for UnsafeWriter<'buffer, B>
where
B: Buffer,
{
fn write(&mut self, buf: &[u8]) -> IOResult<usize> {
common_buffer_write(self.buffer, &mut self.offset, buf)
}
fn flush(&mut self) -> IOResult<()> {
Ok(())
}
}
impl<'buffer, B> Seek for UnsafeWriter<'buffer, B>
where
B: Buffer,
{
fn seek(&mut self, pos: SeekFrom) -> IOResult<u64> {
common_buffer_seek(self.buffer, &mut self.offset, pos)
}
}
impl<'buffer, B> UnsafeWriter<'buffer, B>
where
B: Buffer,
{
pub unsafe fn new(buf: &'buffer mut B) -> Self {
UnsafeWriter {
offset: 0,
buffer: buf,
}
}
}
pub struct AsyncReader<'buffer, B>
where
B: Buffer,
{
offset: usize,
saved_permissions: Permissions,
buffer: &'buffer B,
}
impl<'buffer, B> Read for AsyncReader<'buffer, B>
where
B: Buffer,
{
fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
common_buffer_read(self.buffer, &mut self.offset, buf)
}
}
impl<'buffer, B> Seek for AsyncReader<'buffer, B>
where
B: Buffer,
{
fn seek(&mut self, pos: SeekFrom) -> IOResult<u64> {
common_buffer_seek(self.buffer, &mut self.offset, pos)
}
}
impl<'buffer, B> AsyncReader<'buffer, B>
where
B: Buffer,
{
pub fn try_new<E>(
ctrl: &mut TargetController<E>,
buf: &'buffer B,
) -> Result<Self, CouldNotCreateAsyncReaderWriter>
where
E: Executing,
{
let mem = memory_mapping(ctrl.process())?;
let buf_map = mem
.find(|m| m.start <= buf.address() && buf.address() + buf.length() <= m.end)
.ok_or(CouldNotCreateAsyncReaderWriter::InvalidAddressRange {
pid: ctrl.process().pid(),
start: buf.address(),
end: buf.address() + buf.length(),
})?;
let matcher = PermissionMatcher {
readable: None,
writable: Some(false),
executable: None,
private: None,
};
if buf_map.permissions.matches(&matcher) {
Ok(AsyncReader {
offset: 0,
saved_permissions: buf_map.permissions.clone(),
buffer: buf,
})
} else {
Err(CouldNotCreateAsyncReaderWriter::PermissionsMismatch {
expected: matcher,
got: buf_map.permissions.clone(),
})
}
}
#[allow(clippy::cast_sign_loss)]
pub fn new<E>(
ctrl: &mut TargetController<E>,
buf: &'buffer B,
) -> Result<Self, CouldNotRestorePermissions>
where
E: Executing,
{
let pid = ctrl.process().pid();
let mem = memory_mapping(ctrl.process())?;
let buf_map = mem
.find(|m| m.start <= buf.address() && buf.address() + buf.length() <= m.end)
.ok_or(CouldNotRestorePermissions::InvalidAddressRange {
pid,
start: buf.address(),
end: buf.address() + buf.length(),
})?;
if buf_map.permissions.writable {
let perm = buf_map.permissions.to_i32() & !PROT_WRITE;
let length = buf_map.end - buf_map.start;
let rax = syscall::mprotect(ctrl, buf_map.start as u64, length as u64, perm as u64)?;
if rax != 0 {
let mut permissions = buf_map.permissions.clone();
permissions.writable = false;
let mprotect_error = match IOError::from_raw_os_error(rax as i32).kind() {
ErrorKind::OutOfMemory => MprotectError::TooManyMappings,
ErrorKind::InvalidData => MprotectError::GrowsUpAndDown { perm: permissions },
error => unreachable!("unexpected error: {}", error),
};
return Err(CouldNotRestorePermissions::MprotectFailed {
pid,
source: mprotect_error,
});
}
}
Ok(AsyncReader {
offset: 0,
saved_permissions: buf_map.permissions.clone(),
buffer: buf,
})
}
#[allow(clippy::cast_sign_loss)]
pub fn restore_permissions<E>(
self,
ctrl: &mut TargetController<E>,
) -> Result<(), CouldNotRestorePermissions>
where
E: Executing,
{
let perm = self.saved_permissions.to_i32();
let buf = self.buffer;
let pid = ctrl.process().pid();
let mem = memory_mapping(ctrl.process())?;
let buf_map = mem
.find(|m| m.start <= buf.address() && buf.address() + buf.length() <= m.end)
.ok_or(CouldNotRestorePermissions::InvalidAddressRange {
pid,
start: buf.address(),
end: buf.address() + buf.length(),
})?;
let length = buf_map.end - buf_map.start;
match syscall::mprotect(ctrl, buf_map.start as u64, length as u64, perm as u64)? {
0 => Ok(()),
rax => {
let mut permissions = buf_map.permissions.clone();
permissions.writable = false;
let mprotect_error = match IOError::from_raw_os_error(rax as i32).kind() {
ErrorKind::OutOfMemory => MprotectError::TooManyMappings,
ErrorKind::InvalidData => MprotectError::GrowsUpAndDown { perm: permissions },
error => unreachable!("unexpected error: {}", error),
};
Err(CouldNotRestorePermissions::MprotectFailed {
pid,
source: mprotect_error,
})
}
}
}
}
pub struct AsyncWriter<'buffer, B>
where
B: Buffer,
{
offset: usize,
buffer: &'buffer mut B,
}
impl<'buffer, B> Write for AsyncWriter<'buffer, B>
where
B: Buffer,
{
fn write(&mut self, buf: &[u8]) -> IOResult<usize> {
common_buffer_write(self.buffer, &mut self.offset, buf)
}
fn flush(&mut self) -> IOResult<()> {
Ok(())
}
}
impl<'buffer, B> Seek for AsyncWriter<'buffer, B>
where
B: Buffer,
{
fn seek(&mut self, pos: SeekFrom) -> IOResult<u64> {
common_buffer_seek(self.buffer, &mut self.offset, pos)
}
}
impl<'buffer, B> AsyncWriter<'buffer, B>
where
B: Buffer,
{
pub fn try_new<E>(
ctrl: &mut TargetController<E>,
buf: &'buffer mut B,
) -> Result<Self, CouldNotCreateAsyncReaderWriter>
where
E: Executing,
{
let mem = memory_mapping(ctrl.process())?;
let buf_map = mem
.find(|m| m.start <= buf.address() && buf.address() + buf.length() <= m.end)
.ok_or(CouldNotCreateAsyncReaderWriter::InvalidAddressRange {
pid: ctrl.process().pid(),
start: buf.address(),
end: buf.address() + buf.length(),
})?;
let matcher = PermissionMatcher {
readable: Some(false),
writable: Some(false),
executable: Some(false),
private: None,
};
if buf_map.permissions.matches(&matcher) {
Ok(AsyncWriter {
offset: 0,
buffer: buf,
})
} else {
Err(CouldNotCreateAsyncReaderWriter::PermissionsMismatch {
expected: matcher,
got: buf_map.permissions.clone(),
})
}
}
}
pub struct SyncReader<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
buffer: &'buffer B,
ctrl: &'ctrl mut TargetController<E>,
offset: usize,
}
impl<'buffer, 'ctrl, B, E> Read for SyncReader<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
common_buffer_read(self.buffer, &mut self.offset, buf)
}
}
impl<'buffer, 'ctrl, B, E> Seek for SyncReader<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
fn seek(&mut self, pos: SeekFrom) -> IOResult<u64> {
common_buffer_seek(self.buffer, &mut self.offset, pos)
}
}
impl<'buffer, 'ctrl, B, E> SyncReader<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
pub fn new(
ctrl: &'ctrl mut TargetController<E>,
buffer: &'buffer B,
) -> Result<Self, CouldNotCreateSyncReaderWriter> {
let mem = memory_mapping(ctrl.process())?;
mem.find(|m| m.start <= buffer.address() && buffer.address() + buffer.length() <= m.end)
.ok_or(CouldNotCreateSyncReaderWriter::InvalidAddressRange {
pid: ctrl.process().pid(),
start: buffer.address(),
end: buffer.address() + buffer.length(),
})?;
Ok(SyncReader {
buffer,
ctrl,
offset: 0,
})
}
pub fn controller(&mut self) -> &mut TargetController<E> {
self.ctrl
}
}
pub struct SyncWriter<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
buffer: &'buffer mut B,
ctrl: &'ctrl mut TargetController<E>,
offset: usize,
}
impl<'buffer, 'ctrl, B, E> Write for SyncWriter<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
fn write(&mut self, buf: &[u8]) -> IOResult<usize> {
common_buffer_write(self.buffer, &mut self.offset, buf)
}
fn flush(&mut self) -> IOResult<()> {
Ok(())
}
}
impl<'buffer, 'ctrl, B, E> Seek for SyncWriter<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
fn seek(&mut self, pos: SeekFrom) -> IOResult<u64> {
common_buffer_seek(self.buffer, &mut self.offset, pos)
}
}
impl<'buffer, 'ctrl, B, E> SyncWriter<'buffer, 'ctrl, B, E>
where
B: Buffer,
E: Executing,
{
pub fn new(
ctrl: &'ctrl mut TargetController<E>,
buffer: &'buffer mut B,
) -> Result<Self, CouldNotCreateSyncReaderWriter> {
let mem = memory_mapping(ctrl.process())?;
mem.find(|m| m.start <= buffer.address() && buffer.address() + buffer.length() <= m.end)
.ok_or(CouldNotCreateSyncReaderWriter::InvalidAddressRange {
pid: ctrl.process().pid(),
start: buffer.address(),
end: buffer.address() + buffer.length(),
})?;
Ok(SyncWriter {
buffer,
ctrl,
offset: 0,
})
}
pub fn controller(&mut self) -> &mut TargetController<E> {
self.ctrl
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use anyhow::Error as AnyError;
use crate::mapping::Permissions;
use crate::process::spawn_process;
use super::*;
#[test]
fn unsafe_read_some_code() -> Result<(), AnyError> {
let mut path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path_buf.push("resources/test/say_hello_no_pie");
let process = spawn_process::<_, _, &str>(path_buf, vec![])?;
let ctrl = process.wait()?.assume_alive()?;
let buffer = remote_view(ctrl.process(), 0x0040_1126, 30)?;
unsafe {
let mut reader = UnsafeReader::new(&buffer);
let mut buf: [u8; 5] = Default::default();
let count = reader.read(&mut buf)?;
assert_eq!(count, 5);
assert_eq!(buf, [0x55, 0x48, 0x89, 0xe5, 0x48]);
}
let process = ctrl.resume()?;
let state = process.wait()?;
assert!(state.has_exited(), "{}", state.reason());
Ok(())
}
#[test]
fn async_read_some_code() -> Result<(), AnyError> {
let mut path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path_buf.push("resources/test/say_hello_no_pie");
let process = spawn_process::<_, _, &str>(path_buf, vec![])?;
let mut ctrl = process.wait()?.assume_alive()?;
let buffer = remote_view(ctrl.process(), 0x0040_1126, 50)?;
let mut reader = AsyncReader::try_new(&mut ctrl, &buffer)?;
let mut buf: [u8; 5] = Default::default();
let count = reader.read(&mut buf)?;
assert_eq!(count, 5);
assert_eq!(buf, [0x55, 0x48, 0x89, 0xe5, 0x48]);
let process = ctrl.resume()?;
let state = process.wait()?;
assert!(state.has_exited(), "{}", state.reason());
Ok(())
}
#[test]
fn buffer_allocation() -> Result<(), AnyError> {
let process = spawn_process("/bin/ls", ["-l"])?;
let mut ctrl = process.wait()?.assume_alive()?;
let buffer = allocate_buffer(&mut ctrl, 1000, PROT_READ)?;
assert_ne!(buffer.address(), 0);
let mapping = memory_mapping(ctrl.process()).unwrap();
let opt = mapping.find(|m| m.start == buffer.address());
assert!(opt.is_some());
let m = opt.unwrap();
assert_eq!(
m.permissions,
Permissions {
readable: true,
writable: false,
executable: false,
private: true
}
);
assert!(m.is_anonymous());
let process = ctrl.resume()?;
let state = process.wait()?;
assert!(state.has_exited(), "{}", state.reason());
Ok(())
}
#[test]
fn buffer_deallocation() -> Result<(), AnyError> {
let process = spawn_process("/bin/ls", ["-l"])?;
let mut ctrl = process.wait()?.assume_alive()?;
let buffer = allocate_buffer(&mut ctrl, 1000, PROT_READ)?;
buffer.deallocate(&mut ctrl)?;
let process = ctrl.resume()?;
let state = process.wait()?;
assert!(state.has_exited(), "{}", state.reason());
Ok(())
}
}