#reverse-engineering #debugging #process-id #linux-macos #macos

vmemory

Read and write the memory in other processes for Windows and Unix-based systems regardless of memory page protections

8 releases

0.1.8 Dec 8, 2021
0.1.7 Dec 7, 2021
0.1.0 Nov 22, 2021

#344 in Operating systems

29 downloads per month

MIT license

43KB
707 lines

vmemory

Rust library for reading/writing memory in other processes for Windows, macOS, Linux, and in the future potentially, BSD variants. This will write to memory regardless of memory page protections.

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());
}

Dependencies

~2MB
~42K SLoC