A discovered static pointer path, returned by scan_pointer_paths.
from PyMemoryEditor import PointerPathResolving 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.
.. py:class:: PointerPath(base_address, offsets, module=None, module_offset=None, ptr_size=8)
:param int base_address: absolute static base for *this* run (the slot
whose pointer the chain dereferences first).
:param Tuple[int, ...] offsets: forward-order offsets, ready to hand to
:py:meth:`resolve_pointer_chain`.
:param Optional[str] module: name of the module containing
``base_address`` (``None`` when the base falls in a caller-supplied
static range with no known module).
:param Optional[int] module_offset: ``base_address - module.base_address``
— the portable, ASLR-independent part of the base. ``None`` when
``module`` is.
:param int ptr_size: pointer width — ``8`` for 64-bit (default) or ``4``
for 32-bit.
PointerPath is a @dataclass(frozen=True) — instances are immutable and
hashable.
| Attribute | Type | Meaning |
|---|---|---|
base_address | int | Absolute static base for the run the path was found in. |
offsets | Tuple[int, ...] | Forward-order offsets. |
module | Optional[str] | Module owning base_address. |
module_offset | Optional[int] | base_address - module.base_address. |
ptr_size | int | Pointer width (4 or 8). |
.. py:method:: resolve(process)
Walk this path in ``process`` and return the final target address.
:param AbstractProcess process: the open process.
:returns: the target address (``int``).
.. py:method:: to_pointer(process, *, pytype=int, bufflength=None)
Build a live :py:class:`RemotePointer` for the value at the end of this
path.
:returns: a :py:class:`RemotePointer` re-resolving on every access.
.. py:method:: 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.
:raises LookupError: the module is not loaded in ``process``.
.. py:method:: 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
:py:meth:`from_dict` + :py:meth:`rebase`.
.. py:classmethod:: from_dict(data)
Rebuild a :py:class:`PointerPath` from :py:meth:`to_dict` output. Numeric
fields accept either hex strings (``"0x158"``) or plain ints.
.. py:method:: 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.
.. py:method:: __str__()
Cheat Engine-style textual representation, e.g.::
"game.exe"+0x10F4F4 -> [+0x0] -> +0x158
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))loaded = process.load_pointer_paths("pointers.json")
hp_ptr = loaded[0].rebase(process).to_pointer(process, pytype=int, bufflength=4)
hp_ptr.value = 9999stable = process.compare_pointer_scans(
"scan1.json", "scan2.json", "scan3.json",
)
print(f"{len(stable)} stable pointers")- [Pointer scan guide](../guide/pointer-scan.md)
- [`RemotePointer`](remote-pointer.md)