fifa17-python: commit working FUT backend deployment (client/server split)
Freeze the running offline FUT backend into version control as fifa17-recon/docker/fifa17-python/ - declarative and rebuildable from a fresh checkout: * OPENFUT_BIND / OPENFUT_ADVERTISE client/server split in the responders (lsx, blaze, roster, utas, pow) + entrypoint.sh; OPENFUT_ADVERTISE is required for remote mode (compose and entrypoint fail without it) * docker-compose.yml reproducing the frozen baseline container exactly (env, ports incl. the 8085->8080 POW-content remap, /state bind, restart) * .env.example / .env for site config - the LAN IP is never hardcoded in source * tools/ + data/ staged from openfut-fut-backend:python-baseline-2026-08-10, verified byte-identical to the running container at freeze time * client_arm.sh (the 105 client-side arming counterpart) * Dockerfile bakes /app/SHA256SUMS.txt so any image is self-identifying * docs/BASELINE-python-2026-08-10.md: frozen image/container/hash record, restore instructions and rebuild-equivalence procedure Secrets (redir key/cert, .env) and runtime state (docker/state) stay gitignored. The live container is untouched pending the .105 launcher audit.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pseudo-backtrace from a minidump: walk the faulting thread's stack and report
|
||||
every qword that points into a loaded module's code (i.e. plausible return
|
||||
addresses), innermost first."""
|
||||
import struct, sys
|
||||
|
||||
p = sys.argv[1]
|
||||
d = open(p, "rb").read()
|
||||
nstreams, dirrva = struct.unpack_from("<II", d, 8)
|
||||
streams = {}
|
||||
for i in range(nstreams):
|
||||
st, size, rva = struct.unpack_from("<III", d, dirrva + i * 12)
|
||||
streams.setdefault(st, []).append((size, rva))
|
||||
|
||||
|
||||
def mstring(rva):
|
||||
(ln,) = struct.unpack_from("<I", d, rva)
|
||||
return d[rva + 4: rva + 4 + ln].decode("utf-16-le", "replace")
|
||||
|
||||
|
||||
mods = []
|
||||
size, rva = streams[4][0]
|
||||
(n,) = struct.unpack_from("<I", d, rva)
|
||||
off = rva + 4
|
||||
for i in range(n):
|
||||
base, sz, csum, ts, nrva = struct.unpack_from("<QIIII", d, off)
|
||||
mods.append((base, sz, mstring(nrva).split("\\")[-1]))
|
||||
off += 108
|
||||
mods.sort()
|
||||
|
||||
# faulting thread + exception address
|
||||
size, rva = streams[6][0]
|
||||
tid, _ = struct.unpack_from("<II", d, rva)
|
||||
code, flags, recptr, exc_addr, nparams = struct.unpack_from("<IIQQI", d, rva + 8)
|
||||
|
||||
|
||||
def whose(a):
|
||||
for base, sz, name in mods:
|
||||
if base <= a < base + sz:
|
||||
return name, a - base
|
||||
return None, 0
|
||||
|
||||
|
||||
# thread list
|
||||
size, rva = streams[3][0]
|
||||
(nthreads,) = struct.unpack_from("<I", d, rva)
|
||||
off = rva + 4
|
||||
target = None
|
||||
for i in range(nthreads):
|
||||
t_id, susp, pcls, prio, teb, stk_start, stk_size, stk_rva, ctx_size, ctx_rva = \
|
||||
struct.unpack_from("<IIIIQQIIII", d, off)
|
||||
if t_id == tid:
|
||||
target = (stk_start, stk_size, stk_rva, ctx_size, ctx_rva)
|
||||
off += 48
|
||||
|
||||
print("faulting thread %#x exception at %s+%#x" % (tid, *whose(exc_addr)))
|
||||
if not target:
|
||||
print("no stack captured for the faulting thread"); sys.exit(0)
|
||||
stk_start, stk_size, stk_rva, ctx_size, ctx_rva = target
|
||||
print("stack %#x..%#x (%d bytes)\n" % (stk_start, stk_start + stk_size, stk_size))
|
||||
|
||||
# CONTEXT_AMD64: Rsp at offset 0x98, Rip at 0xF8 (RUNTIME layout)
|
||||
if ctx_size >= 0x100:
|
||||
rsp = struct.unpack_from("<Q", d, ctx_rva + 0x98)[0]
|
||||
rip = struct.unpack_from("<Q", d, ctx_rva + 0xF8)[0]
|
||||
print("RSP=%#x RIP=%#x (%s+%#x)\n" % (rsp, rip, *whose(rip)))
|
||||
else:
|
||||
rsp = stk_start
|
||||
|
||||
print("=== plausible return addresses (innermost first) ===")
|
||||
seen, out = set(), []
|
||||
start = max(rsp - stk_start, 0)
|
||||
for o in range(int(start), stk_size - 8, 8):
|
||||
(v,) = struct.unpack_from("<Q", d, stk_rva + o)
|
||||
name, off2 = whose(v)
|
||||
if name is None:
|
||||
continue
|
||||
key = (name, off2)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
out.append((stk_start + o, name, off2))
|
||||
for i, (sa, name, off2) in enumerate(out[:45]):
|
||||
tag = ""
|
||||
if "cards" in name.lower():
|
||||
tag = " <-- CardsDLL ghidra %#x" % (0x180000000 + off2)
|
||||
print(" [%2d] %#x %s+%#x%s" % (i, sa, name, off2, tag))
|
||||
Reference in New Issue
Block a user