Introduction

Welcome modder!

Native Memory Scripter is a plugin which allows you to create a native plugin by writing Python scripts. This saves a lot of time and makes the process of native plugin dev a lot easier.

You can find all the information you need in this technical document. If there are any api’s missing or things that you feel can/should be improved, don’t hesitate to make an issue report or even a pr.

Features

  • Use Python to manipulate memory
  • Assembly of plaintext asm / disassembly of bytes
  • Virtual method table hooking
  • Module search, loading, and unloading
  • Segment search (pages)
  • External symbol search in modules with name demangling support
  • Import address table search and hooking
  • Pattern scanning for bytes in memory (supports AVX2, SSE4.2, and regular scalar scanning)
  • Memory manipulation (read, write, set, and allocate memory)
  • Call native functions from python, and hook (vmt, iat, and jmp) native functions with a python callback, along with trampoline to call the original
    • Powered by a JIT compiler
  • Written in Rust 🦀

License

This software has a source-available non-open source license. This software:

  • may only be used in personal use for the purpose in which it was redistributed by the original creator
  • may be forked, however modifications are not allowed
  • may be compiled in its unmodified form
  • modifications and compiling with modifications are allowed under the condition that you submit your changes back to the main repo
  • cannot be redistributed
  • cannot be used/re-used in your own projects
  • cannot be used for any commercial purposes
  • cannot be sold
  • code cannot be used in any way for any purpose. it is copyrighted

For full terms, please see the license

Disclaimer

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Configuration

The configuration uses toml and is stored at native-memory-scripter.toml alongside the dll.

dev

This section contains developer settings.

keytypedescription
consoleboolShows or hides the debug console.
dev_modeboolShows or hides dev mode.1
1

Dev mode is a special mode that spawns an interactive python interpreter window that you can script in while the game is running

log

This section controls the built in logger.

keytypedescription
levelstrAllows you to fine tune what gets logged.
targetsboolShow or hide log targets.2
2

A target is a piece of information that describes what part of the code a particular message came from. The target is the code’s namespace by default.

Project Layout and Setup

In the same directory native-memory-scripter.dll is inside, the directory layout will be as follows

│   native-memory-scripter.dll
│   native-memory-scripter.log
│   native-memory-scripter.toml
│
└───native-scripts
    │
    ├───script
    │       plugin.toml
    │       main.py
    │       module1.py
    │
    └───_packages
        └───libs
                package1.py

At the top level is the dll’s config file and a log file.

Plugin folder names

Plugin folder names can be named anything, but it’s strongly recommended to stick to the same name as your plugin.

Native Plugin Scripts

Each native plugin script must be placed in a folder, along with a plugin.toml describing the plugin and a main.py which is the plugin’s entry point. Each plugin is concurrently run in a separate python interpreter. Scripts may import any local module from their own directory, e.g. import module1.

Plugin details

Every plugin must provide a plugin.toml that describes the plugin

[plugin]
name = "My Plugin Name"
author = "Plugin Author"
description = "Description of my plugin"
version = "0.1.0"

Libraries

The _packages directory is where modules are stored that can be used across multiple scripts. Every script can import from here with e.g. from libs import package1

Best Practices

It is important to keep some best practices in mind when making a script, so that things continue to work properly.

Please ensure you follow these tips carefully.

Code with API changes in mind

Api may change between plugin versions as this product is updated.

Because of that, it is very important to script against specific versions of native memory scripter so you can account for any api changes and keep things running smoothly with no errors on the user side. Using this technique, you can support multiple versions seamlessly. You can check the plugin version in your script by using info.version. All api changes are catalogued in the changelog.

Beware of Dropping

Objects that require allocation such as WStr, Callable, and others, automatically free their memory when deleted or reclaimed by gc. This can serve as a source of hidden UB since it can cause a UAF. Because of this, even allocated code can suddenly disappear during execution!

Make sure that any allocated objects in Python stay alive during the duration you’re using them. This is absolutely crucial. This especially applies when returning some allocated data from a callback function. If the data goes out of scope, python reclaims it in gc, so your returned data is now UAF.

If you need to debug if something is dropping prematurely, turning on trace logs which will help you determine when drops are happening.

Tips and Tricks

Use the struct module

Use the python struct module to pack and unpack custom structs over ffi.

Let’s say you received a ptr to a struct:

