![ida-tui](logo-trans.png) **IDA Pro in a terminal.** Listing, decompiler, graph — keyboard-first, mouse-capable. `disasm` · `pseudocode` · `cfg` · `hex` · `strings` · `structs` · `traces` · `rpc` --- ``` ┌ ida-tui ─────────────────────────────────────────────────────────────────────┐ │ .text:00002490 ; ---------- S U B R O U T I N E ---------- │ │ .text:00002490 main proc near │ │ .text:00002490 endbr64 │ │ .text:00002494 push rbp ; ← cursor │ │ .text:00002495 mov rbp, rsp │ │ .text:00002498 sub rsp, 0B0h ; `o` → 176 │ └──────────────────────────────────────────────────────────────────────────────┘ ``` > **Status: personal project, actively hacked on.** No packaging, no versioning, > no support. It assumes a licensed IDA Pro and a venv at `~/ida-venv`. Things > move and break. Poke around; don't file expectations. ## Why IDA's own UI is excellent and it is a GUI. This is for the times you're in a terminal over SSH, in a tmux pane next to your notes, or driving RE from a script — and still want the listing, Hex-Rays, and a real control-flow graph. It is **not** a reimplementation of IDA. It's a frontend: IDA does the analysis, this draws it. ## Install Needs **Python ≥ 3.11** and **IDA Pro 9.4+ with idalib**. ```sh uv sync ``` That pulls [ida-codemode](https://github.com/HexRaysSA/ida-codemode) from PyPI, which is how ida-tui talks to IDA. To also attach to databases you have open in the IDA GUI, install its plugin: ```sh uvx --prerelease=allow --from ida-codemode ida-codemode-mcp --install-plugin ``` Hacking on ida-codemode itself? Point at a checkout instead: ```sh uv add --editable ../ida-codemode ``` ## Run ```sh ./ida-tui /path/to/binary # attach to a GUI session, or open a managed database ./ida-tui # attach, when exactly one database is registered ``` ida-tui never owns an IDA process — it takes a **lease**. A matching database open in the IDA GUI is reused, otherwise Code Mode starts or shares a managed idalib worker. Quitting drops the lease and leaves everyone else alone. Headerless blobs need a hint, or IDA assumes x86 at address 0 and analyses nothing: ```sh ./ida-tui fw.bin --processor arm --base 0x8000000 ``` These apply only when the database is **created** — an existing IDB already records them. On ARM, `t` toggles ARM/Thumb at the cursor, `T` scans a vector table for Thumb entry points. ## Keys | | | |---|---| | `enter` `escape` | follow / back | | `tab` `F5` | listing ↔ pseudocode | | `space` | control-flow graph (`z` zoom, `m` minimap, `J`/`K` walk edges) | | `s` | split view — listing and pseudocode, cursor-synced | | `g` `/` `?` | goto · search · search back | | `x` `n` `y` `;` | xrefs · rename · retype · comment | | `c` `d` `p` `u` `a` | make code · data · function · undefine · string | | `o` `O` `B` | cycle this literal's format · reverse · opcode bytes | | `\` `"` `ctrl+t` | hex · strings · structs | | `ctrl+n` `ctrl+p` | symbol palette · command palette | | `ctrl+s` `ctrl+l` `q` | save · reload as… · quit | | `F1` | all of them | ## What's in it **Listing** — code, data and undefined runs in one continuous view, with IDA's own colours. Line-virtualized: a 400 MB binary scrolls like a text file. **Decompiler** (`tab`) — Hex-Rays pseudocode, highlighted, with per-line address anchors. Renames, retypes and comments write back. **Graph** (`space`) — basic blocks laid out with a real layered (Sugiyama) algorithm and routed, colour-coded edges. The boxes hold the *same rows* as the listing, so renames and xrefs work inside them. **Split view** (`s`) — listing and pseudocode side by side, cursor-synced. The focused pane drives; the other highlights the instructions the current C line owns. **Search** (`ctrl+f`) — the whole database, as **text** through the disassembly or as **bytes** with IDA's wildcard patterns (`48 8b ?? c3`). It guesses which you meant; `hex:`/`text:` overrides. **Structs / types** (`ctrl+t`) — local types as plain C, editable and highlighted. `ctrl+s` declares it straight back into the database. **Literal formats** (`o`) — hex → decimal → binary → char → offset, IDA's own key. Skips the stops that wouldn't change anything, so no press is a silent no-op. **Findings export** (`ctrl+e`) — the session as a markdown writeup: your comments, names and prototypes, grouped by function. idatui journals its own edits, so the report is *yours*, not IDA's analyzer's. **Execution traces** — load a [Tenet](https://github.com/gaasedelen/tenet) trace and move through time. Both code views paint the execution trail; the dock shows registers and stack as of that instant. ```sh ./ida-tui /path/to/binary --trace trace.0.log # ] [ step · } { step over ``` **RPC** — drive the live TUI from another process (agent-driven RE, livestreams): ```sh ./ida-tui /abs/path/bin --rpc /tmp/ida.sock python -m idatui.drive pc main # pseudocode of main python -m idatui.drive rename sub_5BE0 foo # goto + rename ``` Scripted feature tour, for screen recordings: `python tools/demo.py --spawn` **Also** — hex view (`\`), strings (`"`), symbol and command palettes (`ctrl+n`/`ctrl+p`), multi-binary projects, and a splash that renders as a real image on terminals speaking the kitty graphics protocol. ## Tests ```sh python3 tests/run.py --fast # 380 checks, <1s, any python3 — between edits python3 tests/run.py --list # what runs, and what needs IDA python3 tests/run.py # 1031 checks, ~50s — before a commit ``` Every suite declares `NEEDS_IDA`; `--fast` runs only the pure ones (stdlib, no IDA, no database). The rest drive a headless Textual `Pilot` against a real database. Iterate on one with `--only`: ```sh ~/ida-venv/bin/python tests/test_scenarios.py targets/echo --only hex,rename ``` Before optimising or debugging a slow run, read [`.fastfeedback/SPEED.md`](.fastfeedback/SPEED.md) — per-suite timings, the known flake, and the four ways a test here wastes minutes. ## Working on this with an LLM agent [`.agents/skills/idatui/SKILL.md`](.agents/skills/idatui/SKILL.md) is an [Agent Skills](https://agentskills.io/specification) skill covering the architecture, the test loop, and the traps that cost real time here. Compatible harnesses pick it up automatically; point others at the file. (A user-level skill of the same name wins, so symlink yours if you keep one.) — [sl0p.foo](https://sl0p.foo)