#!/usr/bin/env python3 """Decode the running-sum atom ladders in /hub parser FUN_180139610 and name each atom from docs/fut_atoms.tsv. The dispatch is `sub ecx,d0 / sub ecx,d1 / .../ cmp ecx,dN`: the atom that each branch handles is the CUMULATIVE sum of the deltas up to and including that step (a jz after each sub tests atom==running_sum). Plus there are direct `cmp esi,imm`. """ import subprocess, re DLL = "/tmp/fut/cardsdll.dll" TSV = "/home/alex/Documents/OpenFUT/fifa17-recon/docs/fut_atoms.tsv" FUNC, STOP = 0x180139610, 0x18013e600 atoms = {} for line in open(TSV): p = line.rstrip("\n").split("\t") if len(p) >= 3: try: atoms[int(p[1], 16)] = p[2] except ValueError: pass out = subprocess.check_output( ["objdump", "-d", "-M", "intel", "--start-address=%#x" % FUNC, "--stop-address=%#x" % STOP, DLL], text=True) # linear list of (addr, mnem, dest_reg, imm) for sub/cmp on 32-bit regs, stop at int3 pad seq = [] int3 = 0 for ln in out.splitlines(): parts = ln.split("\t") if len(parts) < 3: continue addr_s = parts[0].strip().rstrip(":") try: addr = int(addr_s, 16) except ValueError: continue instr = parts[2].strip() bits = instr.split(None, 1) mnem = bits[0] ops = bits[1].strip() if len(bits) > 1 else "" if mnem == "int3": int3 += 1 if int3 >= 4: break continue int3 = 0 mo = re.match(r"(e?[a-d]x|e?si|e?di|e?bp|r\d+d?),\s*(0x[0-9a-f]+)$", ops) if mnem in ("sub", "cmp") and mo: seq.append((addr, mnem, mo.group(1), int(mo.group(2), 16))) # walk ladders: consecutive sub/cmp on the SAME register form one ladder; the running # sum at each element is the atom that element dispatches. A `cmp` closes the ladder. found = {} # atom -> (addr, kind) i = 0 while i < len(seq): addr, mnem, reg, imm = seq[i] # a ladder starts on a sub if mnem == "sub": run = 0 j = i while j < len(seq) and seq[j][2] == reg and seq[j][1] in ("sub", "cmp"): run += seq[j][3] found.setdefault(run, (seq[j][0], "ladder")) if seq[j][1] == "cmp": j += 1 break j += 1 i = j else: # a lone cmp reg,imm on an atom-holding reg is a direct atom test if 0 < imm <= 0x400: found.setdefault(imm, (addr, "direct")) i += 1 TOKENS = {0x1, 0x6, 0x7, 0x9, 0xa, 0xb, 0xc, 0xd} # SAX token enum, not atoms print("Atoms dispatched by hub parser FUN_%#x:" % FUNC) print("=" * 70) for a in sorted(found): if a in TOKENS: continue tag = " <-- TOKEN?" if a < 0x10 else "" print(" %#06x %-28s (%s @ %#x)%s" % (a, atoms.get(a, "?"), found[a][1], found[a][0], tag)) print("\nKnown tile counters for reference: 0x33=auctionCount, 0x90=clubPlayers") print("\nName-based tile-count candidates:") KEYS = ("sell","sold","trade","auction","pile","list","count","num","offer", "won","outbid","target","watch","transfer","active","unassigned") for a in sorted(found): if a in TOKENS: continue n = atoms.get(a, "").lower() if any(k in n for k in KEYS): print(" %#06x %s" % (a, atoms.get(a, "?")))