#![allow(unused)]
fn main() {
#[repr(C)]
struct Foo {
    foo: u32,
    bar: u64,
    baz: *const () // platform sized ptr
}
}

Then you could pack or unpack it in python like so

import struct

class Foo:
    def __init__(self, bar, baz, some_ptr):
        self.bar = bar
        self.baz = baz
        self.some_ptr = some_ptr

    def pack(self):
        return struct.pack('IQP', self.bar, self.baz, self.some_ptr)

    @classmethod
    def unpack(cls, packed):
        bar, baz, some_ptr = struct.unpack('IQP', packed)
        return cls(bar, baz, some_ptr)

Store bytes in hex

This is relatively simple, but useful way to store your bytes in a readable format.

mybytes = bytes.fromhex('00 00 00 00')

Here be Dragons 🐉

This goes without saying, but anything can happen when editing a process’s memory. This requires extensive knowledge, reverse engineering, and a delicate touch. There is no limit to the UB this can cause. Most of these functions are unsafe and will easily cause UB if used wrong. A crash is the best case scenario.

You have been warned! Author takes no responsibility in case of accidents.

If you need help with anything or need to understand api safety invariants, feel free to visit my discord.

FAQ

Why does it keep crashing?

This is almost certainly the result of UB. See if you can check the following:

  • are the function arguments correct?

  • is the function return correct?

  • is the function abi correct?

  • are you following all safety invariants when reading, writing, and changing protections on memory?

  • is the address you’re hooking able to be hooked at the assembly level? not all addresses will work because of things like function size or where you placed the hook.

  • if executing the trampoline fails, did you hook at a location where relocating old instructions to the trampoline works? for example, instructions which are relative to their location cannot be relocated.

asm

This module allows you to assemble and disassemble assembly instructions

Function: assemble

Assembles asm instruction(s) from a string

This function is safe

Parameters

This function has multiple calling types.

First

Assemble a single instruction

  • code: str - a string with the instruction to assemble, e.g. jmp [rip]

Second

Assemble multiple instructions (with runtime address)

  • code: str - a string with the instructions to assemble, e.g. jmp [rip]; nop.
  • runtime_address: int - the address to annotate each instruction with.

Exceptions

If instruction fails to assemble

Return Value

Returns an Inst representing the assembled instruction

Function: code_len

Given a starting address and minimum length, finds the closest byte length that form valid instructions

This function is unsafe 🐉

  • The code address must at minimum be valid for reads from min_length up to the nearest instruction end

Parameters

  • code: int - the starting address of the code
  • min_length: int - the minimum desired byte length you want

Exceptions

If it fails to diassemble

Return Value

Returns an int representing the length in bytes of the nearest valid instructions to the min_length

Function: disassemble

Disassemble instructions.

Parameters

This function has multiple calling types.

First (address)

Disassemble single instruction at address.

This call is unsafe 🐉

Address must be valid for up to 16 bytes read.

  • address: int - the starting address of the code to disassemble.

Second (address)

Disassemble all instructions from an address, with a runtime address.

This call is unsafe 🐉

Address must be valid for up to size read.

  • address: int - the starting address of the code to disassemble.
  • size: int - how many bytes into the address to read.
  • runtime_address: int - the address to annotate each instruction with.

Third (address)

Disassemble count instructions from address.

This call is unsafe 🐉

Address must be valid for up to size read.

  • address: int - the starting address of the code to disassemble.
  • size: int - how many bytes into the address to read.
  • runtime_address: int - the address to annotate each instruction with.
  • count: int - how many instructions to disassemble.

First (bytes)

Disassemble all bytes from into instructions.

This call is safe

  • bytes: bytes - the bytes to disassemble.

Second (bytes)

Disassemble all bytes from into instructions, with runtime address.

This call is safe

  • bytes: bytes - the bytes to disassemble.
  • runtime_address: int - the address to annotate each instruction with.

Third (bytes)

Disassemble count instructions from address.

This call is safe

  • bytes: bytes - the bytes to disassemble.
  • runtime_address: int - the address to annotate each instruction with.
  • count: int - how many instructions to disassemble.

Exceptions

If it fails to diassemble

Return Value

Returns an Inst representing the disassembled instruction

Example

import asm

foo = bytes.fromhex('00 00 00 00')
insts = asm.disassemble(foo)

