"""ADVERSARIAL Q2. Batch.

Targets under attack:
 (a) dim1 claim 4: "FUN_1800147f0 ... a miss returns NULL and the caller then
     dereferences address 0x40, i.e. it would crash" -- ABSENCE OF A NULL CHECK.
     Method: print the RAW DISASSEMBLY of FUN_1800147f0 from the CALL to
     FUN_180014420 to the next 40 instructions, so a TEST/JZ is visible if present.
     Control: the same raw-listing method applied to FUN_180014380's call sites,
     where the decompiler DOES show a null test, must show TEST/JZ. Same form.
 (b) dim1 claim 5: "+0x290 is written in exactly TWO places in all of CardsDLL".
     objdump found 12 dword/qword writes at +0x290 plus one QWORD write at +0x28c
     that covers it. Resolve the containing function of every one and decide.
 (c) dim1 claim 9/10: model+0x94 = group ordinal, model+0x1a0 = sortPriority;
     +0x1a0 pushed to no Flash field. Print FUN_18002c3c0 and FUN_180015d80 in full
     and print their exact address ranges so the claim can be re-checked in objdump.
 (d) dim1 claim 3: FUN_1800150d0 / FUN_180012950 / FUN_180014380 full.
 (e) dim1 claim 7: FUN_180014580 / FUN_180014df0 six literals; enumerate.
 (f) FUN_180014610 group-tile builder: does tile+0x9c really get the ordinal
     (CHILD_CATEGORY) and tile+0xac the displayGroupAssetId? Recommendation #1
     depends entirely on this.
"""
import sys, traceback, re
OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store/adv/q2_out.txt"
try:
    fh = open(OUT, "w")
    def P(*a):
        s = " ".join(str(x) for x in a)
        print(s); fh.write(s + "\n")

    TARGETS = [0x1800150d0, 0x180012950, 0x180014380, 0x180014420, 0x1800147f0,
               0x180014610, 0x18002c3c0, 0x180015d80, 0x180014580, 0x180014df0,
               0x18007e7f0, 0x18007d1a0, 0x18007dab0]
    P("=== FUNCTION BOUNDS ===")
    for t in TARGETS:
        f = func(t)
        if f is None:
            P("%#x -> NO FUNCTION" % t); continue
        P("%#x %-22s min=%#x max=%#x size=%#x" % (t, f.getName(),
            int(f.getBody().getMinAddress().getOffset()),
            int(f.getBody().getMaxAddress().getOffset()),
            int(f.getBody().getNumAddresses())))

    # (b) resolve containing functions of every +0x290 write objdump found
    P()
    P("=== (b) containing functions of every raw +0x290 / +0x28c write ===")
    W = [0x180051da3,0x18007d3ba,0x18007f0c0,0x18008c777,0x18008fd45,0x1800d3564,
         0x1800d43fc,0x18013454a,0x180189d84,0x18018caa3,0x18018e1ff,0x180191f77,
         0x18015b885,0x180067eb0,0x180067ebf]
    for w in W:
        f = func(w)
        P("  %#x -> %s @ %#x" % (w, f.getName() if f else "NONE",
                                 int(f.getEntryPoint().getOffset()) if f else 0))
    # is any of those functions in the store-screen vtable?
    P()
    P("=== store screen vtable 0x1801ff690 (first 48 slots) ===")
    ents = set()
    for off, tgt, nm in vtable(0x1801ff690, 48):
        P("   +%#04x %#x %s" % (off, tgt, nm))
        ents.add(tgt)
    P("vtable also at 0x1801ff6f8 / 0x1801ff610 per the claim; dumping 0x1801ff610:")
    for off, tgt, nm in vtable(0x1801ff610, 24):
        P("   +%#04x %#x %s" % (off, tgt, nm))

    # (a) raw disassembly around the FUN_180014420 call inside FUN_1800147f0
    P()
    P("=== (a) RAW LISTING of FUN_1800147f0 (whole function) ===")
    f = func(0x1800147f0)
    it = listing.getInstructions(f.getBody(), True)
    n = 0
    while it.hasNext():
        i = it.next(); n += 1
        P("   %#x  %s" % (int(i.getAddress().getOffset()), str(i)))
    P("instruction count:", n)

    P()
    P("=== (a-control) RAW LISTING of FUN_180014610 (whole function) ===")
    f = func(0x180014610)
    it = listing.getInstructions(f.getBody(), True)
    n = 0
    while it.hasNext():
        i = it.next(); n += 1
        P("   %#x  %s" % (int(i.getAddress().getOffset()), str(i)))
    P("instruction count:", n)

    for t in TARGETS:
        P()
        f = func(t)
        P("======== DECOMPILE %s @ %#x ========" % (f.getName() if f else "?", t))
        src = dec(t, 300)
        P("len(src) =", len(src))
        P(src)
        P("======== END %#x ========" % t)
    fh.close()
except Exception:
    traceback.print_exc()
    try: fh.close()
    except Exception: pass
