"""D3 store-price q1. HYPOTHESES H1 extPrice.finalPrice (0x180139070) / originalPrice (0x18013aae0) read ONLY atom 0x11a (externalPriceId). Atoms 0x1b (amount) and 0xc4 (currency) are SKIPped. H2 The real-money price line is rendered from the Origin/Dime commerce catalog singleton FUN_1801a0040 -> DAT_1802ef5a0, looked up by externalPriceId, inside FUN_18002cc90, which early-returns when vm+0x6c == -1. H3 The pack->viewmodel adapter FUN_18002c3c0 recognises exactly three currency name literals: "mtx", "coins", "points". H4 pack record +0x78 (externalPriceId sink) is constructed to -1. CONTROL for the absence check (H1): the SAME syntactic form. I enumerate EVERY scalar operand of EVERY instruction in each function, so ==, !=, switch tables and sub/dec ladders are all covered by construction. The positive control is that the scan MUST find 0x11a in both functions and MUST find 0x124/0x134/0x1d0 in the sibling currency-element parser FUN_180138bd0, which is known to read them. """ import traceback, sys OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store" try: w = open(OUT + "/q1_raw.txt", "w") def p(*a): s = " ".join(str(x) for x in a) print(s) w.write(s + "\n") # ---------- 1. instruction-level scalar census (absence check) ---------- from ghidra.program.model.lang import OperandType def scalars(entry): f = func(entry) body = f.getBody() out = {} it = listing.getInstructions(body, True) n = 0 while it.hasNext(): ins = it.next() n += 1 for i in range(ins.getNumOperands()): for o in ins.getOpObjects(i): try: v = int(o.getValue()) except Exception: continue out.setdefault(v & 0xFFFFFFFFFFFFFFFF, []).append( (int(ins.getAddress().getOffset()), str(ins.getMnemonicString()))) return n, out p("=" * 70) p("H1 SCALAR CENSUS -- every immediate/scalar operand in the function body") for name, ent in [("finalPrice FUN_180139070", 0x180139070), ("originalPrice FUN_18013aae0", 0x18013aae0), ("CONTROL currencyElem FUN_180138bd0", 0x180138bd0)]: n, sc = scalars(ent) p("") p("--- %s : %d instructions, %d distinct scalars" % (name, n, len(sc))) for atom, label in [(0x11a, "externalPriceId"), (0x1b, "amount"), (0xc4, "currency"), (0x124, "finalFunds"), (0x134, "funds"), (0x1d0, "name"), (0xa, "active")]: hits = sc.get(atom, []) p(" atom %-6s %-16s : %s" % (hex(atom), label, ("ABSENT" if not hits else ", ".join("%x %s" % h for h in hits)))) # any indirect jump (jump table) would break the census -> report f = func(ent) it = listing.getInstructions(f.getBody(), True) ind = [] while it.hasNext(): ins = it.next() if ins.getFlowType().isJump() and ins.getFlowType().isComputed(): ind.append(hex(int(ins.getAddress().getOffset()))) p(" computed/indirect jumps in body: %s" % (ind or "NONE")) # full sorted scalar list, so nothing is hidden p(" all scalars: %s" % sorted(hex(k) for k in sc)) # ---------- 2. who consumes the viewmodel ---------- p("") p("=" * 70) p("H3 callers of the pack->viewmodel adapter FUN_18002c3c0") for frm, typ, fn, ent in xrefs_to(0x18002c3c0): p(" %-12x %-10s %s @ %x" % (frm, typ, fn, ent)) p("") p("H2 callers of the price formatter FUN_18002cc90") for frm, typ, fn, ent in xrefs_to(0x18002cc90): p(" %-12x %-10s %s @ %x" % (frm, typ, fn, ent)) # ---------- 3. the mtx sibling FUN_18002e680 ---------- p("") p("=" * 70) p("other 'mtx' consumer FUN_18002e680") src = dec(0x18002e680) p("len(src) = %d (printed IN FULL below)" % len(src)) p(src) # ---------- 4. commerce singleton ---------- p("") p("=" * 70) p("H2 DAT_1802ef5a0 (returned by FUN_1801a0040) xrefs") for frm, typ, fn, ent in xrefs_to(0x1802ef5a0): p(" %-12x %-10s %s @ %x" % (frm, typ, fn, ent)) p(" live qword value in the STATIC image: %#x" % qword(0x1802ef5a0)) p("") p("callers of FUN_1801a0040 (commerce getter)") cs = xrefs_to(0x1801a0040) p(" count=%d" % len(cs)) for frm, typ, fn, ent in cs[:60]: p(" %-12x %-10s %s @ %x" % (frm, typ, fn, ent)) # ---------- 5. the points gate ---------- p("") p("=" * 70) p("H3 points gate DAT_1802de0d0 xrefs") for frm, typ, fn, ent in xrefs_to(0x1802de0d0): p(" %-12x %-10s %s @ %x" % (frm, typ, fn, ent)) # ---------- 6. pack record ctor ---------- p("") p("=" * 70) p("H4 pack record ctor FUN_1801342d0") src = dec(0x1801342d0) p("len(src) = %d (FULL)" % len(src)) p(src) # ---------- 7. currency-name literals census ---------- p("") p("=" * 70) p("every xref to the 'mtx' / 'coins' / 'points' / 'DRAFT_TOKEN' literals") for lit in [b"mtx\x00", b"coins\x00", b"points\x00", b"DRAFT_TOKEN\x00", b"POINTS\x00", b"FIFA_POINTS\x00", b"MTX\x00"]: hits = find_all(lit) p("") p(" literal %-14s occurrences=%d %s" % (lit, len(hits), [hex(h) for h in hits[:8]])) for h in hits[:8]: xs = xrefs_to(h) for frm, typ, fn, ent in xs[:20]: p(" %#x <- %-12x %-8s %s @ %x" % (h, frm, typ, fn, ent)) w.close() except Exception: traceback.print_exc() try: w.write(traceback.format_exc()) w.close() except Exception: pass