"""ADVERSARIAL Q1.

HYPOTHESIS UNDER ATTACK (dim1 claim 3): "FUN_1800150d0 ... finds-or-creates a group by
an exact string compare on displayGroup.value", i.e. wire-record +0x00 holds
displayGroup.value.

WHY IT IS NOT PROVEN: live we serve description == displayGroup.value == the SAME
STRING for all three packs ("Bronze Pack"/"Gold Pack"/"Premium Gold"), so the live
group caption cannot distinguish displayGroup.value (atom 0xd9->0x377) from
description (atom 0xd1). If the key is actually `description`, recommendation #2
(serve displayGroup.value="gold") silently does nothing.

METHOD: decompile the 0x158 wire-record element deserializer 0x18013af30 IN FULL,
print len(src), and enumerate the atom dispatch. Explicitly search the raw
disassembly of the function for EVERY syntactic dispatch form the brief warns about:
  ==  imm, !=  imm, switch case labels (jump table), and sub/dec ladders.
CONTROL: atom 0x20f (packType) is known-present (live pack model +0x38 = "BRONZE"),
so whatever form finds packType must also be applied to 0xd1/0xd9/0xda/0x2cb.
The control uses the SAME method (raw immediate scan over the same instruction
range), not a different one.
"""
import sys, traceback, re
OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store/adv/q1_out.txt"
try:
    fh = open(OUT, "w")
    def P(*a):
        s = " ".join(str(x) for x in a)
        print(s); fh.write(s + "\n")

    ATOMS = {0x23:"assetId",0xd1:"description",0xd9:"displayGroup",0xda:"displayGroupAssetId",
             0xdb:"displayGroupUseDefaultImage",0x15c:"id",0x20f:"packType",0x250:"priority",
             0x2cb:"sortPriority",0x377:"value",0x36a:"useDefaultImage",0x260:"purchase"}

    for target in (0x18013af30,):
        f = func(target)
        P("=== FUNCTION %s @ %#x  body=%s ===" % (f.getName(), int(f.getEntryPoint().getOffset()), f.getBody()))
        src = dec(target, 300)
        P("len(src) =", len(src))
        P("---- FULL DECOMPILE BEGIN ----")
        P(src)
        P("---- FULL DECOMPILE END ----")

    # raw instruction scan of the whole function body for every atom immediate
    P()
    P("=== RAW INSTRUCTION SCAN over FUN_18013af30 body: all forms ===")
    f = func(0x18013af30)
    body = f.getBody()
    it = listing.getInstructions(body, True)
    ins = []
    while it.hasNext():
        i = it.next()
        ins.append((int(i.getAddress().getOffset()), str(i.getMnemonicString()), str(i)))
    P("instruction count:", len(ins))
    # collect all immediates appearing anywhere in the text form
    found = {}
    for a, mn, txt in ins:
        for m in re.finditer(r'0x([0-9a-fA-F]+)', txt):
            v = int(m.group(1), 16)
            if v in ATOMS:
                found.setdefault(v, []).append((a, mn, txt))
    for v in sorted(ATOMS):
        lst = found.get(v, [])
        P("atom %#05x %-28s hits=%d" % (v, ATOMS[v], len(lst)))
        for a, mn, txt in lst:
            P("      %#x  %s" % (a, txt))
    # dispatch-form census: CMP/SUB/DEC ladders on the atom register
    P()
    P("=== dispatch-form census (CMP/SUB/DEC/SWITCH inside the function) ===")
    forms = {"CMP":0,"SUB":0,"DEC":0,"JMP":0,"SWITCH":0}
    for a, mn, txt in ins:
        if mn in forms: forms[mn]+=1
        if mn == "JMP" and "[" in txt: forms["SWITCH"]+=1
    P(forms)
    P("all CMP with a small immediate (candidate atom compares):")
    for a, mn, txt in ins:
        if mn in ("CMP","SUB","DEC","ADD") :
            m = re.search(r'0x([0-9a-fA-F]{1,4})\s*$', txt)
            if m:
                v=int(m.group(1),16)
                if 0x10 <= v <= 0x400:
                    P("   %#x  %-8s %s   -> imm %#x %s" % (a, mn, txt, v, ATOMS.get(v,"")))
    fh.close()
except Exception:
    traceback.print_exc()
