"""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