summaryrefslogtreecommitdiffstats
path: root/idatui/drive.py (follow)
Commit message (Collapse)AuthorAgeFilesLines
* app: the view mode is a type, and 'disasm' is goneblasty2 days1-5/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | _active was a bare string with 49 comparisons across four modules and a fifth value nobody meant to keep. "disasm" was assigned on exactly one path -- a decompile that failed with nowhere to return to -- and named the same widget as "listing". Four sites understood it; five compared against "listing" alone and silently took the wrong branch: * Tab out of a failed decompile set "listing" instead of "decomp", so the first press appeared to do nothing. * rpc.py carried a workaround for a mode change that never arrived, keyed on being ALREADY in the ghost state -- so it fired in the rare case and not in the common one. Now keyed on LISTING, which is the case that happens. * drive.py asked the socket to show it "disasm", a value the app will now never report, and would have toggled twice and given up. ViewMode is a StrEnum on purpose: _active goes straight to drivers as cursor.kind and the pilot compares it to plain strings, so members being strings keeps every payload and comparison working. What it buys is one place that says which modes exist, and an AttributeError instead of silence on a typo. Read it through is_listing/is_decomp/is_hex/is_graph/in_code rather than ==. The bare comparisons are what let the ghost hide, and they are what the next mode would have to hunt down -- adding "graph" already cost one crash that way (_active_code_view returning None when a prompt closed). view_modes_all_handled walks the enum and asks the app the questions it asks itself. Verified it bites: adding a fifth unhandled member fails it twice. 746 checks, 142.3s.
* rpc: an opfmt verb, and 'drive fmt'blasty3 days1-1/+20
| | | | | | | | mode is cycle/back/show or an explicit format. 'show' reports the current format and the stops on offer without editing, which is what a driver needs: the rendered text alone can't be trusted (a listing read before an ARM/Thumb switch shows the old decoding). 'word' puts the cursor on a token first, so a literal can be named instead of steered to.
* drive: a note went to address 0, and a slow edit was reported as a failureagent7 days1-3/+8
| | | | | | | | | | | | | | | | | | | Two bugs found driving a 65KB ARM firmware image (one flat 42k-line listing, no ELF sections to break it up): `note` did goto + `cursor line=0` before commenting. Line 0 is the top of the function only in the DECOMPILER; in the listing it is the top of the SEGMENT, so every note landed at address 0 -- and scrolling a 42k-line listing there took so long the call timed out, which read as "comments are broken". goto already lands on the function's first line, so the cursor call just goes; note now also reports where it landed. The client's 90s timeout was too tight for the same reason: comments on that listing take 26-106s (the rebuild has no function boundary to stop at), so the CLI reported "no response ... server busy or the op is hung" for edits that had already been applied. Believing a successful edit failed is the worse error -- the driver redoes it, or "fixes" what was never broken. Default is now 300s; IDATUI_RPC_TIMEOUT still overrides.
* rpc: make a raw firmware image drivable (load options, define, bulk symbols)user7 days1-1/+35
| | | | | | | | | | | | | | | | | | | | | | | | Opening a headerless blob was the one workflow that fell out of the driving surface entirely, and each gap hid the next: - `pane spawn` couldn't pass --processor/--base/--ida-args, so the pane came up "ready" with zero functions (x86 at 0) and the only way through was to hand-write a project file. It now forwards them to idatui.launch. - c/p/t/T (code, function, ARM<->Thumb, vector scan) existed as listing bindings with no verb, so a driver had to guess raw keys -- and raw keys are swallowed by whatever modal happens to be up. `define {kind,target?}` goes through the app's own edit worker and reports what IDA actually did. - every name went through the typed rename prompt: a navigation (listing page + decompile) plus two prompt round-trips each. A 427-symbol map took tens of minutes of driving. `rename_many {items|file}` hands IDA's rename tool the whole list in one call (371 symbols in 3s) and refreshes the caches and the function table once. drive gains `define <kind> [target...]` and `syms <file.json>`. Verified live against a real pane (tests/test_rawimage_rpc.py, 13 checks: spawn load options, define thumb/func + unknown-kind rejection, rename_many from a file and inline, with resolve/functions readback).
* projects phase 4: cross-binary back, linkage-guided pre-warm, project verbsblasty2026-07-251-0/+26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three things, all following from phase 3 making cross-binary jumps ordinary. **A cross-binary jump was a one-way door.** Nav history is per-binary, so arriving in another binary — a project search hit, or now following an import into the library that implements it — landed you in an empty history with nothing to take you back. _switch_then_goto records the binary it came FROM, and action_back falls through to that hop once local history is spent: Esc walks back through the function you were in, then the binary you were in. Manual Ctrl+O switching records nothing, because that isn't navigation. **Pre-warm follows the linkage graph, not list order.** _prewarm_provider warms the binary providing the most of this one's imports — where a follow is most likely to go, so its startup is paid before you ask for it. "Next in the list" would have been arbitrary; phase 3 gave us something better to ask. pool.prewarm() refuses rather than making room. Evicting a binary the user visited to speculatively load one they haven't is a straight downgrade, and it throws away that binary's caches as well; at a tight budget pre-warm just does nothing. The cost of a worker that doesn't exist yet can only be estimated, so it uses the largest resident one (same program, different database) — and if that estimate proves wrong, the speculative worker is the one evicted, never a chosen one. **Driving a project.** pane spawn --project FILE [--open BIN]; `binaries` lists the inventory (active / resident / indexed / where Esc returns to) and `switch {binary,addr?}` makes another active — with an address it takes the search-hit path, so it records a hop. state gains `binary` and `hops`, which it should have had the moment project mode existed. Verified on real sessions: drive binaries/switch against an echo+cat project pane; Esc crossing back from a switch; and prewarm on echo+libc picking libc (provider of echo's imports) and warming it after an evict. tests: +5 pool (prewarm warms, no-ops when resident, refuses at budget, evicts nothing when refusing, ignores unknown labels) and +4 project UI (jump records the hop, Esc crosses back, hop consumed). Confirmed the Esc-back checks fail with the branch removed. 195/0 scenarios, 27/0 project UI, 36/0 index, 27/0 pool, 33/0 project. Left open: project-level persistence across sessions.
* drive: make `dis` drive the live UI too (generalize _show_decomp -> _show_view)user2026-07-111-7/+11
|
* drive pc: render pseudocode in the TUI, not just the driveruser2026-07-111-3/+32
| | | | | | | | The pc command called only the read-only pseudocode RPC (runs off the UI loop, never touches the screen), so LLM-driven sessions showed nothing on the live pane. Compose goto + toggle_view + search so the real TUI navigates to the function, makes the decomp pane the visibly-active view, and jumps the cursor to the needle — then return the same text as before.
* drive: add save + retype; point docs/skills at the ergonomic frontendblasty2026-07-101-2/+15
| | | | | | | | Round out idatui.drive with 'save' and 'retype <fn> <proto>' so the whole common RE loop (orient/understand/act/persist) has a terse command. Update docs/RPC.md and both skills (idatui, idatui-rpc) to recommend idatui.drive as the day-to-day driving surface, with rpcclient/raw as the fallback for unwrapped verbs.
* drive: ergonomic RE helper over the RPC socket (terse text, auto socket)blasty2026-07-101-0/+218
Driving via rpcclient meant re-exporting the socket every fresh shell, long invocations, and piping ~every call through python -c to pull one field or grep pseudocode. idatui.drive fixes that: it auto-resolves the socket (the single live pane from the registry), prints compact text instead of JSON, and bundles the common gestures: where | go | pc <fn> [substr] | dis <fn> [n] | callees/callers <fn> | names <substr> | rename <old> <new> | mv old=new... | note <fn> <text> | screen | raw <method> k=v So 'goto+rename+parse' becomes 'drive rename old new', and 'pseudocode | python -c grep' becomes 'drive pc fn needle'. rpcclient stays the raw transport. rpc_smoke drives it end-to-end (29 green).