Pointersο
In a running program, most values you care about are reached through a pointer chain β a static base address plus a series of offsets that ultimately land on the value. Cheat Engineβs βpointer scanβ feature is built around this idea, and PyMemoryEditor offers the same workflow:
resolve_pointer_chainβ walk a chain you already know.RemotePointerβ a live, re-resolving handle that re-walks the chain on every read/write.scan_pointer_pathsβ the reverse operation: find chains that resolve to a given address. See Pointer scan.
Why pointer chains?ο
A scanned address (0x1FA3C140) typically changes every run: the OS loads
modules at randomized base addresses (ASLR), and the heap allocates objects in
different places each time you launch the program.
A multi-level pointer, in contrast, expresses a value as
module + offset β [+x] β [+y] β β¦. The static base (the module + static
offset) doesnβt change between runs once ASLR is accounted for, so the same
recipe works every time.
Walking a chainο
resolve_pointer_chain performs the walk and returns the final address
where the value lives:
# Cheat-table entry: "game.exe" + 0x10F4F4 -> [+0x0] -> [+0x158]
module = next(m for m in process.get_modules() if m.name == "game.exe")
base = module.base_address + 0x10F4F4
hp_address = process.resolve_pointer_chain(base, [0x0, 0x158])
hp = process.read_int(hp_address)
Method signatureο
- resolve_pointer_chain(base_address, offsets, *, ptr_size=None)
Walk a multi-level pointer chain.
Reads
ptr_sizebytes atbase_addressto obtain the first pointer, then for each offset inoffsets[:-1]adds the offset and dereferences again. The last offset is added without dereferencing β the returned integer is the final address where the value of interest lives.- Parameters:
base_address (int) β starting address β typically
module_base + static_offset.offsets (Sequence[int]) β sequence of offsets to walk. Pass
[]to dereferencebase_addressonce and return that pointer.ptr_size (int) β pointer width β
8for 64-bit targets,4for 32-bit. LeaveNone(the default) to use the targetβspointer_size, detected automatically.
- Returns:
the final address (an
int).
32-bit vs 64-bit
By default (ptr_size=None) the pointer width is detected from the target β
4 bytes for a 32-bit process, 8 for 64-bit. Pass ptr_size explicitly only
to override that; setting it to the wrong width reads pointers of the wrong
size and yields garbage addresses.
Live pointers: RemotePointerο
resolve_pointer_chain finds an address once. A RemotePointer wraps the
same recipe in a reusable handle β every time you read .value, the chain
is re-walked, so the handle keeps working even as the target moves things
around the heap.
# A handle to the player's HP, behind a two-level pointer.
hp_ptr = process.get_pointer(
base + 0x10F4F4,
[0x0, 0x158],
pytype=int,
bufflength=4,
)
print(hp_ptr.value) # read it
hp_ptr.value = 9999 # write it
process.get_pointer(...) is a convenience wrapper around the
RemotePointer constructor β it accepts the same arguments
(base_address, offsets, pytype, bufflength, ptr_size).
Direct vs chained handlesο
The offsets argument controls what RemotePointer does on every access:
offsets | Behavior |
|---|---|
None (default) | Direct handle: address = base_address, no dereferencing. Use this to wrap an address you already have (e.g. from search_by_value). |
[] (empty list) | Dereferences base_address once and reads the value at that pointer. |
[o1, o2, ...] | Walks the chain on every access β resolve_pointer_chain semantics. |
Pointer arithmeticο
RemotePointer supports C-style arithmetic. Adding an integer returns a
new handle, without touching memory:
# Mana is stored right after HP, so just step 4 bytes forward.
mp_ptr = hp_ptr + 4
print(mp_ptr.value)
You can also subtract two pointers to get a byte distance:
distance = mp_ptr - hp_ptr # 4
RemotePointer APIο
- class RemotePointer(process, base_address, offsets=None, *, pytype=int, bufflength=None, ptr_size=None)
A re-resolving, read/write handle to a typed value in a target process.
- property process
The
AbstractProcessthis pointer reads from / writes to.
- property base_address
The starting address the pointer was built with.
- property offsets
The pointer-chain offsets, or
Nonefor a direct handle.
- property address
The address the value currently lives at β recomputed on every access for a pointer chain.
- property value
Read or write the value at
addressusing the bound type.
- read(pytype=None, bufflength=None)
Read the value at
address, optionally overriding the bound type for one-off reads.
- write(value, pytype=None, bufflength=None)
Write
valuetoaddress, optionally overriding the bound type.
- __add__(delta)
ptr + nβ a new pointernbytes ahead.
- __sub__(other)
ptr - nβ a new pointernbytes behind.ptr - other(whereotheris aRemotePointer) β the byte distance between the two resolved addresses.
- __int__()
The resolved
addressβ handy for arithmetic and logging.
See also
Pointer scan β discover chains that resolve to a given address.
API reference β full
RemotePointerreference.