#!/usr/bin/env python3 """Find guest addresses holding one value in snapshot A and another in snapshot B. The classic differential search, which is how you locate a field whose ADDRESS is unknown but whose VALUE is known at two moments. Used here for the GamePart slot: `GP_EXTRAS` = 5 on the extras screen, `GP_MISSION_SELECT` = 7 on the next one, so the slot is a word that reads 5 then 7. Both snapshots are sparse copies of `/dev/shm/xenia_memory_*`, so the ~4.6 GB of holes cost nothing: walk A's allocated extents with SEEK_DATA and only look at B where A already matched. diff_words.py [--near ] """ import os import struct import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from gmem import off_to_vas # noqa: E402 def main(): if len(sys.argv) < 5: print(__doc__) return 1 pa, va_, pb, vb_ = sys.argv[1], int(sys.argv[2], 0), sys.argv[3], int(sys.argv[4], 0) pat_a = struct.pack(">I", va_) fa, fb = open(pa, "rb"), open(pb, "rb") size = min(os.path.getsize(pa), os.path.getsize(pb)) hits, scanned = [], 0 off = 0 while off < size: try: off = os.lseek(fa.fileno(), off, os.SEEK_DATA) except OSError: break if off >= size: break try: end = min(os.lseek(fa.fileno(), off, os.SEEK_HOLE), size) except OSError: end = size while off < end: n = min(1 << 22, end - off) fa.seek(off) buf = fa.read(n) scanned += len(buf) i = 0 while True: i = buf.find(pat_a, i) if i < 0: break p = off + i if p % 4 == 0: fb.seek(p) if fb.read(4) == struct.pack(">I", vb_): hits.append(p) i += 1 off += n print(f"scanned {scanned/1e6:.0f} MB of allocated guest memory") print(f"addresses reading {va_} in A and {vb_} in B: {len(hits)}") for h in hits[:40]: vas = off_to_vas(h) print(f" file off {h:#012x} guest VA {[hex(v) for v in vas][:2]}") if len(hits) > 40: print(f" … and {len(hits)-40} more") return 0 if __name__ == "__main__": raise SystemExit(main())