8cba70dc90
- Add 8 files present in docker/fifa17-python/tools but missing from the top-level tree: fut_accounts.py + 7 test_*.py contracts (all committed in the server's docker tree; byte-identical to the running image). - Preserve newer responder work already matching the running container: utas_server.py (offlineSeason), lsx_responder_v2.py (OPENFUT_BIND), blaze_responder_v3b.py, autopatch.py, pow_server.py, fut_store.py, test_fut_contract.py, fifa17-hook-m1.sh. - Add 30 newer ghidra_queries (draft purchase/state, SBC 9-26, runtime registries). Local tree is now a strict superset of B with all shared files byte-identical.
62 lines
2.3 KiB
Python
62 lines
2.3 KiB
Python
"""Continue the SBC response handoff analysis after the 2026-08-07 passive trace.
|
|
|
|
Proven live boundary:
|
|
request +0x80 factory -> response 0x18022e5b0
|
|
response +0x08 -> 0x18017b2b0 returns true
|
|
request +0x90 -> parsed response callback returns normally
|
|
request +0x88 -> ownership transfer returns normally
|
|
|
|
The next unknown is the receiving owner's virtual +0x18 consumer called by
|
|
0x1801631e0. Recover the concrete receiver, its vtable, and downstream publication.
|
|
"""
|
|
import traceback
|
|
|
|
try:
|
|
def dump_function(a, label):
|
|
f = func(a)
|
|
print("\n=== %s @%#x (%s) ===" % (label, a, f.getName() if f else "?"))
|
|
if f:
|
|
print("entry=%s body=%s" % (f.getEntryPoint(), f.getBody()))
|
|
print(dec(a))
|
|
|
|
def dump_instructions(a, before=0, count=80):
|
|
f = func(a)
|
|
print("\n=== instructions around %#x ===" % a)
|
|
if not f:
|
|
return
|
|
rows = []
|
|
for ad in f.getBody().getAddresses(True):
|
|
ins = listing.getInstructionAt(ad)
|
|
if ins:
|
|
rows.append(ins)
|
|
pivot = next((i for i, ins in enumerate(rows)
|
|
if int(ins.getAddress().getOffset()) >= a), 0)
|
|
for ins in rows[max(0, pivot-before):pivot+count]:
|
|
print(" %s %s" % (ins.getAddress(), ins))
|
|
|
|
dump_function(0x1801631e0, "post-request ownership handoff / owner consumer")
|
|
dump_instructions(0x1801631e0, count=120)
|
|
|
|
print("\n=== callers/xrefs of 0x1801631e0 ===")
|
|
for ent, name in callers(0x1801631e0):
|
|
print(" caller %#x %s" % (ent, name))
|
|
print(dec(ent))
|
|
for frm, typ, name, ent in xrefs_to(0x1801631e0):
|
|
print(" xref from=%#x type=%s fn=%s entry=%#x" %
|
|
(frm, typ, name, ent))
|
|
|
|
request_vtable = 0x18022e5c0
|
|
print("\n=== category request vtable %#x ===" % request_vtable)
|
|
for off, target, name in vtable(request_vtable, 40):
|
|
print(" +%#04x -> %#x %s" % (off, target, name))
|
|
|
|
for slot, label in ((0x80, "typed factory"),
|
|
(0x88, "ownership transfer"),
|
|
(0x90, "completion callback")):
|
|
target = qword(request_vtable + slot)
|
|
dump_function(target, "request %s slot +%#x" % (label, slot))
|
|
dump_instructions(target, count=100)
|
|
|
|
except Exception:
|
|
traceback.print_exc()
|