aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-09 12:15:42 +0200
committerblasty <blasty@local>2026-07-09 12:15:42 +0200
commit3527cb15cf02a7c50d976c52b39ec73e0adf760d (patch)
treedd107191f88fe728c777438662d60bb57dc962d2
parentadd Ctrl+B toggle to show/hide the functions pane (diff)
downloadida-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.py17
-rw-r--r--idatui/tui.py27
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