aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/vdiff2.py
blob: 9777629d136389a6568627c07e73dae56a68c5c5 (plain) (blame)
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()