#!/usr/bin/env python3 """Dump the guest's DECOMPRESSED executable image out of live Xenia memory. Why this exists: the static PPC route the corpus is built on ran against a disassembly database at `/work/xenia-rs/sylpheed.db`, and that file is **not in this container** — the same migration that took the Xenia storage root. Without it, every finding that cites a `sub_82xxxxxx` is unre-checkable. `/disc/default.xex` cannot substitute: it is encrypted and LZX-compressed. Its header is intact (`XEX2`, original PE name `default.pe`) and everything after is noise — `strings` finds **zero** occurrences of `GamePart` in it. Xenia decompresses, decrypts and relocates the image at load, so a running guest holds exactly the flat VA image the corpus calls the `.pe`. Dump it once and the static route works offline, with no emulator and no disc. Validated on write, and both checks are the corpus's own, not this tool's: * `0x820A1630` must hold the **GamePart id table** — 29 pointers into `.rdata` resolving to `GP_TITLE` … `GP_TEST`, with `GP_CHALLENGE` at id 26 (docs/re/challenge-mission-gate.md); * the image must contain the Xbox 360 D3D runtime's own error strings, which a mis-based or partial dump does not. dump_image.py [OUT.pe] # with Canary running """ import os import struct import sys SD = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, SD) import gmem # noqa: E402 LO, HI = 0x82000000, 0x82400000 BASE = LO EXPECT = {0: "GP_TITLE", 3: "GP_LOAD", 11: "GP_READY_ROOM", 26: "GP_CHALLENGE", 28: "GP_TEST"} def validate(buf): def s_at(va): o = va - BASE e = buf.find(b"\0", o, o + 64) return buf[o:e].decode("ascii", "replace") bad = [] for i, want in EXPECT.items(): p = struct.unpack_from(">I", buf, 0x820A1630 - BASE + 4 * i)[0] got = s_at(p) if LO <= p < HI else f"" if got != want: bad.append(f"GamePart id {i}: expected {want!r}, got {got!r}") if buf.count(b"ERR[D3D]") < 1: bad.append("no Xbox 360 D3D runtime strings — this is not the game image") return bad def main(out): path = gmem.mem_path() off = gmem.va_to_off(LO) with open(path, "rb") as f: f.seek(off) buf = f.read(HI - LO) if len(buf) < HI - LO: print(f"short read: {len(buf)} of {HI - LO}", file=sys.stderr) return 1 bad = validate(buf) for b in bad: print("FAIL:", b, file=sys.stderr) if bad: return 2 open(out, "wb").write(buf) pages = sum(1 for i in range(0, len(buf), 4096) if any(buf[i:i + 4096])) print(f"wrote {out} {len(buf)} bytes VA {LO:#x}..{HI:#x}") print(f"validated: GamePart id table + D3D runtime strings; " f"{pages}/{len(buf)//4096} non-empty 4K pages") return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1] if len(sys.argv) > 1 else "/sylph-home/re/sylpheed-image.pe"))