PointerPath

A discovered static pointer path, returned by scan_pointer_paths.

from PyMemoryEditor import PointerPath

Resolving base_address and then walking offsets with resolve_pointer_chain lands on the target. When the base sits inside a known module, module / module_offset express it ASLR-independently so the path survives a restart β€” feed it to rebase() in the next run.

Construction

class PointerPath(base_address, offsets, module=None, module_offset=None, ptr_size=8)
Parameters:
  • base_address (int) – absolute static base for this run (the slot whose pointer the chain dereferences first).

  • offsets (Tuple[int, ...]) – forward-order offsets, ready to hand to resolve_pointer_chain().

  • module (Optional[str]) – name of the module containing base_address (None when the base falls in a caller-supplied static range with no known module).

  • module_offset (Optional[int]) – base_address - module.base_address β€” the portable, ASLR-independent part of the base. None when module is.

  • ptr_size (int) – pointer width β€” 8 for 64-bit (default) or 4 for 32-bit.

PointerPath is a @dataclass(frozen=True) β€” instances are immutable and hashable.

Attributes

AttributeTypeMeaning
base_addressintAbsolute static base for the run the path was found in.
offsetsTuple[int, ...]Forward-order offsets.
moduleOptional[str]Module owning base_address.
module_offsetOptional[int]base_address - module.base_address.
ptr_sizeintPointer width (4 or 8).

Methods

Resolving

resolve(process)

Walk this path in process and return the final target address.

Parameters:

process (AbstractProcess) – the open process.

Returns:

the target address (int).

to_pointer(process, *, pytype=int, bufflength=None)

Build a live RemotePointer for the value at the end of this path.

Returns:

a RemotePointer re-resolving on every access.

Rebasing across runs

rebase(process)

Return a copy with base_address recomputed from the live module base in process β€” the call that makes a saved path valid again after a restart moved the module (ASLR).

Raises:
  • ValueError – this path has no associated module (its base came from a caller-supplied static range), so it cannot be rebased.

  • LookupError – the module is not loaded in process.

Serialization

to_dict()

Serialise to a JSON-friendly dict (hex strings) for export. The ASLR-independent part β€” module + module_offset + offsets β€” is what makes a saved path replayable in a later run via from_dict() + rebase().

classmethod from_dict(data)

Rebuild a PointerPath from to_dict() output. Numeric fields accept either hex strings ("0x158") or plain ints.

Comparison & display

recipe()

The ASLR-independent identity of this path: (module, module_offset, offsets). Two paths from different runs describe the same pointer when their recipes are equal.

__str__()

Cheat Engine-style textual representation, e.g.:

"game.exe"+0x10F4F4 -> [+0x0] -> +0x158

Example workflows

Saving and reloading

paths = list(process.scan_pointer_paths(address))
process.save_pointer_paths(paths, "pointers.json")

# Later, in a different run:
loaded = process.load_pointer_paths("pointers.json")
for path in loaded:
    live = path.rebase(process)
    print(live.resolve(process))

Building a live handle from a saved path

loaded = process.load_pointer_paths("pointers.json")
hp_ptr = loaded[0].rebase(process).to_pointer(process, pytype=int, bufflength=4)
hp_ptr.value = 9999

Intersecting independent scans

stable = process.compare_pointer_scans(
    "scan1.json", "scan2.json", "scan3.json",
)
print(f"{len(stable)} stable pointers")