diff options
| author | blasty <blasty@local> | 2026-07-09 12:15:42 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-09 12:15:42 +0200 |
| commit | 3527cb15cf02a7c50d976c52b39ec73e0adf760d (patch) | |
| tree | dd107191f88fe728c777438662d60bb57dc962d2 | |
| parent | add Ctrl+B toggle to show/hide the functions pane (diff) | |
| download | ida-tui-3527cb15cf02a7c50d976c52b39ec73e0adf760d.tar.gz ida-tui-3527cb15cf02a7c50d976c52b39ec73e0adf760d.tar.xz ida-tui-3527cb15cf02a7c50d976c52b39ec73e0adf760d.zip | |
tui: --open PATH to create/attach a session for an arbitrary binary
- app: when open_path is set, idb_open (idle_ttl=1e9, long timeout) and pin the
returned session; clean status on failure (e.g. non-writable dir)
- tui.py: --open flag + usage docs (attach vs open)
- verified: --open targets/cat -> new session, 161 funcs loaded
| -rw-r--r-- | idatui/app.py | 17 | ||||
| -rw-r--r-- | idatui/tui.py | 27 |
2 files changed, 38 insertions, 6 deletions
diff --git a/idatui/app.py b/idatui/app.py index a91b3f0..574ec4d 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -247,10 +247,12 @@ class IdaTui(App): Binding("escape", "back", "Back"), ] - def __init__(self, url: str, db: str | None, keepalive: bool = True) -> None: + def __init__(self, url: str, db: str | None, keepalive: bool = True, + open_path: str | None = None) -> None: super().__init__() self._url = url self._db = db + self._open_path = open_path self._do_keepalive = keepalive self.client: IDAClient | None = None self.program: Program | None = None @@ -285,7 +287,18 @@ class IdaTui(App): try: client = IDAClient(self._url, db=self._db) client.connect() - if self._db is None: + if self._open_path is not None: + self.app.call_from_thread(self._status, f"opening {self._open_path}…") + import os + path = os.path.abspath(os.path.expanduser(self._open_path)) + res = client.call("idb_open", input_path=path, + idle_ttl_sec=1_000_000_000, timeout=1800.0) + if not (isinstance(res, dict) and res.get("success")): + err = res.get("error") if isinstance(res, dict) else res + self.app.call_from_thread(self._status, f"open failed: {err}") + return + client.set_db(res["session"]["session_id"]) + elif self._db is None: client.set_db(client.resolve_db()) health = client.health() module = health.get("module", "?") diff --git a/idatui/tui.py b/idatui/tui.py index 3fc7edd..e787fe6 100644 --- a/idatui/tui.py +++ b/idatui/tui.py @@ -1,4 +1,18 @@ -"""Launcher: python -m idatui.tui [--url URL] [--db SESSION] [--no-keepalive]""" +"""Launcher for the idatui TUI. + + # attach to the single open session on a running server + python -m idatui.tui + + # attach to a specific session + python -m idatui.tui --db 80d83396 + + # open (or reopen) an arbitrary binary, then drive it + python -m idatui.tui --open /path/to/binary + +The server (supervisor) must already be running (see spawn.sh). --open creates a +session via idb_open; the binary's directory must be writable (idalib writes a +.i64 next to it). +""" from __future__ import annotations import argparse @@ -10,12 +24,17 @@ from .client import DEFAULT_URL def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser(prog="idatui", description="Minimal TUI for IDA over MCP") - p.add_argument("--url", default=os.environ.get("IDA_MCP_URL", DEFAULT_URL)) - p.add_argument("--db", default=os.environ.get("IDA_MCP_DB")) + p.add_argument("--url", default=os.environ.get("IDA_MCP_URL", DEFAULT_URL), + help=f"MCP server URL (default {DEFAULT_URL})") + p.add_argument("--db", default=os.environ.get("IDA_MCP_DB"), + help="attach to an existing session id") + p.add_argument("--open", metavar="PATH", + help="open (or reopen) a binary and drive it (dir must be writable)") p.add_argument("--no-keepalive", action="store_true", help="Do not bump idle-TTL / run the keepalive heartbeat") args = p.parse_args(argv) - IdaTui(url=args.url, db=args.db, keepalive=not args.no_keepalive).run() + IdaTui(url=args.url, db=args.db, open_path=args.open, + keepalive=not args.no_keepalive).run() return 0 |
