#!/usr/bin/env python3 """Read-only live disassembler for the FIFA17 client (CardsDLL / FIFA17.exe). ldis.py [nbytes] [--exe] disassemble ldis.py --bytes [nbytes] hexdump ldis.py --map show module bases CardsDLL image base 0x180000000; FIFA17.exe image base 0x140000000. Live address = module_base + (image_va - img_base). Sections map 1:1 for both, but this is recomputed and printed so the offset trap stays visible. """ import re import subprocess import sys import tempfile PID = None CARDS_IMG = 0x180000000 EXE_IMG = 0x140000000 def pid(): global PID if PID is None: import glob, os for d in glob.glob("/proc/[0-9]*"): try: if open(os.path.join(d, "comm")).read().strip() == "FIFA17.exe": PID = int(os.path.basename(d)) break except OSError: pass if PID is None: raise SystemExit("FIFA17.exe not running") return PID def module_base(needle): """Base = the NAMED PE-header mapping for the module (Wine maps the rest anonymously, so never trust the mapping that merely CONTAINS an address).""" for l in open(f"/proc/{pid()}/maps"): if needle.lower() in l.lower(): return int(l.split("-")[0], 16) raise SystemExit(f"module {needle} not mapped") def live(va, exe=False): if exe: return module_base("FIFA17.exe") + (va - EXE_IMG) return module_base("CardsDLL") + (va - CARDS_IMG) def read(va, n, exe=False): la = live(va, exe) with open(f"/proc/{pid()}/mem", "rb", 0) as m: m.seek(la) return la, m.read(n) def main(): a = sys.argv[1:] if not a or a[0] == "--map": print(f" pid = {pid()}") print(f" CardsDLL = 0x{module_base('CardsDLL'):x} (image 0x{CARDS_IMG:x})") print(f" FIFA17.exe = 0x{module_base('FIFA17.exe'):x} (image 0x{EXE_IMG:x})") return hexdump = a[0] == "--bytes" if hexdump: a = a[1:] exe = "--exe" in a a = [x for x in a if x != "--exe"] va = int(a[0], 16) n = int(a[1]) if len(a) > 1 else 160 la, buf = read(va, n, exe) print(f" image 0x{va:x} -> live 0x{la:x} ({len(buf)} bytes)") if hexdump: for i in range(0, len(buf), 16): c = buf[i:i + 16] print(f" 0x{va+i:x}: {' '.join(f'{b:02x}' for b in c):<47} " + "".join(chr(b) if 32 <= b < 127 else "." for b in c)) return with tempfile.NamedTemporaryFile(suffix=".bin") as f: f.write(buf) f.flush() out = subprocess.run( ["objdump", "-D", "-b", "binary", "-m", "i386:x86-64", "-M", "intel", f"--adjust-vma=0x{va:x}", f.name], capture_output=True, text=True).stdout for line in out.splitlines(): if re.match(r"\s+[0-9a-f]+:", line): print(" " + line.strip()) main()