#!/usr/bin/env python3 """Classify call sites of the 130000/130001 provider stubs. A call whose result is COMPARED implements a predicate ("is this the FUT custom club?"). Only a call whose result is STORED can assign a team id. This turns an unreadable 81-site list into the handful that could actually introduce 130000 into a struct. classify_calls.py [more_targets...] """ import re import sys asm = sys.argv[1] targets = [t.lower().lstrip("0x") for t in sys.argv[2:]] lines = [] for l in open(asm, errors="replace"): m = re.match(r"\s*([0-9a-f]+):\s+((?:[0-9a-f]{2} )+)\s*(.*)", l) if m: lines.append((int(m.group(1), 16), m.group(3).strip())) idx = {a: i for i, (a, _t) in enumerate(lines)} STORE = re.compile(r"^mov\s+(?:DWORD PTR |QWORD PTR )?\[[^\]]+\],(eax|rax)\b") CMP = re.compile(r"^(cmp|sub|test)\b.*\b(eax|rax)\b") MOVREG = re.compile(r"^mov\s+(e[a-z]{2}|r\d+d|r[a-z]{2}),(eax|rax)\b") for tgt in targets: print(f"\n ===== callers of 0x{tgt} =====") stores, cmps, other = [], [], [] for i, (a, txt) in enumerate(lines): if not txt.startswith("call") or tgt not in txt: continue # look at the next few instructions for the fate of eax window = [lines[j][1] for j in range(i + 1, min(i + 7, len(lines)))] verdict, detail = "other", window[0] if window else "" for w in window: if STORE.match(w): verdict, detail = "STORE", w break if CMP.match(w): verdict, detail = "compare", w break if MOVREG.match(w): verdict, detail = "movreg", w break rec = (a, detail) (stores if verdict == "STORE" else cmps if verdict == "compare" else other).append(rec) print(f" STORE (can assign) : {len(stores)}") for a, d in stores: print(f" 0x{a:x} {d}") print(f" compare (predicate) : {len(cmps)}") print(f" other/moved to reg : {len(other)}") for a, d in other[:14]: print(f" 0x{a:x} {d}")