"""ADVERSARIAL VERIFICATION BATCH 2. Q1 COMPLETENESS GAP the D4 report admitted: are there raw, non-accessor reads of record+0xb4 anywhere in the binary? 0xb4 cannot be encoded as a signed disp8, so EVERY [reg+0xb4] reference must carry the literal disp32 bytes b4 00 00 00. Scanning .text for those four bytes and decoding the containing instruction is therefore an EXHAUSTIVE search, not a sample. Same scan for 0x54 and 0x88. Positive control: the scan must find FUN_1801a87f0 (+0xb4), FUN_180141660's ladder (+0xb4 and +0x54) and FUN_1801a85c0 (+0x88). Q2 FUN_18013f4d0 -- the family-6 handler the deser tail calls with (record, resourceId, AMOUNT). If it stores amount in the record, the standing "amount is dropped" verdict is wrong. Q3 the +0xe0 mystery: FUN_1801a8540, FUN_1800e5940 (manager publisher), FUN_1800e6e20 (player publisher) in full. Q4 FUN_180166660 itemState mapper, FUN_1800d7b50/b30/b10/af0 value readers. Q5 who calls FUN_18013fe00 and FUN_180141660 (is the ladder really on every path). """ import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/cards/adv/" try: def scan_disp(off): pat = bytes([off & 0xFF, (off >> 8) & 0xFF, (off >> 16) & 0xFF, (off >> 24) & 0xFF]) hits = find_all(pat, blocks=(".text",)) rows = [] for h in hits: ins = listing.getInstructionContaining(addr(h)) if ins is None: continue a = int(ins.getAddress().getOffset()) txt = str(ins) if ("0xb4]" in txt or "0x54]" in txt or "0x88]" in txt or hex(off) in txt.lower()): f = fm.getFunctionContaining(ins.getAddress()) rows.append((a, txt, f.getName() if f else "?")) return rows for off, label in ((0xB4, "record+0xb4 rating"), (0x54, "record+0x54 disputed"), (0x88, "record+0x88 playStyle")): rows = scan_disp(off) print("\n==== EXHAUSTIVE disp32 scan for [reg+%#x] (%s): %d instructions" % (off, label, len(rows))) seen = {} for a, txt, fn in rows: seen.setdefault(fn, []).append((a, txt)) for fn in sorted(seen): print(" %-24s" % fn, ["%#x %s" % (a, t) for a, t in seen[fn]]) bodies = [] for nm, a in (("f_13f4d0_family6", 0x18013F4D0), ("acc_1a8540", 0x1801A8540), ("acc_1a86b0", 0x1801A86B0), ("acc_1a8590_nation", 0x1801A8590), ("acc_1a86a0_team", 0x1801A86A0), ("pub_mgr_1800e5940", 0x1800E5940), ("pub_player_1800e6e20", 0x1800E6E20), ("itemstate_166660", 0x180166660), ("rd_d7b50", 0x1800D7B50), ("rd_d7b30", 0x1800D7B30), ("rd_d7b10", 0x1800D7B10), ("rd_d7af0", 0x1800D7AF0), ("stamp_d84e0", 0x1800D84E0)): f = func(a) s = dec(a, 600) bodies.append("=" * 78) bodies.append("### %s @ %#x len=%d" % (nm, a, len(s))) bodies.append(s) open(OUT + "v2_bodies.txt", "w").write("\n".join(bodies)) print("\nWROTE v2_bodies.txt") print("\n==== callers ====") for nm, a in (("FUN_18013fe00 item deser", 0x18013FE00), ("FUN_180141660 merge", 0x180141660), ("FUN_180135890 players merge", 0x180135890), ("FUN_1801a87f0 rating acc", 0x1801A87F0), ("FUN_1801a80c0 cardlevel acc", 0x1801A80C0), ("FUN_1801a85c0 playstyle acc", 0x1801A85C0), ("FUN_1801a8550 league acc", 0x1801A8550), ("FUN_1801a8540", 0x1801A8540)): xs = xrefs_to(a) cs = sorted(set("%s@%#x" % (x[2], x[3]) for x in xs if x[1].startswith("UNCONDITIONAL_CALL") or "CALL" in x[1])) print("%-30s xrefs=%d callers=%s" % (nm, len(xs), cs)) except Exception: traceback.print_exc()