Cross-platform library for reading/writing memory in other processes for Rust

Overview

vmemory

Rust library for reading/writing memory in other processes for Windows, macOS, Linux, and in the future potentially, BSD variants.

Rationale

After having to write software working with memory reading/writing in processes on multiple operating systems, I found that there are some inconveniences for my purpose. While other cross-platform process memory reading/writing libraries exist for Rust, they did not all fit my purpose or solve the underlying issues I had with the platform-specific APIs. For example, ptrace(2) on Linux only accepts word-sized reads/writes, if a processor word is 8 bytes, only 8 bytes can be read from the process at a time. You could not for example, read 123 bytes.

Another example is that macOS removes important functionality from ptrace(2) even though it is inherited from the BSD implementation, it removes a lot of functionality such as reading/writing memory via ptrace(2). So instead, code must opt to use mach's vm_write, vm_read_overwrite, and vm_region functions which are entirely undocumented by Apple and as such, may be subject to change.

Examples:

The actual ptrace(2) implementation by Apple which does not implement PT_WRITE/PT_READ functionality: https://opensource.apple.com/source/xnu/xnu-344.21.74/bsd/kern/mach_process.c.auto.html

Then there were of course inconsistencies, while many UNIX-based systems implement a form of procfs (BSD variants, Solaris, AIX) as well as Linux, macOS does not and instead memory mapping information is retrieved via mach vm_region-related functions, or via the vmmap software.

With other Rust libraries I used, there were specific things that just were not solved, for example, the issue of granularity with ptrace(2) is the same if they use it as an underlying call. If they use process_vm_writev, then there's no guarantee that page protections such as writing to read-only memory will allow this call to succeed, though this does not seem explicitly documented, there are no indications of the page protection changing to allow this write. Also, https://reviews.llvm.org/D10488.

So this API allows for arbitrary reading/writing memory to other processes regardless of the page protections present, allows for processes to be spawned in a suspended state, and attempts to allow for much more granular reads/writes. In addition, it is easy to retrieve the base address of the first mapping/module for the process.

API

ProcessMemory::new_process(file_path: &str, arguments: &Vec<String>) -> Option<ProcessMemory>

Spawn a new process in a suspended state to be manually resumed via self.resume(), passing the file path of the process to start and the arguments to spawn the process with. Returns an option consisting of the struct to be unwrapped

ProcessMemory::attach_process(pid: u32) -> Option<ProcessMemory>

Attach to a process with the process ID (PID). Returning a structure in an option to be unwrapped, which will allow memory read/write operations

ProcessMemory::write_memory(&self, _address: usize, data: &Vec<u8>, offset: bool)

Write memory to the process. The memory to be written is the memory in the data parameter, at the location of _address in the remote process. The offset boolean will specify whether the value of _address is an offset relative to the first module/mapping loaded into the process (true), or if it is a direct address value to be written (false)

Example, the first module is loaded at 0x00400000

offset is set to true, and _address = 5

Memory would be written at 0x00400005

ProcessMemory::read_memory(&self, _address: usize, size: usize, offset: bool) -> Vec<u8>

Read memory from the process at the location of _address, and read n bytes according to size. The rules off the offset parameter are the same as specified in ProcessMemory::write_memory()

ProcessMemory::resume(&self)

Resume the process from a suspended state (SIGCONT on Linux/macOS. ResumeThread on the first thread from CreateProcess on Windows). This should generally only be used for ptrace(2) sessions on Linux, posix_spawn(2) from a suspended state on macOS, or CreateProcess on Windows. Essentially all ProcessMemory::new_process() calls will require this function to be called

ProcessMemory::base(&self)

Retrieve the base address for the first mapping/module loaded into the process

Examples

Example 1

Using new_process

use vmemory::*;

fn main() {
    //
    // Spawn a new process in a suspended state with no arguments
    //
    let test = ProcessMemory::new_process(r"C:\TEST.EXE", &vec!["".to_string()]).unwrap();

    //
    // Write memory to the process at (base address + 0xA)
    // Writing 4 bytes at this location, each byte = 9
    //
    test.write_memory(0xA, &vec![9, 9, 9, 9], true);

    //
    // Read memory to confirm the write was registered to the process, as well as a few additional bytes that
    // were not written
    //
    let vmem = test.read_memory(0xA, 10, true);

    for v in vmem {
        print!("{:02X} ", v);
    }

    //
    // Get the base address of the first module in the process, and print it out
    //
    println!("\nbase: {:08X}", test.base());

    //
    // Resume the process
    //
    test.resume();
}

Example 2

Here we use attach_process instead of new_process.

Take note of the offset boolean (third argument to write_memory and read_memory) in this example. Here the direct address passed to write_memory and the offset passed to read_memory refer to the same location in the process's memory.

use vmemory::*;

