"""ADVERSARIAL VERIFY Dimension 1: userInfo.feature restriction vocabulary.

Attacks:
 D1.1 feature loop recognises EXACTLY one sub-key (trade 0x330), all else value-SKIP.
 D1.2 trade uses INT getter FUN_1801c79d0, writes +0xa4 only when ==1.
 D1.3 massinfo root FUN_180174630: at END_OBJECT the SOLE `cmp byte[reg+disp],0` site
      is +0x17c -> zero [reg+0x50]; nothing else zeroes a settings field from a feature byte.

Control: 0x330 MUST appear in the feature loop and map to +0xa4 (param_1+0x29). If the
massinfo case that calls the feature parser is not +0xd8, the +0x17c arithmetic is wrong.
"""
import traceback
try:
    FEAT = 0x18013ec10
    MASS = 0x180174630

    src = dec(FEAT, 300)
    print("=== FEATURE FUN_18013ec10  decompile=%d chars ===" % len(src))
    print(src)

    # enumerate every integer constant compared in the loop (dispatch forms)
    print("\n=== raw instructions in feature parser: CMP/immediates + calls ===")
    f = func(FEAT)
    it = listing.getInstructions(f.getBody(), True)
    cnt = 0
    while it.hasNext():
        ins = it.next()
        m = ins.getMnemonicString()
        s = str(ins)
        if m in ("CMP", "SUB", "LEA") and ("0x330" in s or "0x11c" in s):
            print("  %#x %s" % (ins.getAddress().getOffset(), s))
        if m == "CALL":
            print("  %#x %s" % (ins.getAddress().getOffset(), s))
        cnt += 1
    print("  (total insns=%d)" % cnt)

    print("\n=== MASSINFO root FUN_180174630: scan for cmp byte[reg+disp],0x0 ===")
    fm2 = func(MASS)
    it = listing.getInstructions(fm2.getBody(), True)
    hits = []
    n = 0
    prev = []
    while it.hasNext():
        ins = it.next()
        n += 1
        m = ins.getMnemonicString()
        s = str(ins)
        # cmp byte ptr [reg + disp], 0
        if m == "CMP" and "byte ptr" in s and s.rstrip().endswith(",0x0"):
            hits.append((ins.getAddress().getOffset(), s))
        # any MOV of 0 into [reg+0x50]
        if m == "MOV" and "dword ptr" in s and "0x50]" in s and s.rstrip().endswith(",0x0"):
            print("  ZERO-WRITE %#x %s" % (ins.getAddress().getOffset(), s))
    print("  cmp byte[reg+disp],0 sites: %d" % len(hits))
    for a, s in hits:
        print("    %#x %s" % (a, s))
    print("  (massinfo total insns=%d)" % n)

    # confirm which case calls the feature parser and at what struct offset
    print("\n=== calls to FUN_18013ec10 (feature) from anywhere ===")
    for frm, typ, fn, ent in xrefs_to(FEAT):
        print("  %#x %s in %s" % (frm, typ, fn))
except Exception:
    traceback.print_exc()
