1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
#!/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 <new.elf> <funcs.json> <old.elf> <func_name> [--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('<I', b[28:32])[0]
typ, off, va, pa, fsz, msz, fl, al = struct.unpack('<IIIIIIII', b[phoff:phoff + 32])
return b, off, va, fsz
def ins_list(buf, va):
return list(md.disasm(buf, va))
def main():
new_p, funcs_p, old_p, target = sys.argv[1:5]
nb, noff, nva, nfsz = load(new_p)
ob, ooff, ova, ofsz = load(old_p)
funcs = json.load(open(funcs_p))
F = {v[0]: (int(k, 16), v[1]) for k, v in funcs.items()}
if target not in F:
print("no such function"); return
st, en = F[target]
size = en - st
A = ins_list(nb[st - nva + noff: st - nva + noff + size], st)
an = [i.mnemonic for i in A]
best, bestscore = None, -1
lo, hi = ova, ova + ofsz
for cand in range(lo, hi, 2):
co = cand - ova + ooff
if co + size + 32 > 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()
|