# pretty print each instruction
for i in insts:
    print(i)

objects

Object: Inst

An assembly instruction

Properties

address: int

If the runtime_address parameter was used, this will be the instruction’s offset from that base address. Otherwise, the address starts at a default offset of 0.

bytes: bytearray

The instructions binary data.

mnemonic: str

The instruction’s mnemonic.

op_str: str

The instruction’s operands.

cffi

This module allows you to call C functions from python, or use python callbacks for your hooks, as well as allowing you to call the trampoline.

How?

This leverages a JIT compiler to generate a machine code wrapper on the fly which has the same signature as the original native function. This wrapper then translates the native args to python, calls your python function with the args, and translates the return value back to native. In this way, it’s transparent, just as if it were the real function. This also generates a trampoline to call the original code.

In the case of NativeCall – just the trampoline portion is used.

Python functions can JIT

The bundled python interpreter is also compiled with JIT support to make your python functions run faster, though this may compile only under limited circumstances right now.

You must manually enable it. To make use of it, call __jit__() on any python function. If your function is uncompilable, you will quickly know as it’ll throw an exception.

Module: Type

These types are meant to be used in NativeCall and Callable.

Floats

Python type: int

F32

F64

Unsigned

Python type: int

U8

U16

U32

U64

U128

Signed

Python type: int

I8

I16

I32

I64

I128

Strings

CStr

Python type: str

When received as a Callback argument or a NativeCall return, it’s a python string without null terminator. When used as a NativeCall argument and Callback return, you MUST include a null terminator in the string.

WStr

NativeCall return type / Callback argument: int (pointer to address)

NativeCall argument / Callback return type: WStr

This may or may not be null terminated, depends on api requirements. Some functions require you to pass a length, sometimes it needs to be null terminated. This is up to you to manage according to the api.

On a Callback, an argument of this type will be a ptr. On a NativeCall, a return of this type will be a ptr. In order to convert the ptr into a string you can use, see WStr.

To return a WStr from a Callback, or give a WStr argument to a NativeCall, use WStr.

Chars

Python type: str

Char

Python type: str (1 character that can fit in a u8)

1 byte

WChar

Python type: str (1 character that can fit in a u16)

2 bytes

Misc

Ptr

Python type: int

Bool

Python type: boolean

Struct(size: int)

Python type: bytes

This is a by-value struct (not a ptr to a struct). You can use this type in arg or return position to indicate receiving or returning a struct by value.

The size must be correct or it’ll be ub.

Example

import cffi

def foo(arg: int):
    pass

c = cffi.Callable(foo, cffi.Types.U64)

Module: Conv

These types are meant to be used in NativeCall and Callable.

Calling Conventions

C

This is the default.

WindowsFastcall

Stdcall

Example

import cffi

def foo():
    pass

c = cffi.Callable(foo, cffi.Types.U64, conv = cffi.Conv.C)

objects

Object: Callable

Hook a function and use a python function as the hooks callback.

Constructor

  • Callable[[*args], Any] - Any args and Any return type. Constrained to the types available in Type. Must match the following signature passed in to the constructor.
  • *args - Any Type’s matching the corresponding native function’s argument types.
  • **kwargs - ret for the Type return value, and conv to change the calling convention.

Drop

The allocated callback code and trampoline code will automatically be freed when this is deleted or reclaimed.

Properties

address: int

The address of the jitted callback function (the code that is a stand-in replacement of a native function and calls your python callback).

code_size: int

The size of the jitted callback function in bytes.

trampoline_address:

The address of the underlying trampoline (the trampoline fn that the jitpoline calls).

trampoline_size

The size of the underlying trampoline.

jitpoline_address

The address of the jitpoline (the jitted trampoline that __call__ calls).

Magic

This object implements __call__(). You may call this object with your args and it will call the trampoline.

Using the call function is unsafe 🐉

You must use the correct arguments / return types, otherwise using the function will be ub.

Additionally, your callback function MUST gracefully handle all possible exceptions and return something. If it there’s an uncaught exception, it is UB. But to protect the program, it will instantly crash instead. You should fix it asap.

If you specified a return type, you MUST always return a value of that type, even if your function caught an exception.

Methods

hook

jmp hook from address. This will attempt to allocate within ± 2GB of from address so it can use a 5 byte jmp, but if it’s unable to it will use 14 byte jmp.

