# -*- coding: utf-8 -*- """对照实验:证明「按语义名配对」不是归一化过拟合出来的。 风险: 归一化若抹掉太多,任何配对都会"全对",校验就失去意义。 做法(负对照,同一套归一化 + 同一套比对): ① 按语义名配对(实验组) ② 按清单下标配对(负对照:槽位序随机,必然错) ③ 按模块 id 配对(负对照:id 每次重排,必然错) ④ 随机配对(负对照基线) 若①接近 37/37 而 ②③④ 明显更低 => 归一化没有过拟合,配对确实有效。 """ import glob import random import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "tools")) import tdc_modules as tm # noqa: E402 import tdc_program as tp # noqa: E402 # 带"代码地址"的操作数:每份构建的布局不同,必须抹掉 ADDR_OPS = {"JUMP", "JUMP_IF_TRUE", "JUMP_IF_FALSE", "PUSH_TRY"} def norm_code(p, entry, maxlen=400): """归一化指令序列(与 verify_pair_correct2.py 同一套规则,此处内联避免导入副作用)。 抹掉三类**会重排**的操作数,保留指令骨架与字符串的位置: 1. 代码地址(JUMP / JUMP_IF_* / PUSH_TRY) 2. require(N) 的模块号 N 3. PUSH_STR_CHAR 的具体字符值(fp_salt 随机盐串、混淆变量名每次都变) """ code = p.function_code(entry) out = [] for k, inst in enumerate(code): nm, ops = inst["name"], tuple(inst["operands"] or []) if nm in ADDR_OPS: ops = ("ADDR",) * len(ops) elif nm == "PUSH_STR_CHAR": ops = ("CH",) elif nm == "PUSH_IMM" and ops: if (k >= 1 and code[k - 1]["name"] == "LOAD_REG_IMM" and k + 1 < len(code) and code[k + 1]["name"] == "CALL_FUNC"): ops = ("MOD",) * len(ops) out.append((nm, ops)) return tuple(out[:maxlen]) BATCH = sorted(glob.glob(str(ROOT / "har" / "online" / "*" / "tdc_*.js"))) def load(f): p = tp.load(f) rows = tm.analyze(f) reg = dict(p.module_registry(0)) sig = {r["name"]: norm_code(p, reg[r["module"]]) for r in rows} return rows, sig A, fa = load(BATCH[0]) print("基准 %s (归一化:抹掉地址/模块号/字符值,保留指令骨架与串的位置)" % BATCH[0].split("/")[-1]) print() print("%-22s %-22s %-8s %-8s" % ("对比构建", "配对方式", "一致", "不一致")) print("-" * 66) for f in BATCH[1:]: B, fb = load(f) names = sorted(set(fa) & set(fb)) def score(pairs): return sum(1 for a, b in pairs if a == b) # ① 实验组:语义名 p1 = [(fa[n], fb[n]) for n in names] # ② 负对照:清单下标 p2 = [(fa[A[i]["name"]], fb[B[i]["name"]]) for i in range(min(len(A), len(B)))] # ③ 负对照:模块 id(用 id 当 key 去查另一边同名集合的任意一项) fb_by_id = {r["module"]: fb[r["name"]] for r in B} p3 = [(fa[A[i]["name"]], fb_by_id.get(A[i]["module"], ())) for i in range(len(A))] # ④ 负对照:随机配对 rnd = random.Random(7) shuf = names[:] rnd.shuffle(shuf) p4 = [(fa[n], fb[m]) for n, m in zip(names, shuf)] for label, pairs in (("①语义名(实验组)", p1), ("②清单下标(负对照)", p2), ("③模块id(负对照)", p3), ("④随机配对(负对照)", p4)): s = score(pairs) print("%-22s %-22s %-8d %-8d" % (f.split("/")[-1], label, s, len(pairs) - s)) print() print(">>> 判读: ①应≈37/37;②③④应明显更低 => 归一化未过拟合,配对有效")