"""Phase 5: triage SEMANTIC deltas, prioritising LITERAL changes. A security fix most often shows as a changed constant (bound tightened, mask narrowed, size corrected) or a NEW comparison. Those are exactly what the old mnemonic-only diff could not see, so they are ranked first. """ import json, glob, os, re, sys, collections LIT=re.compile(r"\b(?:0[xX][0-9A-Fa-f]+|\d+)\b") CMP=re.compile(r"(?:<=|>=|==|!=|<|>)") def lits(t): return collections.Counter(LIT.findall(t)) def classify(o, n): lo, ln = lits(o), lits(n) gained = sorted((ln-lo).elements()); lost = sorted((lo-ln).elements()) dcmp = len(CMP.findall(n)) - len(CMP.findall(o)) dlen = len(n) - len(o) score = 0 if gained or lost: score += 3 if dcmp > 0: score += 3 # a comparison was ADDED -> candidate guard if abs(dlen) > 200: score += 1 return dict(gained=gained[:8], lost=lost[:8], dcmp=dcmp, dlen=dlen, score=score) if __name__=='__main__': diffs=json.load(open('phase4_diffs.json')) corp={os.path.basename(p)[:-5]: json.load(open(p)) for p in glob.glob('phase3/*.json')} fams=sys.argv[1:] or ['sos','hdcp','sys_drv','asd'] rows=[] for pair, r in diffs.items(): o,n = pair.split(' -> ') if not any(o.startswith(f+'_') for f in fams): continue for fn in r['changed']: if fn not in corp[o] or fn not in corp[n]: continue c=classify(corp[o][fn], corp[n][fn]) if c['score']>0: rows.append((c['score'], pair, fn, c)) rows.sort(key=lambda t:(-t[0], t[1])) print("SEMANTIC deltas with literal or comparison changes: %d\n"%len(rows)) for sc,pair,fn,c in rows[:40]: po=pair.split(' -> ')[0].rsplit('_',1)[0].replace('sys_drv_','').replace('sos_','').replace('asd_','').replace('hdcp_','') pn=pair.split(' -> ')[1].rsplit('_',1)[0].replace('sys_drv_','').replace('sos_','').replace('asd_','').replace('hdcp_','') print("score=%d %-11s->%-11s %-44s +cmp=%-3d dlen=%-6d"%(sc,po,pn,fn[:44],c['dcmp'],c['dlen'])) if c['gained']: print(" gained literals: %s"%c['gained']) if c['lost']: print(" lost literals: %s"%c['lost'])