This function is unsafe 🐉

  • from must point to a xr function with the same signature as your callable (abi, parameters, and return).
  • from: int|Symbol - the function address or Symbol to hook.

hook_iat

Hook an import address table entry.

This function is unsafe 🐉

Exceptions

If virtual protect fails.

hook_vmt

Hook a virtual method table entry.

This function is unsafe 🐉

  • index must be a valid index, and abi, args, and return must all be correct types.
  • vtable: VTable - the vtable within which to hook.
  • index: int - the index of the vtable method to hook.

Exceptions

If virtual protect fails.

unhook

Unhook the callback.

This function is unsafe 🐉

Exceptions

If virtual protect fails.

Example

import modules
import symbols
import cffi

# this will be called every time the original code calls "createTestClass"
def foo():
    # call the trampoline
    val = callable(obj)
    return val

module = modules.load("Dll1.dll")

create = symbols.find(module, "createTestClass")

callable = cffi.Callable(foo, ret = cffi.Type.U64)
callable.hook(create)

# unhook the callback
callable.unhook()

Object: NativeCall

Call a native function from Python.

Constructor

  • address: int|Symbol - The address or Symbol to call.
  • *args - Any Type’s matching the corresponding native function’s argument types.
  • **kwargs - ret for the Type return value, and conv to change the calling convention.

Drop

The allocated code will automatically be freed when this is deleted or reclaimed.

Magic

This object implements __call__(). You may call this object with your args and it will call the underlying native function.

Using the call function is unsafe 🐉

You must use the correct arguments / return types, otherwise calling this is ub.

Example

import modules
import symbols
import cffi

module = modules.load("Dll1.dll")

create = symbols.find(module, "createTestClass")
one = symbols.find(module, "callTestMethod")
two = symbols.find(module, "callTestMethod2")

create = cffi.NativeCall(create, ret = cffi.Type.U64)
obj = create()

one = cffi.NativeCall(one, cffi.Type.U64)
one(obj)

two = cffi.NativeCall(two, cffi.Type.U64)
two(obj)

Object: WStr

Converts a WStr python string to a wstr pointer, or a pointer to a python string.

Drop

Memory is freed when this object is deleted or reclaimed by gc.

Constructor

This constructor has two ways to call it. One way to convert a python string to WStr, and another way to convert an address to a python string.

Python string to WStr

When used this way, it converts your python string to a type that you can return in a Callable or give as an argument to a NativeCall.

This call is safe

  • data: str - the string to convert to WStr. do not put a null terminator in it, use the kwarg for that.
  • **kwargs - null: boolean - insert a null terminator at the end of your string.

Address to WStr

When used this way, it converts a WStr pointer to this type, allowing you to access it as a python string.

Use either one of the null or len kwargs to tell this type how to decode the string from the address. Only 1 of these is allowed, do not use both kwargs.

This call is unsafe 🐉

  • address must be a valid address for reads up to len or up to the next null terminator
  • address: int - the WStr pointer address.
  • **kwargs - null: boolean - decode a string with a null terminator.
  • **kwargs - len: int - decode a string with a specific length. this is not byte length. this is element length. that is, one element is a u16 (2 bytes).
  • **kwargs - lossy: boolean - do not raise exception if provided string is not valid utf8. Warning, will return a string yes, but invalid characters will have been replaced with .

Using the type

To convert a WStr to a regular python string, just do str(foo) on your WStr type.

Properties

size: int

The byte size of the WStr. (The number of u16 elements is size / 2)

address: int

The address to the WStr’s buffer.

Exceptions

If you supply an address and both null and len kwargs.

If you supply the address but neither null or len kwargs.

If the utf16 string to decode from an address is not a valid utf8 string (try using the lossy kwarg!).

If the first arg is not an address (int) or str.

If there is no first arg.

If you provide types to arg or kwargs that do not match the listed types.

hook

This module allows you to use regular jmp hooks.

How?

The from address is replaced with a 5 or 14 byte jmp (depending on whether target address is within 32-bits or not).

The old code that was replaced is placed at the beginning of the trampoline, and a jmp is made back to the original function, but right after the original jmp we placed.

The custom hook function is free to call the trampoline if it wishes to.

The jmp can actually be placed anywhere, not necessarily at the beginning of the function. But you will have to make sure that this works properly in the assembly.

