[memhack] DLL Reference - Redacted-Rice/ITB-RedactedRiceExts GitHub Wiki

DLL Reference

The memhack DLL provides low-level memory and process access functions. These functions are the foundation that powers all struct operations, but you typically don't need to call them directly.

Additional to the functions defined here, the DLL also provides a C++ implementation of a [[memory scanner | [memhack] scanner] which can be used real time. This is useful for reverse engineering and also for finding dynamic values run time with no clear pointer chain found via reverse engineering

Prefer Using Structs Instead

For modders: You should use the struct system instead of calling DLL functions directly. This documentation is mainly for understanding how memhack works internally or for advanced use cases.

The struct system provides significant advantages over direct DLL calls:

  • Type safety/using the correct type
  • Automatic validation of the memory to the degree defined by the struct
  • Instant event/hook firing via setters
  • Convinience & Readability - its easier to use and tell whats going on using the structs

When to Use DLL Directly

You might need direct DLL access for:

  • Reverse engineering unknown memory structures
  • Advanced memory scanning or pattern matching
  • Working internal to memhack with struct manager

Safety Warnings

Important: Direct memory access can easily crash the game or corrupt the game state and save file. These should be mostly mitigating using the existing structs and the added game functions for accessing them

Overview

The memhack DLL is exposed at memhack.dll and provides three main interfaces:

  • memhack.dll.memory - Memory read/write operations
  • memhack.dll.process - Process information
  • memhack.dll.scanner - Runtime capable scanner. Separated out into its own page.

Memory Operations

The memory interface provides functions for reading and writing different data types.

Read Functions

Function Return Type Description
readByte(address) int Read 1 byte (0-255)
readInt(address) int Read 4-byte signed integer
readBool(address) bool Read 1-byte boolean
readFloat(address) float Read 4-byte float
readDouble(address) double Read 8-byte double
readPointer(address) int Read 4-byte pointer (32-bit address)
readNullTermString(address, maxLength) string Read null-terminated string
readByteArray(address, length) string Read binary data

Write Functions

Function Description
writeByte(address, value) Write 1 byte
writeInt(address, value) Write 4-byte signed integer
writeBool(address, value) Write 1-byte boolean
writeFloat(address, value) Write 4-byte float
writeDouble(address, value) Write 8-byte double
writePointer(address, value) Write 4-byte pointer
writeNullTermString(address, value, maxLength) Write null-terminated string
writeByteArray(address, value) Write binary data

Memory Management

Function Description
allocNullTermString(str) Allocate memory for string, returns pointer
freeNullTermString(address) Free memory allocated by allocNullTermString
isAccessAllowed(address, size, isWrite) Check if memory region is accessible
getAccessibleSize(address, isWrite) Returns how many bytes are accessible from address for read/write
getUserdataAddr(userdata) Get memory address of Lua userdata object

Constants

Name Value Description
MAX_NULL_TERM_STRING_LENGTH 2048 Maximum null terminated string length for string read/write helpers
MAX_BYTE_ARRAY_LENGTH 65536 Maximum byte array length for byte-array helpers

Process Operations

Function Return Type Description
getExeBase() int Get base address of game executable
getHeapRegions(write) table Get list of heap memory regions (see below)

getHeapRegions

Returns a table of heap memory regions. Each region has:

  • base - Base address of the region
  • size - Size of the region in bytes
  • end - End address of the region (base + size)

The write parameter (boolean) determines whether to return writable regions only.

Example:

-- Get readable heap regions
local regions = memhack.dll.process.getHeapRegions(false)

for i, region in ipairs(regions) do
    LOG(string.format("Region %d: 0x%X - 0x%X (size: %d)", 
        i, region.base, region.end, region.size))
end

Example: Direct Memory Access

Note: You should use structs instead of direct DLL calls when possible.

Note: If you are replacing objects in the game, do NOT deallocate them as the game will handle it. Only deallocate if you are using it on the Lua side and not passing ownership to the game

-- Reading memory directly
local address = 0x12345678
local value = memhack.dll.memory.readInt(address)
LOG("Value at address: " .. value)

-- Writing memory directly
memhack.dll.memory.writeInt(address, 42)

-- Getting game's base address
local exeBase = memhack.dll.process.getExeBase()
LOG(string.format("Game exe base: 0x%X", exeBase))

-- Allocating string memory
local str = "Hello World"
local strPtr = memhack.dll.memory.allocNullTermString(str)
LOG(string.format("Allocated string at: 0x%X", strPtr))
-- We allocated and kept ownership so we need to free as well
memhack.dll.memory.freeNullTermString(strPtr)

Example: Getting Userdata Address

Get the memory address of a game object:

local pawn = Board:GetPawn(0)
local pawnAddr = memhack.dll.memory.getUserdataAddr(pawn)
LOG(string.format("Pawn is at address: 0x%X", pawnAddr))

local game = Game
local gameAddr = memhack.dll.memory.getUserdataAddr(game)
LOG(string.format("Game object at: 0x%X", gameAddr))

Example: Memory Validation

Check if memory is safe to access:

local address = 0x12345678
local size = 256
local isReadable = memhack.dll.memory.isAccessAllowed(address, size, false)
local isWritable = memhack.dll.memory.isAccessAllowed(address, size, true)

if isReadable then
    local data = memhack.dll.memory.readByteArray(address, size)
end