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)
memory_box = { git = "https://github.com/zaarov/MemoryBox.git" }An enum describing low-level failure modes: NullPointer, InvalidLength, VirtualProtectFailed, ReadFailed, WriteFailed, OutOfBounds. All public APIs return Result<..., MemoryError>.
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 viaReadProcessMemoryinto aVec<u8>and searches for the pattern with wildcards.
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 usingReadProcessMemory. ReturnsErrinstead of causing an access violation when memory is unreadable.deref(&self) -> Result<LocalPtr, MemoryError>— read a pointer-sized value usingread_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 viaWriteProcessMemory, thenFlushInstructionCache.chain(self) -> LocalPtrChain— start a fluent pointer-chasing chain.
Fluent-style API for pointer chasing; methods return Result:
offset(self, off: isize) -> Result<Self, MemoryError>deref(self) -> Result<Self, MemoryError>finish(self) -> LocalPtr
Patterns are &[Option<u8>]. Example:
Some(0x48)means the byte must match 0x48.Noneis a wildcard (??).
Example pattern used in the library:
// pattern: Some(0xDE), Some(0xAD), None, Some(0xBE) means "DE AD ?? BE"constGAME_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),];use memory_box::{ModuleContext,MemoryError};fnread_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)}fnread_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)?))}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
VirtualProtectto setPAGE_READWRITE,write with
WriteProcessMemory,restore the old protection,
call
FlushInstructionCache.