#!/usr/bin/env python3 """Locate a DIVERGED function in an older build by best-similarity search and print an instruction-level diff. A diverged function by definition has no exact mnemonic match, so we score every candidate start in the old image by longest-common-prefix of mnemonics against the new function, and diff against the best scorer. usage: vdiff2.py [--ctx=N] """ import json, sys, struct from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB, CS_MODE_LITTLE_ENDIAN md = Cs(CS_ARCH_ARM, CS_MODE_THUMB | CS_MODE_LITTLE_ENDIAN) def load(p): b = open(p, 'rb').read() phoff = struct.unpack(' len(ob): break B = ins_list(ob[co:co + 64], cand) k = 0 for x, y in zip(an, [i.mnemonic for i in B]): if x != y: break k += 1 if k > bestscore: bestscore, best = k, cand print("%s: new @0x%06x size=%d ; best old match @0x%06x (prefix %d instrs)" % (target, st, size, best, bestscore)) B = ins_list(ob[best - ova + ooff: best - ova + ooff + size + 64], best) i = j = 0 shown = 0 while i < len(A) and j < len(B): a, b = A[i], B[j] if a.mnemonic == b.mnemonic and a.op_str == b.op_str: i += 1; j += 1; continue # resync: look ahead for next matching pair print("NEW 0x%06x %-8s %-30s | OLD 0x%06x %-8s %s" % (a.address, a.mnemonic, a.op_str, b.address, b.mnemonic, b.op_str)) shown += 1 if shown > 60: print("..."); break best_k = None for da in range(0, 12): for db in range(0, 12): if i + da < len(A) and j + db < len(B) and \ A[i + da].mnemonic == B[j + db].mnemonic and \ A[i + da].op_str == B[j + db].op_str: if best_k is None or da + db < best_k[0] + best_k[1]: best_k = (da, db) if best_k is None: i += 1; j += 1 else: i += best_k[0]; j += best_k[1] if __name__ == "__main__": main()