Skip to content

zaarov/MemoryBox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Memory Box

Small, pragmatic Rust library for local process memory manipulation — mainly useful when developing DLL mods, game trainers, or memory-based hooks on Windows.

Status: Experimental / Alpha — API is still evolving. Platform: Windows (can work at linux if your game or application are running through proton, wine, bottles, etc)


Installation

memory_box = { git = "https://github.com/zaarov/MemoryBox.git" }

Key concepts & types

MemoryError

An enum describing low-level failure modes: NullPointer, InvalidLength, VirtualProtectFailed, ReadFailed, WriteFailed, OutOfBounds. All public APIs return Result<..., MemoryError>.

ModuleContext

Represents the current module (the process / DLL where the code runs):

  • ModuleContext::current() -> Result<ModuleContext, MemoryError> — queries Windows (GetModuleHandleW + GetModuleInformation) to obtain module base + size.

  • pattern_scan(&self, pattern: &[Option<u8>]) -> Result<LocalPtr, MemoryError> — reads the module via ReadProcessMemory into a Vec<u8> and searches for the pattern with wildcards.

LocalPtr

A small wrapper around a raw address in the current process (usize). Methods return Result and are crash-safe:

  • from_addr(address: usize) -> LocalPtr — construct from address.

  • offset(&self, off: isize) -> Result<LocalPtr, MemoryError> — add/subtract offset with checked arithmetic.

  • read_bytes(&self, len: usize) -> Result<Vec<u8>, MemoryError> — read bytes using ReadProcessMemory. Returns Err instead of causing an access violation when memory is unreadable.

  • deref(&self) -> Result<LocalPtr, MemoryError> — read a pointer-sized value using read_bytes (handles both 32- and 64-bit).

  • rip_relative(&self, offset_offset: isize, instruction_len: isize) -> Result<LocalPtr, MemoryError> — resolve RIP-relative addressing (reads a 32-bit displacement and computes target).

  • write_bytes(&self, data: &[u8]) -> Result<(), MemoryError> — direct local write (kept for internal uses).

  • write_bytes_protected(&self, data: &[u8]) -> Result<(), MemoryError> — change page protections and write safely via WriteProcessMemory, then FlushInstructionCache.

  • chain(self) -> LocalPtrChain — start a fluent pointer-chasing chain.

LocalPtrChain

Fluent-style API for pointer chasing; methods return Result:

  • offset(self, off: isize) -> Result<Self, MemoryError>

  • deref(self) -> Result<Self, MemoryError>

  • finish(self) -> LocalPtr


Pattern format

Patterns are &[Option<u8>]. Example:

  • Some(0x48) means the byte must match 0x48.

  • None is a wildcard (??).

Example pattern used in the library:

// pattern: Some(0xDE), Some(0xAD), None, Some(0xBE) means "DE AD ?? BE"
const GAME_MANAGER_IMP: [Option<u8>; 17] = [
    Some(0x48),
    Some(0x8B), 
    Some(0x05),
    None, 
    None, 
    None, 
    None,
    Some(0x48), 
    Some(0x8B), 
    Some(0x58), 
    Some(0x38),
    Some(0x48), 
    Some(0x85), 
    Some(0xDB), 
    Some(0x74),
    None, 
    Some(0xF6),
];

Examples

Resolve a pointer chain and read an f32 (safe)

use memory_box::{ModuleContext, MemoryError};

fn read_health() -> Result<f32, MemoryError> {
    let ptr = ModuleContext::current()?
        .pattern_scan(&GAME_MANAGER_IMP)?
        .rip_relative(3, 7)?
        .deref()?
        .chain()
        .offset(0x18)?
        .deref()?
        .offset(0x310)?
        .deref()?
        .offset(0xD8)?
        .deref()?
        .offset(0x1C8)?
        .offset(0x60C)?
        .finish();

    let bytes = ptr.read_bytes(4)?;
    let value = f32::from_le_bytes(bytes.try_into().map_err(|_| MemoryError::ReadFailed)?);
    Ok(value)
}

Read a primitive helper

fn read_addr_as_i32(addr: LocalPtr) -> Result<i32, MemoryError> {
    let bytes = addr.read_bytes(4)?;
    Ok(i32::from_le_bytes(bytes.try_into().map_err(|_| MemoryError::ReadFailed)?))
}

Protected write (patch code/data)

let target: LocalPtr = LocalPtr::from_addr(0x7FF6_1234_0000);
let new_bytes: [u8; 5] = [0x90, 0x90, 0x90, 0x90, 0x90];
target.write_bytes_protected(&new_bytes)?;

write_bytes_protected will:

  • call VirtualProtect to set PAGE_READWRITE,

  • write with WriteProcessMemory,

  • restore the old protection,

  • call FlushInstructionCache.

About

A small, focused Rust library for local memory manipulation intended to help the development of DLL mods and hooks.

Resources

Stars

Watchers

Forks

Contributors

Languages