diff --git a/fifa17-recon/tools/live/census_clubitems.py b/fifa17-recon/tools/live/census_clubitems.py new file mode 100755 index 0000000..16efc01 --- /dev/null +++ b/fifa17-recon/tools/live/census_clubitems.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +"""Read-only census of FIFA 17's RESIDENT club-item vector. + +Chain, every link from CardsDLL static RE: + [CardsDLL+0x2e6398] -> owner object (FUN_18011a830) + owner->vtable[0x4e8] -> getter returning mgr (call *0x4e8(%rdx)) + mgr+0x108 .. mgr+0x110 -> club-item vector, stride 24 + element+0x10 -> the item record pointer (FUN_1800d73d0) + record+0x4c cardtype (derived from cardsubtypeid by FUN_1800d8330: 9/10/11 -> 7) + record+0x50 cardsubtypeid + record+0x5c itemState (101 activeHomeKit, 102 activeAwayKit) + record+0x60 category (clone driver FUN_1801c3480 requires 4) + record+0x94 teamid + record+0xba teamkittypetechid (u16) +Offsets not in that list are labelled UNVERIFIED and only dumped raw. +No writes. Ever. +""" +import re, struct, sys, collections + +PID = int(sys.argv[1]) if len(sys.argv) > 1 else 44405 +mem = open(f"/proc/{PID}/mem", "rb", buffering=0) + +def rd(a, n): + mem.seek(a); return mem.read(n) +def q(a): + return struct.unpack(" 48 8b 81 off32 c3 or 48 8b 41 off8 c3 +b = rd(getter, 12) +mgr = None +if b[0:3] == bytes.fromhex("488d81"): + off = struct.unpack_from(" mgr = {mgr:#x}") +elif b[0:3] == bytes.fromhex("488d41"): + off = b[3]; mgr = owner + off + print(f" getter returns owner+{off:#x} (EMBEDDED subobject) -> mgr = {mgr:#x}") +elif b[0:3] == bytes.fromhex("488b81"): + off = struct.unpack_from(" mgr = {mgr:#x}") +elif b[0:3] == bytes.fromhex("488b41"): + off = b[3]; mgr = q(owner + off) + print(f" getter returns [owner+{off:#x}] -> mgr = {mgr:#x}") +elif b[0:2] == bytes.fromhex("488b") and b[2] == 0xc1: + mgr = owner; print(" getter returns owner itself") +else: + print(" getter shape unrecognised; trying owner as mgr") + mgr = owner + +for label, mgr_try in (("resolved", mgr), ("owner", owner)): + try: + beg, end = q(mgr_try + 0x108), q(mgr_try + 0x110) + except OSError: + print(f" [{label}] +0x108/0x110 unreadable"); continue + if not (0 < beg <= end) or (end - beg) % 24 or (end - beg) > 24*100000: + print(f" [{label}] vector implausible: {beg:#x}..{end:#x}") + continue + n = (end - beg) // 24 + print(f"\n === club-item vector via {label}: {beg:#x}..{end:#x} {n} slot(s) ===") + hist = collections.Counter(); rows = [] + for k in range(n): + try: + rec = q(beg + k*24 + 0x10) + except OSError: + continue + if not rec: + hist[("", None)] += 1; continue + try: + r = rd(rec, 0xC0) + except OSError: + continue + if len(r) < 0xC0: continue + ct, sub, st, cat = i32(r,0x4c), i32(r,0x50), i32(r,0x5c), i32(r,0x60) + team = i32(r,0x94); kt = struct.unpack_from("14} {'ctype':>5} {'subtype':>7} {'state':>5} {'cat':>4} {'team':>5} {'kittype':>7}") + for rec, ct, sub, st, cat, team, kt in rows[:12]: + print(f" {rec:#14x} {ct:>5} {sub:>7} {st:>5} {cat:>4} {team:>5} {kt:>7}") + break