fn main() {

    //
    // Attach to a process with a process ID (PID) of 3145
    // Immediately resume from the ptrace attachment
    //
    let mut test = ProcessMemory::attach_process(3145).unwrap();
    test.resume();

    //
    // Write 5 bytes at the direct address (no offset) 0x5616B07DB000
    //
    let write_test: Vec<u8> = vec![7, 7, 9, 9, 9];
    test.write_memory(0x5616B07DB000, &write_test, false);

    //
    // Read 5 bytes from the offset (0) relative to the base address of the first mapping/module in the process
    //
    let vmem = test.read_memory(0, 5, true);

    for v in &vmem {
        print!("{:02X} ", v);
    }

    //
    // Print out the base address of the process
    //
    println!("\nbase: {:08X}", test.base());
}
You might also like...
An attempt to implement equivalent of C++ "P1478R1: Byte-wise atomic memcpy" in Rust

atomic-memcpy Byte-wise atomic memcpy. This is an attempt to implement equivalent of C++ "P1478R1: Byte-wise atomic memcpy" in Rust. This is expected

Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

A rust library that makes reading and writing memory of the Dolphin emulator easier.

dolphin-memory-rs A crate for reading from and writing to the emulated memory of Dolphin in rust. A lot of internals here are directly based on aldela

Rust library for reading/writing numbers in big-endian and little-endian.

byteorder This crate provides convenience methods for encoding and decoding numbers in either big-endian or little-endian order. Dual-licensed under M

Pure rust library for reading / writing DNG files providing access to the raw data in a zero-copy friendly way.

DNG-rs   A pure rust library for reading / writing DNG files providing access to the raw data in a zero-copy friendly way. Also containing code for re

Cross-platform, cross-browser, cross-search-engine duckduckgo-like bangs

localbang Cross-platform, cross-browser, cross-search-engine duckduckgo-like bangs What are "bangs"?? Bangs are a way to define where to search inside

Convenience library for reading and writing compressed files/streams

compress_io Convenience library for reading and writing compressed files/streams The aim of compress_io is to make it simple for an application to sup

This library provides a data view for reading and writing data in a byte array.

Docs This library provides a data view for reading and writing data in a byte array. This library requires feature(generic_const_exprs) to be enabled.

An opinionated, monolithic template for Bevy with cross-platform CI/CD, native + WASM launchers, and managed cross-platform deployment.
An opinionated, monolithic template for Bevy with cross-platform CI/CD, native + WASM launchers, and managed cross-platform deployment.

🕊️ Bevy Shell - Template An opinionated, monolithic template for Bevy with cross-platform CI/CD, native + WASM launchers, and managed cross-platform

Tar file reading/writing for Rust

tar-rs Documentation A tar archive reading/writing library for Rust. # Cargo.toml [dependencies] tar = "0.4" Reading an archive extern crate tar; use

:crab: Small exercises to get you used to reading and writing Rust code!
:crab: Small exercises to get you used to reading and writing Rust code!

rustlings 🦀 ❤️ Greetings and welcome to rustlings. This project contains small exercises to get you used to reading and writing Rust code. This inclu

This project contains small exercises to get you used to reading and writing Rust code
This project contains small exercises to get you used to reading and writing Rust code

rustlings 🦀 ❤️ Greetings and welcome to rustlings. This project contains small exercises to get you used to reading and writing Rust code. This inclu

A rust proc-macro which allows for reading and writing to remote objects through a generated enum

Remote-Obj A rust proc-macro which allows for reading and writing fields/variants of (possibly nested) remote objects by generating a single enum whic

Zero-Copy reading and writing of geospatial data.

GeoZero Zero-Copy reading and writing of geospatial data. GeoZero defines an API for reading geospatial data formats without an intermediate represent

Easy reading and writing of `serde` structs to/from Google Sheets

serde_sheets Read and write structs directly from google sheets using serde and csv Implement serde::Serialize to write and serde::Deserialize to read

ezio offers an easy to use IO API for reading and writing to files and stdio

ezio - a crate for easy IO ezio offers an easy to use IO API for reading and writing to files and stdio. ezio includes utilities for generating random

Thread-safe clone-on-write container for fast concurrent writing and reading.

sync_cow Thread-safe clone-on-write container for fast concurrent writing and reading. SyncCow is a container for concurrent writing and reading of da

IDE tools for writing pest grammars, using the Language Server Protocol for Visual Studio Code, Vim and other editors

Pest IDE Tools IDE support for Pest, via the LSP. This repository contains an implementation of the Language Server Protocol in Rust, for the Pest par

A cross-platform and safe Rust API to create and manage memory mappings in the virtual address space of the calling process.

mmap-rs A cross-platform and safe Rust API to create and manage memory mappings in the virtual address space of the calling process. This crate can be

