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
135
136
137
|
"""Render a modal's chrome headless, with fake data, and print it.
python3 experiments/modal_shot.py [name] [cols] [rows]
python3 experiments/modal_shot.py --list
Chrome (borders, titles, spacing) is the one thing you cannot judge from the
pane you are working in, and opening a binary just to look at a dialog costs a
full auto-analysis. Every modal here is fed a stub, so this needs NO worker, NO
IDA and no .i64 -- it runs under any python with textual, in about a second.
The app it boots is a bare `App` carrying `IdaTui.CSS` and the app theme, which
is exactly what the modals resolve their styles against.
"""
import asyncio
import os
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, REPO)
from textual.app import App, ComposeResult # noqa: E402
from textual.widgets import Static # noqa: E402
from idatui._sync import wait_for # noqa: E402
from idatui.app import ( # noqa: E402
IDATUI_THEME,
BusyScreen,
ConfirmScreen,
HelpScreen,
IdaTui,
LoadingScreen,
QuitScreen,
StructEditor,
XrefsScreen,
)
from idatui.domain import Struct # noqa: E402
from idatui.rpc import screen_text # noqa: E402
class _StubProgram:
"""Just enough Program for the struct editor to render."""
_STRUCTS = [
Struct(name="timespec", size=0x10, members=2, is_union=False, ordinal=1),
Struct(name="stat", size=0x90, members=15, is_union=False, ordinal=2),
Struct(name="pthread_mutex_t", size=0x28, members=4, is_union=True, ordinal=3),
]
_SRC = (
"struct timespec\n"
"{\n"
" __time_t tv_sec; /* seconds */\n"
" __syscall_slong_t tv_nsec;\n"
"};\n"
)
def list_structs(self):
return list(self._STRUCTS)
def struct_source(self, name):
return self._SRC.replace("timespec", name)
def _make(name: str):
"""name -> a mounted-ready modal screen."""
if name == "structs":
return StructEditor(_StubProgram())
if name == "confirm":
return ConfirmScreen("Delete struct 'timespec' ?", "This cannot be undone.")
if name == "quit":
return QuitScreen(["echo", "libc.so.6"])
if name == "help":
return HelpScreen()
if name == "busy":
return BusyScreen("decompiling sub_2297\u2026")
if name == "loading":
return LoadingScreen("echo", "opening database\u2026")
if name == "xrefs":
return XrefsScreen(
" xrefs to main",
[
(0x1234, " sub_2297+0x1c call main"),
(0x5678, " _start+0x21 mov rdi, main"),
],
)
raise SystemExit(f"unknown modal {name!r}; --list to see them")
NAMES = ["structs", "confirm", "quit", "help", "busy", "loading", "xrefs"]
class _Shot(App):
CSS = IdaTui.CSS
def compose(self) -> ComposeResult:
# A little content underneath, so the modal's edge is visible against
# something rather than floating on an empty screen.
yield Static(
"\n".join(" .... the view behind the dialog ...." for _ in range(60))
)
def on_mount(self) -> None:
self.register_theme(IDATUI_THEME)
self.theme = IDATUI_THEME.name
async def main() -> None:
args = [a for a in sys.argv[1:] if not a.startswith("--")]
if "--list" in sys.argv:
print(" ".join(NAMES))
return
names = [args[0]] if args else NAMES
cols = int(args[1]) if len(args) > 1 else 100
rows = int(args[2]) if len(args) > 2 else 32
for name in names:
app = _Shot()
async with app.run_test(size=(cols, rows)) as pilot:
app.push_screen(_make(name))
await pilot.pause()
if name == "structs":
await wait_for(
lambda: bool(getattr(app.screen, "_structs", None)), pilot.pause, 5
)
app.screen.on_option_list_option_selected(
type("E", (), {"option_index": 0})()
)
await wait_for(
lambda: "{" in app.screen.query_one("#se-edit").text, pilot.pause, 5
)
await pilot.pause()
print(f"\n=== {name} " + "=" * (cols - len(name) - 5))
print(screen_text(app)["text"])
if __name__ == "__main__":
asyncio.run(main())
|