aboutsummaryrefslogtreecommitdiffstats
ModeNameSize
-rw-r--r--.git-blame-ignore-revs213logstatsplainblame
d---------.githooks38logstatsplain
-rw-r--r--.gitignore455logstatsplainblame
-rw-r--r--CONTRIBUTING.md3169logstatsplainblame
-rw-r--r--LICENSE1079logstatsplainblame
-rw-r--r--README.md6430logstatsplainblame
-rw-r--r--TODO3497logstatsplainblame
d---------docs290logstatsplain
d---------experiments838logstatsplain
-rwxr-xr-xida-tui827logstatsplainblame
d---------idatui1138logstatsplain
-rw-r--r--logo-trans.png224298logstatsplainblame
-rw-r--r--logo.ans40439logstatsplainblame
-rw-r--r--logo.png818833logstatsplainblame
-rw-r--r--pyproject.toml1126logstatsplainblame
-rw-r--r--ruff.toml1982logstatsplainblame
d---------tests948logstatsplain
d---------tools122logstatsplain
-rw-r--r--uv.lock20624logstatsplainblame

ida-tui

IDA Pro in a terminal. Listing, decompiler, graph — keyboard-first, mouse-capable.

disasm · pseudocode · cfg · hex · strings · structs · traces · rpc


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.

uv sync

That pulls ida-nexus 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:

uvx ida-hcli plugin install ida-nexus

Hacking on ida-nexus itself? Point at a checkout instead:

uv add --editable ../ida-nexus

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 IDA Nexus starts or shares a managed idalib worker. Quitting drops the lease and leaves everyone else alone. Changes made in the GUI or another client arrive over IDA Nexus's IDB event stream; ida-tui debounces bursts and refreshes its cached views automatically. On quit with unsaved changes, a final managed-worker lease can discard the session without saving; GUI-backed or still-shared sessions leave that final decision with their owner or remaining clients. If the owning GUI or worker closes, ida-tui never replaces it by spawning a headless worker implicitly. It keeps the cached view disconnected until a matching owner is reopened and an attach-only rediscovery succeeds. Remote operations are typed, source-backed Python functions. ida-nexus installs their content-addressed modules once per IDA Python interpreter, so ida-tui keeps normal refactorable source without paying to resend hot listing/decompiler code. Operation attribution is also a per-call provider rather than a fixed string, so it can evolve from IDA TUI to labels such as IDA TUI: alice.

Headerless blobs need a hint, or IDA assumes x86 at address 0 and analyses nothing:

./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+r ctrl+s ctrl+l q refresh view · 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 trace and move through time. Both code views paint the execution trail; the dock shows registers and stack as of that instant.

./ida-tui /path/to/binary --trace trace.0.log     # ] [ step · } { step over

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 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

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:

~/ida-venv/bin/python tests/test_scenarios.py targets/echo --only hex,rename

sl0p.foo