#!/usr/bin/env python3
"""Dump the DECRYPTED FIFA17 login machinery from live /proc/PID/mem.

FIFA17.exe's .data-region code is packed/encrypted on disk (objdump of the file is
garbage); the real instructions only exist decrypted in memory at runtime. This grabs
generous windows around the known login-path VAs (from prior live recon) plus the
resolved OriginMgr / session objects, then disassembles each window at its true VA so a
follow-up reversing pass works on real code.

Run while FIFA17 is running (ptrace_scope=0). Output -> ./login_dump/ + manifest.txt.
"""
import glob, os, subprocess, struct, sys

OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "login_dump")
os.makedirs(OUT, exist_ok=True)

# (name, VA, bytes_before, bytes_after) — code windows around the login machinery.
CODE = [
    ("dispatch_case2",      0x146f1e080, 0x120, 0x180),  # event dispatcher; case-2 sets m_isLoggedIn
    ("event_matcher",       0x147102880, 0x40,  0x400),  # sender/element matcher
    ("login_parser",        0x147138660, 0x40,  0x400),  # <Login> element parser (reads IsLoggedIn)
    ("loginstate_pclogin",  0x1471b58e0, 0x40,  0x600),  # LoginStatePCLogin entry
    ("txt_not_login_ebisu", 0x1471b5b00, 0x40,  0x400),  # TXT_NOT_LOGIN_TO_EBISU write site(s)
    ("pclogin_callsite",    0x1471b6780, 0x60,  0x120),  # session-object call: ff 50 60 (vtbl+0x60)
]

# Data pointers to resolve (name, ptr_VA, deref_chain_offsets, dump_span).
# We read *[ptr_VA], then optionally add offsets, then dump `span` bytes there.
DATA = [
    ("originmgr",     0x1448acf50, [],          0x80),   # OriginMgr; m_isLoggedIn @+0x13, loginError @+0x14
    ("online_flags",  0x1448a3ac0, [],          0x40),   # "internet reachable" byte lives here
    ("auth_block",    0x1448a3b20, [0x4e98],    0x60),   # auth slots +0x08/+0x10
    ("session_obj",   0x144b86bf8, [],          0x80),   # LoginStatePCLogin session object (vtbl @+0)
]

def find_pid():
    for d in glob.glob('/proc/[0-9]*'):
        try:
            if open(d + '/comm').read().strip() == 'FIFA17.exe':
                return int(os.path.basename(d))
        except Exception:
            pass
    return None

def read(f, va, n):
    f.seek(va); return f.read(n)

def rd_u64(f, va):
    b = read(f, va, 8)
    return struct.unpack('<Q', b)[0] if len(b) == 8 else 0

def disasm(path, va):
    asm = path + ".asm"
    with open(asm, "w") as out:
        subprocess.run(["objdump", "-D", "-b", "binary", "-m", "i386:x86-64",
                        "-M", "intel", "--adjust-vma=%#x" % va, path],
                       stdout=out, stderr=subprocess.DEVNULL)
    return asm

def main():
    pid = find_pid()
    if not pid:
        print("FIFA17.exe not running — launch it first."); sys.exit(1)
    man = open(os.path.join(OUT, "manifest.txt"), "w")
    man.write("FIFA17 login-machinery dump  pid=%d\n\n" % pid)
    with open(f"/proc/{pid}/mem", "rb") as f:
        for name, va, before, after in CODE:
            start = va - before
            data = read(f, start, before + after)
            p = os.path.join(OUT, f"{name}_{start:x}.bin")
            open(p, "wb").write(data)
            disasm(p, start)
            line = f"CODE  {name:22s} window {start:#x}..{start+len(data):#x} ({len(data)}B) -> {os.path.basename(p)}[.asm]"
            print(line); man.write(line + "\n")
        man.write("\n")
        for name, ptr, chain, span in DATA:
            base = rd_u64(f, ptr)
            addr = base
            trail = f"*[{ptr:#x}]={base:#x}"
            for off in chain:
                nxt = rd_u64(f, addr + off) if off and base else base
                # for a single deref-with-offset we dump AT base+off, not deref again:
                addr = base
            target = base + (chain[0] if chain else 0)
            data = read(f, target, span) if base else b""
            p = os.path.join(OUT, f"{name}_{target:x}.bin")
            open(p, "wb").write(data)
            # hex preview
            hexp = " ".join("%02x" % x for x in data[:0x40])
            line = f"DATA  {name:22s} {trail} dump@{target:#x} ({len(data)}B) -> {os.path.basename(p)}\n      first64: {hexp}"
            print(line); man.write(line + "\n")
    man.close()
    print("\nWrote dumps + manifest to", OUT)

if __name__ == "__main__":
    main()