Not all instructions can be replaced

Certain instructions cannot be relocated to the trampoline. For example, instructions which use a relative address require themselves to be at the original address. It is your job to ensure the location that gets replaced is capable of being replaced. Or you could also just not call the trampoline.

Function: hook

Hooks an address by placing a jmp at the target and creating a trampoline to execute the original function.

This function is unsafe 🐉

  • from must be a valid address which can be written to, and must be a valid location to write a jmp of 5 or 14 bytes at.
  • to must be a valid target location, and must properly handle the requirements of the assembly at the jmp site.

If the from address is within 32-bits of the to address, will write a 5 byte jmp, otherwise will write a 14 byte jmp.

Parameters

  • from: int - the address to hook.
  • to: int - the address to redirect the from address to.

Exceptions

If virtual protect fails, or fails to get the underlying code len.

Return Value

Returns a Trampoline which can be used to execute the original code at the hooked location.

objects

Object: Trampoline

An import address table symbol.

Drop

Trampoline will free the underlying trampoline code, and unhook the target.

Properties

address: int

The address of the trampoline function.

size: int

The size of the trampoline function in bytes.

Methods

unhook

Unhook the hooked function.

This function is unsafe 🐉

Exceptions

If virtual protect fails.

iat

This module allows one to search through a Module’s IAT (import address table) and hook the entries.

How?

The module’s internal structures to find its import addresss table, then we extract the needed information from it. After that, all that needs to be done is write a new ptr to the iat’s entry for that specific function.

Function: enum

Returns a list of all import address table entries in a Module.

This function is safe

Parameters

  • module: Module - the module to get the symbols for.

Exceptions

If module in memory is invalid or cannot otherwise be read.

Return Value

Returns a [IATSymbol]

Function: enum_demangled

Returns a list of all import address table entries in a Module and demangles the names.

This function is safe

Parameters

  • module: Module - the module to get the symbols for.

Exceptions

If module in memory is invalid or cannot otherwise be read.

Return Value

Returns a [IATSymbol]

Function: find

Finds an IATSymbol in a module Module.

This function is safe

Parameters

This function has two calling signatures.

Name / Ordinal

  • module: Module - the module to get the symbols for.
  • name: str|u16 - the symbol name or ordinal number to look for. must be exact case-sensitive match.

Name / Ordinal and Dll name

  • module: Module - the module to get the symbols for.
  • dll_name: str - the dll name to look for the symbol in. is an exact case sensitive match, e.g. fooBar.dll.
  • name: str|u16 - the symbol name or ordinal number to look for. must be exact case-sensitive match.

Exceptions

If module in memory is invalid or cannot otherwise be read.

Return Value

Returns a IATSymbol if found, or None if not found.

Function: find_demangled

Finds an IATSymbol in a module Module and demangles its name.

This function is safe

Parameters

This function has two calling signatures.

Name

  • module: Module - the module to get the symbols for.
  • name: str - the symbol name to look for. must be exact case-sensitive match.

Name and Dll name

  • module: Module - the module to get the symbols for.
  • dll_name: str - the dll name to look for the symbol in. is an exact case sensitive match, e.g. fooBar.dll.
  • name: str - the symbol name to look for. must be a case-sensitive fuzzy match. this means your search term matches by case-exactly, however it is a contains search, e.g. searching for FooBar in void symbol FooBarBaz() matches, but foobar won’t match.

Exceptions

If module in memory is invalid or cannot otherwise be read.

Return Value

Returns a IATSymbol if found, or None if not found.

objects

Object: IATSymbol

An import address table symbol.

Drop

IAT entry will automatically unhook itself once deleted or garbage collected.

Properties

name: Optional[str]

The name of the symbol.

ordinal: Optional[int]

The ordinal of the symbol.

dll_name: str

The name of the dll.

orig_fn: int

A pointer to the original iat entry function.

iat: int

A pointer to this IAT entry. Writing an address to this will hook it.

Methods

hook

Hook this iat entry.

This function is unsafe 🐉

  • address must point to a xr function with the same signature as the original (abi, parameters, and return).
  • address: int - the function address to redirect the iat entry to.

Exceptions

If virtual protect fails.

unhook

Unhook this iat entry.

This function is unsafe 🐉

Exceptions

If virtual protect fails.

info

This module contains useful native memory scripter plugin information to scripts. This can also give you context in your scripts.

