ida-tui **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**, **IDA Pro 9.4+ with idalib**, and [ida-codemode](https://github.com/HexRaysSA/ida-codemode) checked out next to this repo (`../ida-codemode`): ```sh git clone https://github.com/HexRaysSA/ida-codemode ../ida-codemode uv sync ``` Install the ida-codemode plugin in IDA too, if you want the TUI to attach to databases you already have open in the GUI. ## 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 have no format to detect — IDA falls back to x86 at address 0 and analyses nothing, so say what it is: ```sh ./ida-tui fw.bin --processor arm --base 0x8000000 ``` `--base` is a real address. These apply only when a database is being **created**; an existing IDB already records them. On ARM, `t` toggles ARM/Thumb decoding at the cursor and `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** — one continuous IDA-style view: code, data and undefined runs together, with IDA's own colour tags and per-operand marks. Line-virtualized, so a 400 MB binary scrolls like a text file. **Decompiler** — Hex-Rays pseudocode with syntax highlighting, per-line address anchors, and rename/retype/comment that write back. **Graph** (`space`) — the current function's basic blocks, laid out with a real layered (Sugiyama) algorithm and routed edges: green taken, red fall-through, blue unconditional, purple loop. The boxes hold the *same rows* as the listing, so highlighting, renames and xrefs work inside them. Above 400 blocks it declines and says so, because nothing readable comes out at that size. → [`docs/GRAPH_VIEW.md`](docs/GRAPH_VIEW.md) **Split view** (`s`) — listing and pseudocode side by side. The focused pane drives; the other highlights every instruction the current C line owns. **Literal formats** (`o`) — hex → decimal → binary → char → offset, IDA's own key. Only stops that change what you see are visited, so no press is a silent no-op. The literal under the cursor is *marked*, and the mark is what changes — it keeps up as the text reflows. Works on Hex-Rays' separate number formats too. **Execution traces** — load a [Tenet](https://github.com/gaasedelen/tenet) trace and move through time: ```sh ./ida-tui /path/to/binary --trace trace.0.log ``` `]`/`[` step, `}`/`{` step over. Both code views are painted with the execution trail — including the pseudocode, since `decomp_map` knows which instructions each C line covers. The dock shows registers and the stack *as of that instant*; bytes the trace never saw print as `??`, not zeros. Trace addresses are rebased onto the database automatically. **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 where # terse-text helper python -m idatui.drive pc main # pseudocode of main python -m idatui.drive rename sub_5BE0 foo # goto + rename ``` → [`docs/RPC.md`](docs/RPC.md) **Splash** — the logo renders as a real image on terminals that speak the kitty graphics protocol, `logo.ans` everywhere else. Support is detected by *asking the terminal*, not by sniffing `$TERM` (under a multiplexer, every variable you'd test is empty while the protocol works fine). ## Tests ```sh python3 tests/run.py --fast # 302 checks, <1s, any python3 — between edits python3 tests/run.py --list # what runs, and what needs IDA python3 tests/run.py # 788 checks, ~2m — 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. ## Docs - [`docs/RPC.md`](docs/RPC.md) — the RPC protocol, verb by verb - [`docs/GRAPH_VIEW.md`](docs/GRAPH_VIEW.md) — how the graph is laid out ## Known sharp edges - PLT/import stubs: following a libc symbol lands on `extrn X:near`, which Hex-Rays can't decompile. No pretty-printing yet. - Xref-select can land on the wrong line in the `x` popup. - No "discard changes and quit": a managed worker saves when its last lease closes, so the TUI can't promise your edits weren't persisted. - A GUI user editing the same database won't invalidate our caches — Code Mode has no change feed yet (see `docs/CODEMODE_UPSTREAM.md`).
sl0p.foo