aboutsummaryrefslogtreecommitdiffstats
path: root/fw/ta_final_note.py
blob: b319fe6d1545ea267852a9d507b24dbd8bd372b8 (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
87
88
89
90
91
92
93
#!/usr/bin/env python3
"""Write the per-TA summary into the IDB as function comments.

Closes the annotation loop: everything recovered about a TA (entry points, param gates,
command map, RPC reach) is written onto the invoke entry so the database is
self-describing, rather than living only in markdown.

usage: ta_final_note.py --prefix opm   (IDATUI_RPC_SOCK -> the pane)
"""
import argparse
import json
import os
import re
import sys

CWD = os.path.expanduser('~/dev/ida-tui-maybe')
sys.path.insert(0, CWD)
from idatui.rpcclient import RpcClient                                   # noqa: E402

REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--prefix', required=True)
    a = ap.parse_args()
    p = a.prefix

    cmds = {}
    cp = os.path.join(REPO, 'fw', 'cmdmaps', '%s.json' % p)
    if os.path.exists(cp):
        cmds = json.load(open(cp))
    rpc = json.load(open(os.path.join(REPO, 'fw', 'audits', 'rpc_table.json')))
    meta = json.load(open(os.path.join(REPO, 'fw', 'syms', '%s.meta.json' % p)))
    h = meta['header']

    cl = RpcClient(os.environ['IDATUI_RPC_SOCK'], timeout=60)
    cl.connect()

    fs = cl.call('functions', filter='%s_TA_InvokeCommandEntryPoint' % p, limit=4)
    if not fs:
        print('%s: no invoke entry named' % p)
        return 1
    ea = fs[0]['ea']
    d = cl.call('disassembly', target=hex(ea))
    d = d.get('disassembly', d)
    dis = [l.get('text', '') for l in d.get('lines', [])]
    gate = next((t for t in dis if re.search(r'CMP.*#0x[0-9A-Fa-f]{3,}', t)), None)

    ids = sorted(cmds, key=lambda x: int(x, 16))
    lines = [
        '%s TA -- recovered structure.' % p.upper(),
        '',
        'info header @0x400100: base=%#x entry=%#x session_tbl=%#x guard=%#x'
        % (h['base'], h['entry'], h['session_tbl'], h['guard']),
        'class magic +0x14 = %r (TA = host-invokable; ASD is DR and is not)'
        % str(meta['header']['magic'])[:12],
        '',
        'Entry points (via the ARM-mode trampoline at %#x):' % h['entry'],
    ]
    labels = ['Create', 'Destroy', 'OpenSession', 'CloseSession', 'Invoke']
    for lab, epa in zip(labels, meta.get('entry_points', [])):
        lines.append('  %-12s %#x' % (lab, epa))
    lines += ['', 'Command surface: %d ids' % len(ids)]
    for k in ids:
        lines.append('  cmd %-6s -> %s' % (k, cmds[k]))
    if gate:
        lines += ['', 'Param-size gate seen in the entry: %s' % gate.strip()]
    t = rpc.get(p, {})
    lines += ['', 'Service RPC reach (IDA-derived, fw/ta_rpc_names.py):']
    lines.append('  SYS_DRV (svc 1): %s'
                 % (', '.join(hex(x) for x in t.get('sysdrv', [])) or 'none resolved'))
    lines.append('  ASD     (svc 2): %s'
                 % (', '.join(hex(x) for x in t.get('asd', [])) or 'none resolved'))
    if 0x202 in t.get('asd', []):
        lines += ['', '!! reaches ASD 0x202 = uninitialised-stack canary disclosure,',
                  '   previously rated not-host-reachable on a four-TA scan.']
    if 0x301 in t.get('asd', []):
        lines += ['', '!! reaches ASD 0x301 = content-controlled write up to 5120 bytes,',
                  '   previously dismissed because no known TA emitted it.']

    txt = '\\n'.join(lines).replace('"', "'")
    # `note` is a drive-level convenience, not an RPC verb: it does goto(fn) then
    # comment(text) on the function's first line. Replicate that here.
    cl.call('goto', target=hex(ea), delay_ms=0)
    cl.call('comment', text=txt)
    cl.call('save')
    print('%s: annotated invoke entry @%#x with %d lines' % (p, ea, len(lines)))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())