API compatibility

One such useful property is version, which gives you native memory scripter’s current version. This lets you handle api changes gracefully even when there are different native memory scripter versions installed on users computers.

Property: version

gets the version of the plugin

Return Value

version

Example

import info
version = info.version
if version.major == 0 and version.minor == 1:
    pass

objects

Version

represents the plugin’s version

Properties

major: int

The plugin’s major version number

minor: int

The plugin’s minor version number

patch: int

The plugin’s patch version number

log

This module contains functions for printing different log types to the console or log file.

Function: trace

Log a trace message to the console/log file.

This function is safe

Parameters

  • *args - any parameter to log.
  • [kwarg] sep: str - the separator. default ' '.

Exceptions

If object string and repr can’t be printed.

Function: debug

Log a debug message to the console/log file.

This function is safe

Parameters

  • *args - any parameter to log.
  • [kwarg] sep: str - the separator. default ' '.

Exceptions

If object string and repr can’t be printed.

Function: info

Log an info message to the console/log file.

This function is safe

Parameters

  • *args - any parameter to log.
  • [kwarg] sep: str - the separator. default ' '.

Exceptions

If object string and repr can’t be printed.

Function: warn

Log a warning message to the console/log file.

This function is safe

Parameters

  • *args - any parameter to log.
  • [kwarg] sep: str - the separator. default ' '.

Exceptions

If object string and repr can’t be printed.

Function: error

Log an error message to the console/log file.

This function is safe

Parameters

  • *args - any parameter to log.
  • [kwarg] sep: str - the separator. default ' '.

Exceptions

If object string and repr can’t be printed.

mem

This module contains the api for editing the current processes memory.

Function: alloc

Allocates size bytes of memory with protection flags Prot in the calling process.

This function is safe

Parameters

  • size: int - the size of memory to be allocated. If the size is 0, the function will allocate a full page of memory. If a specific size is provided, that amount of memory will be allocated, aligned to the next page size.
  • prot: Prot - the protection flags.

Exceptions

If allocation failed.

Return Value

Returns an Alloc.

Function: alloc_granularity

The granularity for the starting address at which virtual memory can be allocated.

This function is safe

Return Value

Returns an int.

Function: alloc_in

Allocates size bytes of memory with protection flags Prot in the calling process.

This function is safe

Parameters

This function can be called in 2 ways:

With align

Without align

Align will automatically be set to 0 for this.

Exceptions

If allocation failed. If begin_addr >= end_addr . If align is not power of 2, less than system allocation granularity, or not a multiple of system allocation granularity. If begin addresses next rounded up to granularity is not within begin..end. If end address rounded down to granularity is not within begin..end. If begin and/or end addresses are outside of minimum/maximum application address.

Return Value

Returns an Alloc.

Function: deep_pointer

Calculates a deep pointer address by applying a series of offsets to a base address and dereferencing intermediate pointers.

This function is unsafe 🐉

Parameters

  • base: int - the base address to start at.
  • offsets: [int] - the offsets used to navigate through the memory addresses. must be unsigned.

Exceptions

If memory address is null, or no offsets were provided.

Return Value

An int representing the final address.

Function: read

Reads size bytes starting at src address into bytearray.

This function is unsafe 🐉

  • src must be a valid address for reads up to size

Parameters

  • src: int - the base address to read from.
  • size: int - the amount of bytes to read.

Return Value

Returns a bytearray of the read bytes.

Function: set

Set dst address + size bytes to byte

This function is unsafe 🐉

  • dst must be a valid address for writes up to size

Parameters

  • dst: int - the destination address to write to.
  • byte: int - the byte to set the memory to.
  • size: int - the amount of bytes from the dst to set.

Function: write

Write some bytes to dst.

This function is unsafe 🐉

  • dst must be a valid address for writes up to size of bytearray.

Parameters

  • src: bytearray - the bytes to write to target.
  • dst: int - the destination address to write to.

Function: prot

Change the protection of a block of memory.

This function is unsafe 🐉

Parameters

  • address: int - the base address to change the protection on.
  • size: int - how many bytes from the base address to change.
  • prot: Prot - the protection flag.

Exceptions

If changing the protection failed.

Return Value

The old Prot.

objects

Object: Alloc

An allocation.

Drop

Memory will be automatically freed when deleted or gc reclaims it.

