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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
"""IDA-free regressions for the source-shipped listing tag parser."""
from __future__ import annotations
import ast
import re
from pathlib import Path
from types import SimpleNamespace
NEEDS_IDA = False
PASS = FAIL = 0
ON, OFF, ESC, INV = "\x01", "\x02", "\x03", "\x04"
ADDR, SEM = "\x28", "\x36"
INSN, REG, NUM, OPND = "\x05", "\x29", "\x0c", "\x40"
def load_parser(semantic_headers=True):
# remote_tools is shipped as source and imports IDAPython at module scope.
# Compile the actual parser, not a copy, with only its IDA surface faked.
path = Path(__file__).resolve().parents[1] / "idatui" / "remote_tools.py"
tree = ast.parse(path.read_text())
node = next(
n
for n in tree.body
if isinstance(n, ast.FunctionDef) and n.name == "_idatui_spans"
)
calls = []
def skipcode(at):
calls.append(at)
if at[0] == INV:
return 1
if at.startswith(ON + ADDR):
return 18
if semantic_headers and at.startswith(ON + SEM):
return 19 if at[2] == "\x03" else 3
return 2
ns = {
"_re": re,
"ida_lines": SimpleNamespace(
COLOR_ADDR=0x28, COLOR_ADDR_SIZE=16, tag_skipcode=skipcode
),
"_IDATUI_TAGS": {INSN: "insn", REG: "reg", NUM: "num"},
"_IDATUI_OPND_TAGS": {OPND: 0},
"_IDATUI_CTL": None,
"_IDATUI_TAGINFO": None,
}
exec(compile(ast.Module(body=[node], type_ignores=[]), str(path), "exec"), ns)
return ns["_idatui_spans"], calls
def tag(t, text):
return ON + t + text + OFF + t
def check(name, actual, expected):
global PASS, FAIL
if actual == expected:
PASS += 1
print(f" ok {name}")
else:
FAIL += 1
print(f" FAIL {name}: {actual!r} != {expected!r}")
def main():
parse, calls = load_parser()
check(
"ordinary colours and operand coordinates",
parse(
tag(INSN, "mov")
+ " "
+ tag(OPND, tag(REG, "eax") + ", " + tag(NUM, "42"))
),
(
[
["insn", "mov"],
["text", " "],
["reg", "eax"],
["text", ", "],
["num", "42"],
],
[[4, 11, 0]],
),
)
# Semantic metadata can itself contain bytes that look like presentation
# controls. The kernel's header length, not a two-byte regex, owns them.
name = ON + SEM + "\x01" + "main" + OFF + SEM
check(
"semantic name preserves enclosing colour",
parse(tag(INSN, "call " + name)),
([["insn", "call main"]], []),
)
typed = ON + SEM + "\x03" + "0123456789ABCDEF" + tag(REG, "MY_TYPE") + OFF + SEM
check(
"type-id payload is invisible and operand survives",
parse(tag(OPND, typed + "+" + tag(NUM, "4"))),
([["reg", "MY_TYPE"], ["text", "+"], ["num", "4"]], [[0, 9, 0]]),
)
check(
"address payload is invisible",
parse(tag(REG, "a" + ON + ADDR + "0123456789ABCDEF" + "b")),
([["reg", "ab"]], []),
)
check(
"inverse is a one-byte code, including at end",
parse(tag(REG, "a" + INV + "b" + INV)),
([["reg", "ab"]], []),
)
check(
"escaped controls are literal",
parse("a" + ESC + ON + "b"),
([["text", "a" + ON + "b"]], []),
)
check("kernel skipcode is consulted", bool(calls), True)
old_parse, _ = load_parser(semantic_headers=False)
check(
"headerless semantic group preserves enclosing colour",
old_parse(tag(INSN, "call " + tag(SEM, "main"))),
([["insn", "call main"]], []),
)
check(
"unknown presentation tags retain their text",
parse(tag("\x7f", "unknown")),
([["text", "unknown"]], []),
)
check("empty line", parse(""), ([], []))
print(f"\n{PASS} passed, {FAIL} failed")
return bool(FAIL)
if __name__ == "__main__":
raise SystemExit(main())
|