wip: checkpoint FIFA 17 SBC research for Windows migration
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""DIMENSION 2 Q1/Q3: slot->disp resolution + READER search per gate byte.
|
||||
|
||||
HYPOTHESIS: each publisher slot is an accessor stub `0f b6 81 <disp32> c3`
|
||||
(movzx eax,byte[rcx+disp]; ret) at model vtable 0x18021c2a0. For the refusing
|
||||
modes (season/draft/tournament), the ONLY reader of the gate byte is the publisher
|
||||
FUN_18006cc60, which hands the value to the script layer -- i.e. no native mode gate.
|
||||
|
||||
CONTROL: slot 0x270 must decode to disp 0x1fd2e (trading), already proven by two
|
||||
prior docs. Reader scan must find FUN_18011dc50 (applier, WRITES 0x1fd2e) and
|
||||
FUN_1801a7260 (TO_TRADE_PILE predicate, READS 0x1fd2e) among the disp-32 hits for
|
||||
0x1fd2e -- both known, so if either is missing the scan form is wrong.
|
||||
"""
|
||||
import traceback
|
||||
try:
|
||||
MODEL_VT = 0x18021c2a0
|
||||
slots = {
|
||||
0x270: "IS_TRADING_ENABLED",
|
||||
0x280: "IS_STORE_ENABLED",
|
||||
0x2b0: "IS_FRIENDLY_SEASON_ENABLED",
|
||||
0x2b8: "IS_TOURNAMENT_QUIT_ENABLED",
|
||||
0x2c0: "IS_PROCESSING_STATE_ENABLED",
|
||||
0x2c8: "IS_DRAFT_MODE_ENABLED",
|
||||
0x2d8: "IS_STORY_MODE_REWARD_ENABLED",
|
||||
0x2f0: "IS_RETURNING_USER_REWARDS_SCREEN_ENABLED",
|
||||
}
|
||||
|
||||
print("=" * 70)
|
||||
print("SLOT -> accessor -> displacement (model offset)")
|
||||
print("=" * 70)
|
||||
disp_by_name = {}
|
||||
for slot in sorted(slots):
|
||||
tgt = qword(MODEL_VT + slot)
|
||||
stub = read_bytes(tgt, 8)
|
||||
disp = None
|
||||
# 0f b6 81 <disp32> c3 -> movzx eax, byte [rcx+disp32] ; ret
|
||||
if stub[0:3] == b"\x0f\xb6\x81" and stub[7] == 0xc3:
|
||||
disp = int.from_bytes(stub[3:7], "little")
|
||||
# 8b 81 <disp32> c3 -> mov eax, [rcx+disp32] ; ret (int getter, 4-byte)
|
||||
elif stub[0:2] == b"\x8b\x81" and stub[6] == 0xc3:
|
||||
disp = int.from_bytes(stub[2:6], "little")
|
||||
name = slots[slot]
|
||||
disp_by_name[name] = disp
|
||||
print("slot +%#05x %-42s -> %#011x stub=%s disp=%s"
|
||||
% (slot, name, tgt, stub.hex(),
|
||||
("%#x" % disp) if disp is not None else "??"))
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
print("READERS: .text hits for each displacement (raw disp32 LE, form-agnostic)")
|
||||
print("catches movzx/mov/cmp/lea/setcc in every encoding")
|
||||
print("=" * 70)
|
||||
for name, disp in disp_by_name.items():
|
||||
if disp is None:
|
||||
continue
|
||||
pat = disp.to_bytes(4, "little")
|
||||
hits = find_all(pat, blocks=(".text",))
|
||||
print("\n%-42s disp %#x (%d hit(s))" % (name, disp, len(hits)))
|
||||
for h in hits:
|
||||
f = fm.getFunctionContaining(addr(h))
|
||||
fn = f.getName() if f else "?"
|
||||
ent = int(f.getEntryPoint().getOffset()) if f else 0
|
||||
ins = listing.getInstructionAt(addr(h - 3)) or listing.getInstructionAt(addr(h - 2)) or listing.getInstructionAt(addr(h))
|
||||
print(" %#011x in %-16s (%#x) ins~ %s"
|
||||
% (h, fn, ent, str(ins) if ins else "?"))
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
print("READERS via vtable slot call: .text scan for call [reg+slot] (ff /2 disp32)")
|
||||
print("=" * 70)
|
||||
# FF /2 with mod=10 (disp32): modrm 0x90..0x97 (rax..rdi), 0x94 needs SIB
|
||||
call_modrm = [0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97]
|
||||
for slot in sorted(slots):
|
||||
pat_disp = slot.to_bytes(4, "little")
|
||||
found = []
|
||||
for mrm in call_modrm:
|
||||
pat = bytes([0xff, mrm]) + pat_disp
|
||||
for h in find_all(pat, blocks=(".text",)):
|
||||
f = fm.getFunctionContaining(addr(h))
|
||||
found.append((h, f.getName() if f else "?",
|
||||
int(f.getEntryPoint().getOffset()) if f else 0))
|
||||
# also REX.W/B variants (41 ff /2, 48/49 not valid for call reg-indirect but include 41)
|
||||
for rex in (0x41,):
|
||||
for mrm in [0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97]:
|
||||
pat = bytes([rex, 0xff, mrm]) + pat_disp
|
||||
for h in find_all(pat, blocks=(".text",)):
|
||||
f = fm.getFunctionContaining(addr(h))
|
||||
found.append((h, f.getName() if f else "?",
|
||||
int(f.getEntryPoint().getOffset()) if f else 0))
|
||||
print("\nslot +%#05x %-42s (%d call-site(s))" % (slot, slots[slot], len(found)))
|
||||
for h, fn, ent in found:
|
||||
print(" %#011x in %-16s (%#x)" % (h, fn, ent))
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
Reference in New Issue
Block a user