Properties

address: int

The address of the allocation.

Object: Prot

The protection flags.

Properties

NONE: Prot

No protection flags

X: Prot

Executable flag

R: Prot

Read flag

W: Prot

Write flag

XR: Prot

Executable + Read flag

XW: Prot

Executable + Write flag

RW: Prot

Read + Write flag

XRW: Prot

Executable + Read + Write flag

Example

import mem
flag = mem.Prot.X

modules

This module lets one find, enumerate over, load, and unload modules.

Function: load

Load a dll into the process.

This function is safe

Parameters

  • path: str - the full path to the dll.

Exceptions

If LoadLibraryW fails.

Return Value

Returns a Module.

Function: unload

Unload a module from the process.

This function is safe

Modules are only ever unloaded from a process once their internal refcount reaches 0. This only decreases the refcount by 1.

Don’t repeatedly call this without reason, or you will unload the module from memory prematurely when it’s still being used 🐉.

Parameters

  • path: str - the full path to the dll.

Exceptions

If GetModuleHandleW or FreeLibrary fails.

Function: find

Find a module by name.

This function is safe

Parameters

  • name: str - the name to search for. exact case-sensitive match, including extension. e.g. foobar.dll.

Exceptions

If unable to create a snapshot, no modules exist, unable to convert from utf16 to utf8, or load library.

Return Value

Returns a Module if found, None if not found.

Function: enum

Return a list of all modules

This function is safe

Exceptions

If unable to create a snapshot, no modules exist, unable to convert from utf16 to utf8, or load library.

Return Value

Returns a [Module] containing all of the process’s modules.

objects

Object: Module

A module.

Drop

Module will unload once gc collects this.

An unloaded module does not necessarily mean it’s unloaded from memory. Windows holds a refcount, and when free library is called, the refcount decreases by 1. This handle increases refcount by 1 when it’s made so it’s always safe to use as long as the handle is alive, thus it’s safe to decrease it by 1.

Properties

base: int

The base address of the module.

end: int

The end address of the module.

size: int

The size of the module.

path: str

The full path to the module.

name: str

The name of the module, e.g. foobar.dll.

popup

This module lets you create windows popups.

Please only use this for things that are important. Spamming pointless popups is really bad ux. Prefer logging over popups, and popups only as a last resort.

Function: error

Shows an error popup.

This function is safe

Parameters

  • title: str - the title of the popup.
  • message: str - the message in the popup.

Function: exclamation

Shows an exclamation popup.

This function is safe

Parameters

  • title: str - the title of the popup.
  • message: str - the message in the popup.

Function: info

Shows an info popup.

This function is safe

Parameters

  • title: str - the title of the popup.
  • message: str - the message in the popup.

Function: question

Shows a question popup.

This function is safe

Parameters

  • title: str - the title of the popup.
  • message: str - the message in the popup.

Function: warn

Shows a warning popup.

This function is safe

Parameters

  • title: str - the title of the popup.
  • message: str - the message in the popup.

scan

This module contains functions for searching through memory for pattern masks.

Scanning uses SIMD

If the computer has AVX2, this is used. If not, but it has SSE4.2, then this is used. If it has none of those, then a regular scalar search is used.

AVX2 and SSE4.2 can reach search speeds of about 1.8gb/s, and scalar search can reach about 1gb/s.

Function: data

Scan for a given set of bytes starting at address for scan_size bytes.

This function is unsafe 🐉

  • address must be a valid address for reads up to scan_size
  • scan_size must be a valid length

Parameters

  • data: bytearray - the data to search for.
  • address: int - the address to start looking from.
  • scan_size: int - how many bytes to search for from the starting address.

Return Value

On success, it returns an int representing the found location’s memory address. On failure, it returns None.

Function: pattern

Searches address for scan_size bytes for a match given some bytes and a pattern.

This function is unsafe 🐉

  • address must be a valid address for reads up to scan_size
  • scan_size must be a valid length

Parameters

  • pattern: bytearray - the data to search for. if some bytes are masked out, it’s customary to leave them at 0.
  • mask: str - the mask for the bytes. use x for a known byte and ? for an unknown byte. example, xx?x?xx?
  • address: int - the starting address to look for the pattern at.
  • scan_size: int - how many bytes to search for from the starting address.

Return Value

On success, it returns an int representing the found location’s memory address. On failure, it returns None.