Comments
  • Can't write to process [MacOS]

    Can't write to process [MacOS]

    I'm able to read memory from a process but fail to write to it on MacOS Monterey 12.0.1. I'm using the second example template.

    The program panics at memory_darwin.rs:43:103

    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: 2', /Users/alejandro/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.0/src/memory_darwin.rs:43:103
    

    Specifically at :

    proc_protect(target_task, _address, buffer.len(), VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE).unwrap();
    
      17:        0x10825f56e - vmemory::memory_darwin::write_memory::h357da045cd595797
                                   at /Users/alejandro/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.0/src/memory_darwin.rs:43:5
      18:        0x10825f311 - vmemory::ProcessMemory::write_memory::h653137f08c1e7ebd
                                   at /Users/alejandro/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.0/src/lib.rs:452:13
      19:        0x10825e6d5 - viewMemory::main::h779eba0e797e2b3b
                                   at /Users/alejandro/viewMemory/src/main.rs:23:2
    
    opened by AOx0 3
  • Can't compile on linux

    Can't compile on linux

    use vmemory::ProcessMemory;
    
    fn main() {
    	let mem = ProcessMemory::attach_process(12577).unwrap();
    	let read = mem.read_memory(0x280, 4, true);
    	println!("{:x?}", read);
    }
    
    error[E0425]: cannot find value `nix_pid` in this scope
       --> vmemory-0.1.8/src/lib.rs:554:24
        |
    554 |         ptrace::detach(nix_pid, None).unwrap(); 
        |                        ^^^^^^^ not found in this scope
    
    opened by Wireless4024 1
  • Can't write memory on MacOS.

    Can't write memory on MacOS.

    Hope you are well.

    This issues occurred in my demo application.

    MacOS version: 12.2.1 (21D62)

    Rust version:

    $ rustc --version   
    rustc 1.58.1 (db9d1b20b 2022-01-20)
    

    Read memory is work, write memory panicked:

    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: 1', /Users/miasto/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.8/src/memory_darwin.rs:235:7
    stack backtrace:
       0: rust_begin_unwind
                 at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:498:5
       1: core::panicking::panic_fmt
                 at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/panicking.rs:107:14
       2: core::result::unwrap_failed
                 at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/result.rs:1613:5
       3: core::result::Result<T,E>::unwrap
                 at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/result.rs:1295:23
       4: vmemory::memory_darwin::sub_alloc
                 at /Users/miasto/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.8/src/memory_darwin.rs:230:5
       5: vmemory::memory_darwin::write_memory
                 at /Users/miasto/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.8/src/memory_darwin.rs:53:26
       6: vmemory::ProcessMemory::write_memory
                 at /Users/miasto/.cargo/registry/src/github.com-1ecc6299db9ec823/vmemory-0.1.8/src/lib.rs:461:13
       7: hello::main
                 at ./src/main.rs:20:5
       8: core::ops::function::FnOnce::call_once
                 at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/ops/function.rs:227:5
    
    opened by chenyanchen 1
Owner
Jason Johnson
Jason Johnson
Rust library to interract with memory written in rust

memory-rs Rust library to interract with memory written in rust It comes with: Pattern scanner (Return address for a pattern given). A pattern example

Alex 1 Jan 13, 2022
A comprehensive memory scanning library

scanflow boasts a feature set similar to the likes of CheatEngine, with a simple command line interface. Utilizing memflow, scanflow works in a wide range of situations - from virtual machines, to dedicated DMA hardware.

memflow 38 Dec 30, 2022
bevy_datasize is a library for tracking memory usage in Bevy apps.

bevy_datasize bevy_datasize is a library for tracking memory usage in Bevy apps. bevy_datasize uses the DataSize trait from the datasize crate to esti

Ben Reeves 4 Mar 8, 2022
Memory hacking library for windows

Memory hacking library for windows

Sara Wahib 4 Apr 11, 2022
MiniDump a process in memory with rust

safetydump Rust in-memory MiniDump implementation. Features ntdll!NtGetNextProcess to obtain a handle for the desired ProcessId as opposed to kernel32

null 26 Oct 11, 2022
memory-mapped registers for x86_64 systems

regmap some well-known and known-to-be-good computer architectures, such as the Microchip PIC product line, or many of the AVR processor family, were

iximeow 31 Dec 6, 2022
In-memory, non stateful and session based code sharing application.

interviewer In-memory, non stateful and session based code sharing application. Test it here: interviewer.taras.lol Note: it's deployed to render auto

2pac 7 Aug 16, 2021
A memory efficient syntax tree for language developers

This crate provides a tree structure which always is contiguously stored and manipulated in memory. It provides similar APIs as rowan and is intended to be an efficient replacement for it (read more below).

John-John Tedro 21 Dec 15, 2022
Compile-time checked Builder pattern derive macro with zero-memory overhead

Compile-time checked Builder pattern derive macro with zero-memory overhead This is very much a work-in-progress. PRs welcome to bring this to product

Esteban Kuber 214 Dec 29, 2022
Benchmarking manual implementation of memcpy in Rust

Manual memcpy Benchmark Benchmarks that compare copying data between two Vec<u8>s using std::slice::copy_from_slice and a loop that copies one byte at

Adam Bratschi-Kaye 0 Feb 2, 2022