RemotePointer

A re-resolving, read/write handle to a typed value in a target process.

from PyMemoryEditor import RemotePointer

Most users build a RemotePointer through process.get_pointer(...). The constructor is documented here for completeness.

Construction

class RemotePointer(process, base_address, offsets=None, *, pytype=int, bufflength=None, ptr_size=None)
Parameters:
  • process (AbstractProcess) – the open process the value lives in.

  • base_address (int) – starting address. For a direct handle this is the address of the value itself; for a pointer chain it is typically module_base + static_offset.

  • offsets –

    how to reach the value from base_address:

    • None (default) β€” direct handle: address is base_address, with no dereferencing. Use this to wrap an address you already have (e.g. from search_by_value()).

    • a sequence (including the empty list []) β€” the value sits at the end of a pointer chain. address is recomputed on every access via resolve_pointer_chain(). [] dereferences base_address once; a non-empty list walks each offset, dereferencing all but the last.

  • pytype (Type) – how to interpret the bytes β€” bool, int, float, str or bytes. Defaults to int.

  • bufflength (int) – value size in bytes. Optional for numeric types (intβ†’4, floatβ†’8, boolβ†’1). For str / bytes it is required only to read (nothing to infer the width from); writing accepts None and stores the whole value, while a set bufflength caps the width (truncating).

  • ptr_size (int) – pointer width used when walking offsets β€” 8 for 64-bit targets, 4 for 32-bit. Leave None (the default) to use the target’s pointer_size, detected automatically. Ignored for direct handles.

Properties

process: AbstractProcess

The process this pointer reads from / writes to.

base_address: int

The starting address the pointer was built with.

offsets: Sequence[int] | None

The pointer-chain offsets, or None for a direct handle.

address: int

The address the value currently lives at. Recomputed on every access for a pointer chain β€” so each read reflects where the target’s pointers point now.

property value

Read or write the value at address using the bound type.

hp_ptr.value           # read
hp_ptr.value = 9999    # write

Methods

read(pytype=None, bufflength=None)

Read the value at address, optionally overriding the bound type for one-off reads.

Parameters:
  • pytype (Type) – interpret the bytes as this type for this call only.

  • bufflength (int) – override the bound buffer size.

Returns:

the decoded value.

write(value, pytype=None, bufflength=None)

Write value to address, optionally overriding the bound type. Returns whatever write_process_memory() returns.

Operators

RemotePointer supports C-style pointer arithmetic:

__add__(delta)
__radd__(delta)

ptr + n (or n + ptr) β†’ a new pointer n bytes ahead. Does not touch memory.

__sub__(other)
  • ptr - n β†’ a new pointer n bytes behind.

  • ptr - other (where other is a RemotePointer) β†’ the byte distance between the two resolved addresses.

__int__()

The resolved address. Useful for arithmetic and logging:

print(f"HP is at 0x{int(hp_ptr):X}")
__repr__()

Diagnostic representation, e.g. <RemotePointer base=0x14010F4F4 -> [0, 344] pytype=int>.

Lazy chain folding

When you do hp_ptr + 4, the shift is folded into the last offset of the chain (rather than the resolved address), so the returned pointer still re-walks the chain on every access. In other words, (hp_ptr + 4) keeps following the target as it moves around the heap β€” it doesn’t snapshot the address at shift time.

Examples

Direct handle from a scan

addresses = list(process.search_by_value(int, 4, 100))
ptr = process.get_pointer(addresses[0], pytype=int, bufflength=4)

ptr.value = 9999

Chained handle from a cheat-table entry

# "game.exe" + 0x10F4F4 -> [+0x0] -> [+0x158]
module = next(m for m in process.get_modules() if m.name == "game.exe")
hp = process.get_pointer(
    module.base_address + 0x10F4F4,
    [0x0, 0x158],
    pytype=int,
    bufflength=4,
)
hp.value -= 10

Walking sibling fields with arithmetic

# HP and MP are stored side-by-side as 4-byte ints.
mp = hp + 4
mp.value = 100

Reading the same address as a different type

# Peek the same address as raw bytes without building a second handle.
print(hp.read(pytype=bytes, bufflength=4))