Function: sig

Searches address for scan_size bytes for a given IDA-style signature.

This function is unsafe 🐉

  • address must be a valid address for reads up to scan_size
  • scan_size must be a valid length

Parameters

  • sig: str - an IDA-style signature to search for, e.g. 11 22 33 ?? 44 ?? 55 ?? ??, where 11 is a known byte and ?? is an unknown byte.
  • address: int - the starting address to look for the pattern at.
  • scan_size: int - how many bytes to search for from the starting address.

Return Value

On success, it returns an int representing the found location’s memory address. On failure, it returns None.

segments

This module finds segments (pages) within a Module.

Function: enum

Return a list of all segments

This function is safe

Return Value

Returns a [Segment] containing all of the process’s segments.

Function: find

Finds the segment of a given address.

This function is safe

Parameters

  • address: int - the address you want to find belongs to what segment.

Return Value

Returns the Segment corresponding to the address. If it cannot find it, will return None.

objects

Object: Segment

A Module’s external symbol.

Properties

base: int

The base address of the segment.

end: int

The end address of the segment.

size: int

The size of the segment.

prot: Prot

The protection of the segment.

symbols

This module finds and demangles external symbols from a Module.

Function: demangle

Demangle a symbol.

Supports C++ (GCC-style compilers and MSVC), Rust (both legacy and v0), Swift (up to Swift 5.3), and ObjC (only symbol detection).

This function is safe

Parameters

  • name: str - the mangled symbol to demangle.

Return Value

On success, it returns str representing the demangled symbol. On failure, it returns None.

Function: enum

Returns a list of all symbols for a Module.

This function is safe

Parameters

  • module: Module - the module to get all symbols from.

Exceptions

If module memory is invalid, or cannot find any exports.

Return Value

Returns a [Symbol].

Function: enum_demangled

Returns a list of all demangled symbols for a Module.

This function is safe

Parameters

  • module: Module - the module to get all demangled symbols from.

Exceptions

If module memory is invalid, or cannot find any exports.

Return Value

Returns a [Symbol].

Function: find

Returns the memory address of a symbol. The search is case-sensitive and must be an exact match.

This function is safe

Parameters

  • module: Module - the module to search through.
  • name: str - the name of the symbol to search for. case-sensitive exact search.

Exceptions

If module memory is invalid, or cannot find any exports.

Return Value

Returns an int representing the memory address of the symbol. If not found, returns None.

Function: find_demangled

Returns the memory address of a demangled symbol. The search is fuzzy, but case-sensitive.

This function is safe

Parameters

  • module: Module - the module to search through.
  • name: str - the name of the symbol to search for. case-sensitive fuzzy search. this means your search term matches by case-exactly, however it is a contains search, e.g. searching for FooBar in void symbol FooBarBaz() matches, but foobar won’t match.

Exceptions

If module memory is invalid, or cannot find any exports.

Return Value

Returns an int representing the memory address of the symbol. If not found, returns None.

objects

Object: Symbol

A Module’s external symbol.

Properties

name: str

The name of the of the module, e.g. foobar.dll.

address: int

The address of the symbol.

vtable

This module contains helpers to hook c++ virtual method tables.

How?

We find the correct index offset in the vtable’s array of pointers and write a new ptr to that location.

objects

VTable

Hook a c++ vtable.

Constructor

  • address: int - the base address of the vtable.

Drop

When this is deleted, it will automatically reset all hooks.

Methods

hook

Hooks a vtable by index.

This function is unsafe 🐉

  • index must be a valid vtable index.
  • dst must point to a xr function with the same signature as the original (abi, parameters, and return).
  • index: int - the index to hook.
  • dst: int - the function address to redirect the vtable entry to.

Exceptions

If virtual protect fails.

unhook

Unhooks a vtable by index.

This function is unsafe 🐉

  • index must be a valid previously hooked vtable index.
  • index: int - the index to unhook.

Exceptions

If virtual protect fails.

get_original

Get a pointer to the original function stored at a previously hooked index.

This function is safe

  • index: int - the index to unhook.

Return Value

Returns an int representing a ptr to the original vtable function if index was previously hooked, otherwise returns None.

reset

Resets all previously hooked vtable entries back to their original functions.

This function is unsafe 🐉

Exceptions

If virtual protect fails.

Examples