78 lines
3.4 KiB
Python
78 lines
3.4 KiB
Python
"""DIMENSION 2 Q3/Q4 finish: draft-tile gating in FUN_1800b2680; SBC/Objectives
|
|
accessor slots and whether any native code reads them.
|
|
|
|
HYPOTHESIS: IS_DRAFT_MODE_ENABLED (cVar7) gates whether the draft hub tile is drawn
|
|
/ enabled. SBC(0x1fd2c,0x1fd42) and Objectives(0x1fd44) have accessor stubs at some
|
|
model vtable slots but NO IS_* publisher name; either a vtable-slot caller reads
|
|
them or they are consumed only by their own accessor (i.e. no native mode gate).
|
|
|
|
CONTROL: draft accessor is model slot 0x2c8 (proven). Walking the vtable and
|
|
matching disp must reproduce 0x2c8->0x1fd3d and 0x270->0x1fd2e.
|
|
"""
|
|
import traceback
|
|
try:
|
|
MODEL_VT = 0x18021c2a0
|
|
# find slots for the SBC/objectives displacements by walking vtable
|
|
want = {0x1fd2c: "allowUntradeableForSBC", 0x1fd42: "allowGracePeriodForSBC",
|
|
0x1fd44: "enableObjectives", 0x1fd28: "sbcGracePeriodMinutes",
|
|
0x1fd3e: "offlineDraft(0x2d0?)", 0x1fd2e: "trading(ctl)",
|
|
0x1fd3d: "draft(ctl)"}
|
|
slot_for_disp = {}
|
|
print("=" * 70)
|
|
print("vtable walk: slot -> accessor disp (0x200..0x340)")
|
|
print("=" * 70)
|
|
for slot in range(0x200, 0x340, 8):
|
|
tgt = qword(MODEL_VT + slot)
|
|
if not (0x180000000 <= tgt < 0x181000000):
|
|
continue
|
|
stub = read_bytes(tgt, 8)
|
|
disp = None
|
|
if stub[0:3] == b"\x0f\xb6\x81" and stub[7] == 0xc3:
|
|
disp = int.from_bytes(stub[3:7], "little")
|
|
elif stub[0:2] == b"\x8b\x81" and stub[6] == 0xc3:
|
|
disp = int.from_bytes(stub[2:6], "little")
|
|
if disp in want:
|
|
slot_for_disp[disp] = slot
|
|
print("slot +%#05x -> %#011x disp %#x %s"
|
|
% (slot, tgt, disp, want[disp]))
|
|
|
|
# scan slot-callers for the objectives + SBC slots
|
|
print()
|
|
print("=" * 70)
|
|
print("slot-call readers for SBC/Objectives accessor slots")
|
|
print("=" * 70)
|
|
call_modrm = [0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97]
|
|
for disp in (0x1fd44, 0x1fd2c, 0x1fd42):
|
|
slot = slot_for_disp.get(disp)
|
|
if slot is None:
|
|
print("\ndisp %#x: no vtable slot found in range" % disp)
|
|
continue
|
|
pat_disp = slot.to_bytes(4, "little")
|
|
found = []
|
|
for pre in ([], [0x41]):
|
|
for mrm in call_modrm:
|
|
pat = bytes(pre + [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("\ndisp %#x slot +%#05x %-24s (%d call-site(s))"
|
|
% (disp, slot, want[disp], len(found)))
|
|
for h, fn, ent in found:
|
|
print(" %#011x in %-16s (%#x)" % (h, fn, ent))
|
|
|
|
# rest of the draft hub-tile builder: how cVar7/8/9 gate the tile
|
|
print()
|
|
print("=" * 70)
|
|
print("FUN_1800b2680 draft/tile gating region (search cVar / DRAFT in decompile)")
|
|
print("=" * 70)
|
|
d = dec(0x1800b2680)
|
|
lines = d.splitlines()
|
|
for i, ln in enumerate(lines):
|
|
if any(k in ln for k in ("cVar7", "cVar8", "cVar9", "DRAFT", "0x70", "0x60",
|
|
"DESTINATION", "GOTO_", "SBC", "OBJECTIVE", "case 0xc",
|
|
"caseD_")):
|
|
print("%4d: %s" % (i, ln.strip()))
|
|
except Exception:
|
|
traceback.print_exc()
|