"""Phase 3: decompile every named function in every build and normalise the text so that only SEMANTICS remain. Normalisation rules (and the reasoning): * symbolic autonames (sub_/loc_/off_/dword_/byte_/word_/unk_/jpt_ + hex) carry a build-specific address -> replaced by a generic token. A *named* callee keeps its name, because that is semantic. * bare hex that falls INSIDE the image's own VA range is an address -> ADDR. Anything outside that range is a real constant and is KEPT VERBATIM. This is the crux: a bounds value like 0x80, a mask like 0x7FF or a size like 0x1400 must survive, because fixes of exactly that shape are what the previous mnemonic-only diff was blind to. * Hex-Rays locals (v3, a1, ...) are renumbered by order of first appearance, so that adding one local does not shift every name and fake a diff. * comments are stripped (they include our own port-tier markers). Determinism is asserted: decompiling the same build twice must be byte-identical. """ import glob, os, re, sys, json, struct, hashlib import idapro, idautils, idc, ida_hexrays SYMRE = re.compile(r'\b(?:sub|loc|off|dword|byte|word|qword|unk|jpt|algn|asc|flt|stru)_[0-9A-Fa-f]+\b') HEXRE = re.compile(r'\b0x([0-9A-Fa-f]+)\b') VARRE = re.compile(r'\b([av])(\d+)\b') def elf_range(p): raw=open(p,'rb').read(); ph=struct.unpack(' not comparable try: t=str(ida_hexrays.decompile(f)) except Exception: continue out[n]=normalise(t, lo, hi) idapro.close_database(save=False) return out if __name__=='__main__': fams=sys.argv[1:] or ['hdcp','sos','sys_drv','asd'] os.makedirs('phase3', exist_ok=True) for fam in fams: for elf in sorted(glob.glob('handover/elf/versions/%s/*.elf'%fam)): tag=os.path.basename(elf).replace('.elf','') dst='phase3/%s.json'%tag d=run(elf) if d is None: print(" FAILED %s"%tag, flush=True); continue json.dump(d, open(dst,'w')) print(" %-42s %5d functions decompiled"%(tag, len(d)), flush=True)