Skip to content

Repository files navigation

GitHub Actions Build statusCirrus CI Build statuscrates.io

A crate to read memory from another process. Code originally taken from the rbspy project. This crate has now returned home to the rbspy GitHub organization. :)

Example

This example re-executes itself as a child process in order to have a separate process to use for demonstration purposes. If you need to read memory from a process that you are spawning, your usage should look very similar to this:

use std::convert::TryInto;use std::env;use std::io::{self,BufReader,BufRead,Read,Result};use std::process::{Command,Stdio};use read_process_memory::{Pid,ProcessHandle,CopyAddress,
copy_address,};fnmain() -> Result<()>{if env::args_os().len() > 1{// We are the child.returnin_child();}// Run this executable again so we have a child process to read.letmut child = Command::new(env::current_exe()?).stdin(Stdio::piped()).stdout(Stdio::piped()).arg("child").spawn()?;// Get a ProcessHandle to work with.let handle:ProcessHandle = (&child).try_into().unwrap();// The child process will print the address to read from on stdout.letmut stdout = BufReader::new(child.stdout.take().unwrap());letmut addr_string = String::new();
stdout.read_line(&mut addr_string)?;let address = usize::from_str_radix(addr_string.trim(),16).unwrap();// Try to read 10 bytes from that addresslet bytes = copy_address(address,10,&handle)?;println!("Read: {:?}", bytes);// Tell the child to exit by closing its stdin.drop(child.stdin.take());// And wait for it to exit.
child.wait()?;Ok(())}fnin_child() -> Result<()>{// Allocate a 10-byte Vec for the parent to read.let readable_bytes:Vec<u8> = vec![0xc0,0x72,0x80,0x79,0xeb,0xf1,0xbc,0x87,0x06,0x14,];// Print the address of the Vec to stdout so the parent can find it.println!("{:x}", readable_bytes.as_ptr()asusize);// Now wait to exit until the parent closes our stdin, to give// it time to read the memory.letmut buf = Vec::new();// We don't care if this succeeds.drop(io::stdin().read_to_end(&mut buf));Ok(())}

How it works

Here's a summary, with some C pseudocode, of how the read-process-memory crate works under the hood on each of the platforms it supports. The three inputs are:

  • PID: the process ID to read from
  • LENGTH: how much memory to read
  • ADDRESS: the address to read from

Linux:

Uses process_vm_readv

void*TARGET= (void*) 0x123412341324;
structioveclocal;
local.iov_base=calloc(LENGTH, sizeof(char));
local.iov_len=LENGTH;
structiovecremote;
remote[0].iov_base=TARGET;
remote[0].iov_len=LENGTH;
process_vm_readv(PID, local, 2, remote, 1, 0);

Mac OS:

Uses vm_read_overwrite

mach_port_name_ttask;
task_for_pid(mach_task_self(), PID, &task)
vm_size_tread_len=LENGTH;
charresult[LENGTH];
vm_read_overwrite(task, TARGET, LENGTH, &result, &read_len)

FreeBSD:

Uses ptrace. This one stops the process to read from it.

// attachintwait_status=0;
attach_status=ptrace(PT_ATTACH, PID, null, 0);
waitpid(PID, &wait_status, 0);
WIFSTOPPED(wait_status)
charresult[LENGTH];
desc=PtraceIoDesc {
piod_op: PIOD_READ_D,
piod_offs: TARGET;
piod_addr: &result;
piod_len: LENGTH,
};
// read dataptrace(PT_IO, PID, &desc, 0);
// detachptrace(PT_DETACH, PID, null, 0);

Windows:

Uses ReadProcessMemory:

charresult[LENGTH];
ReadProcessMemory(PID, ADDRESS, &result, LENGTH, null);

About

Read memory from another process

Topics

Resources

Stars

132 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages