"""Find ADDED guards between firmware versions, using the corrected method. The Phase-4/5 pipeline searched for raw little-endian dwords, which cannot see Thumb `cmp.w`/`mov.w` MODIFIED IMMEDIATES -- the encoding these constants actually use. That produced at least one false negative that read as confirmation (see METHODOLOGY-INLINING-VS-ADDED-GUARDS.md). Correct method: * decode every 2-byte-aligned instruction (linear sweep misaligns) * count immediates of comparison/bound instructions only * report constants whose COUNT RISES in the newer build (rising = added code; falling/equal = outlining or refactor) """ from capstone import * import sys, collections md=Cs(CS_ARCH_ARM,CS_MODE_THUMB) CMPish={'cmp','cmp.w','cmn','cmn.w','subs','sub.w','tst','tst.w','movw','mov.w','movs'} def census(body): c=collections.Counter() for off in range(0,len(body)-4,2): ins=list(md.disasm(body[off:off+4],off)) if not ins: continue i=ins[0] if i.mnemonic not in CMPish: continue if '#' not in i.op_str: continue try: v=int(i.op_str.rsplit('#',1)[1],0) except Exception: continue if v<8: continue # skip trivial small constants c[(i.mnemonic.split('.')[0],v)]+=1 return c def delta(a,b,label): A,B=census(open(a,'rb').read()),census(open(b,'rb').read()) gained=[(k,B[k]-A[k]) for k in set(A)|set(B) if B[k]-A[k]>0] gained.sort(key=lambda t:-t[1]) print("=== %s ==="%label) print(" constants whose count RISES (candidate ADDED guards):") shown=0 for (m,v),n in gained: if n<1: continue print(" %-5s #%-12s +%d (%d -> %d)"%(m,hex(v),n,A[(m,v)],B[(m,v)])) shown+=1 if shown>=18: print(" ..."); break print() if __name__=='__main__': delta('versions/sos_0.49.0.51_14039a1f.bin','versions_lfw/sos_3fd1d56b0467c5ea_98944.bin','sOS .51 -> .52') delta('versions_lfw/sos_3fd1d56b0467c5ea_98944.bin','versions/sos_0.49.0.54_3dcb63e8.bin','sOS .52 -> .54') delta('versions/sys_drv_0.49.0.51_0e768b6a.bin','versions/sys_drv_0.49.0.54_18bd9d26.bin','SYS_DRV .51 -> .54')