| Mode | Name | Size | |
|---|---|---|---|
| d--------- | .auto | 647 | logstatsplain |
| d--------- | .fastfeedback | 36 | logstatsplain |
| -rw-r--r-- | .gitignore | 361 | logstatsplainblame |
| -rw-r--r-- | README.md | 7589 | logstatsplainblame |
| -rw-r--r-- | TODO | 3497 | logstatsplainblame |
| d--------- | docs | 389 | logstatsplain |
| d--------- | experiments | 529 | logstatsplain |
| -rwxr-xr-x | ida-tui | 830 | logstatsplainblame |
| d--------- | idatui | 942 | logstatsplain |
| -rw-r--r-- | logo-trans.png | 1740746 | logstatsplainblame |
| -rw-r--r-- | logo.ans | 40439 | logstatsplainblame |
| -rw-r--r-- | logo.png | 818833 | logstatsplainblame |
| d--------- | plan | 34 | logstatsplain |
| -rw-r--r-- | pyproject.toml | 782 | logstatsplainblame |
| -rw-r--r-- | rehearsed-engineer.md | 452 | logstatsplainblame |
| d--------- | tests | 821 | logstatsplain |
| d--------- | tools | 87 | logstatsplain |
| -rw-r--r-- | uv.lock | 58974 | logstatsplainblame |
**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 checked out next to
this repo (../ida-codemode):
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
./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:
./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
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 trace and move through time:
./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):
./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
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
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:
~/ida-venv/bin/python tests/test_scenarios.py targets/echo --only hex,rename
Before optimising or debugging a slow run, read
.fastfeedback/SPEED.md — per-suite timings, the known
flake, and the four ways a test here wastes minutes.
Docs
docs/RPC.md— the RPC protocol, verb by verbdocs/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
xpopup. - 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).
