Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support reading from 32-bit processes on 64-bit hosts and vice versa #1

Merged
merged 2 commits into from
Apr 26, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/data_member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::{CopyAddress, Memory, ProcessHandle, PutAddress};
pub struct DataMember<T> {
offsets: Vec<usize>,
process: ProcessHandle,
arch: usize,
_phantom: std::marker::PhantomData<*mut T>,
}

Expand All @@ -53,6 +54,7 @@ impl<T: Sized + Copy> DataMember<T> {
Self {
offsets: Vec::new(),
process: handle,
arch: std::mem::size_of::<usize>() * 8,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only place where arch is used, you divide it by 8. I'd recommend not multiplying by 8 here so that you don't have an extra divide every time you try to call get_offset().

_phantom: std::marker::PhantomData,
}
}
Expand All @@ -69,9 +71,21 @@ impl<T: Sized + Copy> DataMember<T> {
Self {
offsets,
process: handle,
arch: std::mem::size_of::<usize>() * 8,
_phantom: std::marker::PhantomData,
}
}

/// Sets the architecture of the DataMember.
///
/// This can be used for reading memory offsets of programs that are of
/// different architectures to the host program.
/// This defaults to the architecture of the host program.
pub fn set_arch(mut self, arch: usize) -> Self {
assert!(arch == 32 || arch == 64);
self.arch = arch;
self
}
}

impl<T: Sized + Copy> Memory<T> for DataMember<T> {
Expand All @@ -80,11 +94,11 @@ impl<T: Sized + Copy> Memory<T> for DataMember<T> {
}

fn get_offset(&self) -> std::io::Result<usize> {
self.process.get_offset(&self.offsets)
self.process.get_offset(&self.offsets, &self.arch)
}

fn read(&self) -> std::io::Result<T> {
let offset = self.process.get_offset(&self.offsets)?;
let offset = self.process.get_offset(&self.offsets, &self.arch)?;
// This can't be [0_u8;size_of::<T>()] because no const generics.
// It will be freed at the end of the function because no references are held to it.
let mut buffer = vec![0_u8; std::mem::size_of::<T>()];
Expand All @@ -94,7 +108,7 @@ impl<T: Sized + Copy> Memory<T> for DataMember<T> {

fn write(&self, value: &T) -> std::io::Result<()> {
use std::slice;
let offset = self.process.get_offset(&self.offsets)?;
let offset = self.process.get_offset(&self.offsets, &self.arch)?;
let buffer: &[u8] =
unsafe { slice::from_raw_parts(value as *const _ as _, std::mem::size_of::<T>()) };
self.process.put_address(offset, &buffer)
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
#![deny(unused)]
#![deny(clippy::pedantic)]

use std::convert::TryInto;

mod data_member;
mod local_member;

Expand All @@ -73,21 +75,22 @@ mod platform;
/// type into a buffer.
pub trait CopyAddress {
/// Copy an address into user-defined buffer.
fn copy_address(&self, addr: usize, buf: &mut [u8]) -> std::io::Result<()>;
fn copy_address(&self, addr: usize, buf: &mut Vec<u8>) -> std::io::Result<()>;

/// Get the actual memory location from a set of offsets.
///
/// If [`copy_address`] is already defined, then we can provide a standard implementation that
/// will work across all operating systems.
fn get_offset(&self, offsets: &[usize]) -> std::io::Result<usize> {
fn get_offset(&self, offsets: &[usize], arch: &usize) -> std::io::Result<usize> {
// Look ma! No unsafes!
let mut offset: usize = 0;
let noffsets: usize = offsets.len();
for next_offset in offsets.iter().take(noffsets - 1) {
offset += next_offset;
let mut copy: [u8; std::mem::size_of::<usize>()] = [0; std::mem::size_of::<usize>()];
let mut copy: Vec<u8> = vec![0; arch / 8];
self.copy_address(offset, &mut copy)?;
offset = usize::from_ne_bytes(copy);
copy.resize(std::mem::size_of::<usize>(), 0);
offset = usize::from_ne_bytes(copy.as_slice().try_into().unwrap());
}

offset += offsets[noffsets - 1];
Expand Down
2 changes: 1 addition & 1 deletion src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl TryIntoProcessHandle for Child {
}

impl CopyAddress for ProcessHandle {
fn copy_address(&self, addr: usize, buf: &mut [u8]) -> std::io::Result<()> {
fn copy_address(&self, addr: usize, buf: &mut Vec<u8>) -> std::io::Result<()> {
let local_iov = iovec {
iov_base: buf.as_mut_ptr() as *mut c_void,
iov_len: buf.len(),
Expand Down
4 changes: 2 additions & 2 deletions src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ impl PutAddress for ProcessHandle {
/// We use `vm_read_overwrite` instead of `vm_read` because it can handle non-aligned reads and
/// won't read an entire page.
impl CopyAddress for ProcessHandle {
fn copy_address(&self, addr: usize, buf: &mut [u8]) -> std::io::Result<()> {
fn copy_address(&self, addr: usize, buf: &mut Vec<u8>) -> std::io::Result<()> {
let mut read_len: u64 = 0;
let result = unsafe {
mach::vm::mach_vm_read_overwrite(
*self,
addr as _,
buf.len() as _,
buf.as_ptr() as _,
buf.as_mut_ptr() as _,
&mut read_len,
)
};
Expand Down
4 changes: 2 additions & 2 deletions src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl TryIntoProcessHandle for Child {

/// Use `ReadProcessMemory` to read memory from another process on Windows.
impl CopyAddress for ProcessHandle {
fn copy_address(&self, addr: usize, buf: &mut [u8]) -> std::io::Result<()> {
fn copy_address(&self, addr: usize, buf: &mut Vec<u8>) -> std::io::Result<()> {
if buf.is_empty() {
return Ok(());
}
Expand All @@ -64,7 +64,7 @@ impl CopyAddress for ProcessHandle {
*self,
addr as minwindef::LPVOID,
buf.as_mut_ptr() as minwindef::LPVOID,
mem::size_of_val(buf) as winapi::shared::basetsd::SIZE_T,
buf.len() as winapi::shared::basetsd::SIZE_T,
ptr::null_mut(),
)
} == winapi::shared::minwindef::FALSE
Expand Down