"""ADVERSARIAL BATCH 3: exhaustive [reg+disp32] scan, tightened.

0xb4 / 0x54 / 0x88 / 0xe0 cannot be a signed disp8, so every [reg+off] reference
must carry the disp32 bytes literally. The scan is therefore exhaustive over .text.
Filter: keep only instructions whose printed operand ends in "+ 0x<off>]", drop LEA
and the unwind-stub noise.
Positive controls that MUST appear: FUN_1801a87f0 (+0xb4 read),
FUN_180141660 (+0xb4 read and +0x54 write), FUN_1801a85c0 (+0x88 read),
FUN_1801a80c0 (+0x54 read and write).
"""
import traceback

try:
    for off in (0xB4, 0x54, 0x88, 0xE0):
        pat = bytes([off, 0, 0, 0])
        hits = find_all(pat, blocks=(".text",))
        rows = []
        for h in hits:
            ins = listing.getInstructionContaining(addr(h))
            if ins is None:
                continue
            txt = str(ins)
            if ("+ %s]" % hex(off)) not in txt:
                continue
            mn = txt.split()[0]
            if mn in ("LEA", "NOP"):
                continue
            f = fm.getFunctionContaining(ins.getAddress())
            fn = f.getName() if f else "?"
            if fn.startswith("Unwind") or fn.startswith("_guard"):
                continue
            rows.append((int(ins.getAddress().getOffset()), txt, fn))
        rows = sorted(set(rows))
        print("\n==== [reg+%#x] exhaustive disp32 scan: %d non-LEA, non-unwind instructions"
              % (off, len(rows)))
        byf = {}
        for a, t, fn in rows:
            byf.setdefault(fn, []).append((a, t))
        for fn in sorted(byf):
            print("  %-26s %s" % (fn, "; ".join("%#x %s" % x for x in byf[fn])))
except Exception:
    traceback.print_exc()
