aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/extract_sigs.py
blob: a10a84258a182657acf9195d6a33ab116bdaadd7 (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
"""Run inside IDA (via ida-mcp execute_python) to dump address-invariant
per-function signatures for cross-version binary diffing.

Writes psp_out/sigs/<input-stem>.json
"""
import idaapi, idc, idautils, ida_funcs, ida_bytes, ida_auto, ida_segment
import ida_gdl, hashlib, json, os


def _prep():
    seg = ida_segment.getnseg(0)
    # SOS carries an ARM prologue/vector region at 0x0-0x3c0; rest is Thumb.
    if seg is not None and seg.start_ea == 0:
        try:
            idc.split_sreg_range(0x0, 'T', 0, idc.SR_user)
            idc.split_sreg_range(0x3C0, 'T', 1, idc.SR_user)
        except Exception as e:
            print('sreg:', e)
    ida_auto.auto_wait()


def _sig(fn):
    toks, ncall = [], 0
    ea = fn.start_ea
    while ea < fn.end_ea:
        if ida_bytes.is_code(ida_bytes.get_flags(ea)):
            m = idc.print_insn_mnem(ea)
            if m:
                parts = [m]
                for oi in range(3):
                    t = idc.get_operand_type(ea, oi)
                    if t == 0:
                        break
                    if t == 1:
                        parts.append('r%d' % idc.get_operand_value(ea, oi))
                    elif t == 5:
                        v = idc.get_operand_value(ea, oi)
                        parts.append('i%x' % v if abs(v) < 0x10000 else 'IMMBIG')
                    elif t in (2, 3, 4):
                        parts.append('MEM')
                    elif t in (6, 7):
                        parts.append('ADDR')
                    else:
                        parts.append('t%d' % t)
                toks.append('|'.join(parts))
                if m.lower().startswith(('bl', 'blx')):
                    ncall += 1
        ea = idc.next_head(ea, fn.end_ea)
    try:
        nbb = len(list(ida_gdl.FlowChart(fn)))
    except Exception:
        nbb = 0
    return toks, nbb, ncall


def run():
    _prep()
    out = {}
    for f in idautils.Functions():
        fn = ida_funcs.get_func(f)
        toks, nbb, ncall = _sig(fn)
        out['%x' % fn.start_ea] = {
            'size': fn.end_ea - fn.start_ea,
            'n': len(toks),
            'bb': nbb,
            'calls': ncall,
            'h': hashlib.sha256('\n'.join(toks).encode()).hexdigest()[:16],
        }
    stem = os.path.basename(idc.get_input_file_path()).rsplit('.', 1)[0]
    d = '/Users/user/dev/amd-psp-re/psp_out/sigs'
    os.makedirs(d, exist_ok=True)
    p = os.path.join(d, stem + '.json')
    json.dump(out, open(p, 'w'))
    print('%s: %d funcs -> %s' % (stem, len(out), p))
    return len(out)


run()