diff --git a/fifa17-recon/tools/live/probe_resident_fields.py b/fifa17-recon/tools/live/probe_resident_fields.py new file mode 100755 index 0000000..c707135 --- /dev/null +++ b/fifa17-recon/tools/live/probe_resident_fields.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Read-only dump of RESIDENT record fields, for both the player and club-item vectors. + +Purpose: the kit clone driver FUN_1801c3480 gates on record+0x60 (category) == 4. +No instruction in CardsDLL writes immediate 4 there, so this reads what value a +genuinely resident record actually carries. Read-only. Never writes. + + mgr+0x0c0 cardtype-2 single slot + mgr+0x0d8..0x0e0 cardtype-1 (player) vector + mgr+0x108..0x110 club-item vector + record+0x4c cardtype +0x50 cardsubtypeid +0x5c itemState + record+0x60 category +0x94 teamid +0xba teamkittypetechid (u16) +""" +import re, struct, sys, collections + +PID = int(sys.argv[1]) +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(" 24 * 100000: + print(" implausible vector, skipping") + continue + # resolve stride: the element must contain a plausible heap pointer + for stride, ptr_off in ((24, 0x10), (16, 0x08), (8, 0x00)): + if span % stride: + continue + n = span // stride + recs, nulls = [], 0 + ok = True + for k in range(n): + try: + rec = q(beg + k * stride + ptr_off) + except OSError: + ok = False; break + if not rec: + nulls += 1; continue + d = decode(rec) + if d is None: + ok = False; break + recs.append((rec, d)) + if not ok: + continue + print(f" stride {stride} (ptr at +{ptr_off:#x}): {n} slots, {len(recs)} populated, {nulls} null") + if not recs and nulls != n: + continue + hist = collections.Counter(d[0:2] for _, d in recs) + for key, c in sorted(hist.items(), key=lambda x: -x[1]): + print(f" (cardtype,subtype)={key} x{c}") + print(f" {'ptr':>14} " + " ".join(f"{f:>8}" for f in FIELDS)) + for rec, d in recs[:8]: + print(f" {rec:#14x} " + " ".join(f"{v:>8}" for v in d)) + cats = collections.Counter(d[3] for _, d in recs) + if cats: + print(f" CATEGORY (+0x60) distribution: {dict(cats)}") + break