"""ADVERSARIAL VERIFICATION BATCH 1 (dim4 + dim5).

HYPOTHESES UNDER ATTACK
 H1 (dim5 f5/f7): the publisher FUN_18006cc60 maps model vtable slots to IS_* names,
    and IS_TRADING_ENABLED (0x1801fc118) has exactly ONE rip-relative reference in
    .text (the lea), i.e. the name is output-only.
    CONTROL: run the same rip-relative scanner against a literal that IS known to be
    compared, e.g. one of the ISOfferTrade error strings 0x180228f20, which must show
    up in a *different* instruction context, and against IS_STORE_ENABLED.
 H2 (dim5 f5 positive control): IS_STORE_ENABLED's accessor (vt+0x280) - what does it
    actually compute? If it is a live-evaluable expression we can compare STORE vs
    TRADING under the same publish mechanism.
 H3 (dim4 f2): FutGetSuggestedPricing deser 0x180163ee0 top-level token is
    START_ARRAY (loop terminates on 0xd) - CONTROL FUN_180165df0 (ISStart) must
    terminate on 10.
 H4 (dim4 f4): 0x1801642c0 is `return 1;`.
 H5 (dim4 f6): tradeState table 0x180229e40 / bidState ladder FUN_180166380.
 H6 (dim4 f9): IS_MAX_AUCTIONS publisher FUN_1800377c0 + GetAuctionCount deser
    0x180163770.
 H7 (dim4 f8): error mapper FUN_1801844c0.
Everything printed IN FULL with len(src).
"""
import traceback, struct

def full(tag, va):
    try:
        s = dec(va)
        print("\n----- %s  %#x  len=%d -----" % (tag, va, len(s)))
        print(s)
    except Exception:
        traceback.print_exc()

try:
    print("### H1: publisher FUN_18006cc60")
    full("publisher", 0x18006cc60)

    print("\n### model vtable slots")
    VT = 0x18021c2a0
    for off in (0x270, 0x280, 0x2b0, 0x988, 0x998, 0xa58, 0xa60, 0x130, 0x5b8, 0xa00):
        t = qword(VT + off)
        print("  vt+%#05x -> %#x  %s" % (off, t, fname(t) if 'fname' in dir() else ''))
    full("vt+0x280 IS_STORE_ENABLED accessor", qword(VT + 0x280))
    full("vt+0x270 IS_TRADING_ENABLED accessor", qword(VT + 0x270))
    full("vt+0xa58 TRADE_PILE_SIZE accessor", qword(VT + 0xa58))

    print("\n### H1 rip-relative reference scan, form independent")
    # Scan .text for any 4-byte little-endian rel32 whose target == literal VA,
    # for every instruction end position. This catches lea/mov/cmp/push equally.
    tblk = None
    for b in mem.getBlocks():
        if b.getName() == ".text":
            tblk = b
    TS = int(tblk.getStart().getOffset()); TE = int(tblk.getEnd().getOffset())
    text = read_bytes(TS, TE - TS + 1)
    print("  .text %#x..%#x len=%d" % (TS, TE, len(text)))

    def ripscan(target, label):
        hits = []
        for i in range(0, len(text) - 4):
            rel = struct.unpack_from('<i', text, i)[0]
            # instruction end = TS + i + 4 (rel32 is the last field of the insn)
            if TS + i + 4 + rel == target:
                hits.append(TS + i)
        print("  %-34s target %#x : %d candidate rel32 sites" % (label, target, len(hits)))
        for h in hits[:20]:
            print("      at %#x  bytes %s  fn %s" % (h - 3, text[h - 6:h + 6].hex(),
                  (fm.getFunctionContaining(addr(h)) or "?")))
        return hits

    lits = {}
    for nm in (b"IS_TRADING_ENABLED\x00", b"IS_STORE_ENABLED\x00",
               b"IS_DRAFT_MODE_ENABLED\x00", b"TRADE_PILE_SIZE\x00",
               b"IS_MAX_AUCTIONS\x00", b"NUM_MAX_AUCTIONS\x00",
               b"You are not allowed to bid on this trade\x00"):
        f = find_all(nm, blocks=(".rdata", ".data", ".text"))
        lits[nm] = f
        print("  literal %-45r -> %s" % (nm[:40], [hex(x) for x in f]))
    for nm, f in lits.items():
        for a in f:
            ripscan(a, nm[:30].decode(errors='replace'))

    print("\n### H3 pricelimits vs ISStart control")
    full("FutGetSuggestedPricing deser", 0x180163ee0)
    full("FutISStart deser CONTROL", 0x180165df0)

    print("\n### H4 generic ack deser")
    full("ack deser", 0x1801642c0)

except Exception:
    traceback.print_exc()
