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
|
#!/usr/bin/env python3
"""Cross-version structural diff + old<->new function pairing.
Functions are matched first by address-invariant signature (identical = unchanged).
Leftovers are paired greedily on (bb, calls, n, size) proximity, which survives
code motion and gives candidate old->new pairs to decompile side by side.
"""
import json, glob, os, re, sys, collections
def load(comp):
out = {}
for f in sorted(glob.glob(f'sigs/{comp}_*.json')):
v = re.search(rf'{comp}_([\d.]+)_', os.path.basename(f)).group(1)
out[v] = json.load(open(f))
return out, sorted(out, key=lambda s: [int(x) for x in s.split('.')])
def pair(a, b):
"""a,b: {addr:{...}} -> (unchanged, pairs, only_a, only_b)"""
ha = collections.defaultdict(list)
for ad, d in a.items(): ha[d['h']].append(ad)
hb = collections.defaultdict(list)
for ad, d in b.items(): hb[d['h']].append(ad)
same = set(ha) & set(hb)
unchanged = sum(min(len(ha[h]), len(hb[h])) for h in same)
ra = [ad for h in ha if h not in same for ad in ha[h]]
rb = [ad for h in hb if h not in same for ad in hb[h]]
used, pairs = set(), []
for ad in sorted(ra, key=lambda x: -a[x]['size']):
da = a[ad]; best = None; bs = 1e18
for bd in rb:
if bd in used: continue
db = b[bd]
s = (abs(da['bb']-db['bb'])*6 + abs(da['calls']-db['calls'])*4
+ abs(da['n']-db['n']) + abs(da['size']-db['size'])/8.0)
if s < bs: bs, best = s, bd
# only accept a pairing that is structurally close
if best is not None and bs < 40:
used.add(best); pairs.append((ad, best, round(bs, 1)))
only_a = [x for x in ra if x not in {p[0] for p in pairs}]
only_b = [x for x in rb if x not in used]
return unchanged, pairs, only_a, only_b
for comp in ('sos', 'sys_drv'):
sigs, vers = load(comp)
if not vers: continue
print(f"\n{'='*72}\n{comp.upper()} ({len(vers)} versions)\n{'='*72}")
print(f"{'transition':<26s} {'unchanged':>9s} {'modified':>8s} {'removed':>7s} {'added':>6s}")
for x, y in zip(vers, vers[1:]):
u, p, oa, ob = pair(sigs[x], sigs[y])
print(f" {x} -> {y:<10s} {u:>9d} {len(p):>8d} {len(oa):>7d} {len(ob):>6d}")
u, p, oa, ob = pair(sigs[vers[0]], sigs[vers[-1]])
print(f"\n {vers[0]} vs {vers[-1]}: {u} unchanged, {len(p)} modified, "
f"{len(oa)} removed, {len(ob)} added")
if p:
print(f"\n modified functions ({vers[0]} -> {vers[-1]}), by size:")
print(f" {'old':>9s} {'new':>9s} {'size':>6s} {'bb':>4s} {'calls':>5s} {'dist':>5s}")
for ad, bd, s in sorted(p, key=lambda t: -sigs[vers[0]][t[0]]['size'])[:25]:
da, db = sigs[vers[0]][ad], sigs[vers[-1]][bd]
print(f" {ad:>9s} {bd:>9s} {da['size']:>6d} "
f"{da['bb']:>2d}->{db['bb']:<2d} {da['calls']:>2d}->{db['calls']:<2d} {s:>5.1f}")
json.dump({'pairs': p, 'only_old': oa, 'only_new': ob},
open(f'sigs/{comp}_diff_{vers[0]}_vs_{vers[-1]}.json', 'w'), indent=1)
|