| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A raw firmware dump has no format to detect, so IDA fell back to x86 at address
0. It doesn't fail — it opens, analyses, and finds nothing. An AArch64 image
loaded this way gave 0 functions; told the truth it gives 35.
ida-tui fw.bin --processor arm --base 0x8000000
and per binary in a project, which is what a multi-image firmware actually
needs:
{"path": "app.bin", "processor": "arm", "base": "0x8000000"}
idapro.open_database() already accepted IDA command-line switches; nothing was
passing any. Plumbed BinaryRef -> WorkerPool -> WorkerClient -> worker argv, plus
a load_args for the single-binary path that has no project ref.
base is written the way people say it (0x8000000, int or string, any base).
IDA's -b is in PARAGRAPHS — -b1000 loads at 0x10000 — so BinaryRef.load_args
converts, and a base that isn't 16-byte aligned is refused rather than silently
landing 16x off. ida_args passes anything else through.
Two bugs found by testing the whole path rather than the happy one:
* Project.load() whitelisted path/label when normalising entries, so the load
options were dropped the first time a project was reopened — set a processor,
come back tomorrow, it's gone.
* Re-passing the switches to an EXISTING database makes IDA refuse the open
(rc != 0, no functions). The .i64 already records how the image was loaded, so
the worker skips them once a database exists. My first guard checked
splitext(path) + ".i64" and never fired, because IDA names it "<file>.i64" —
keeping the extension. It checks both spellings now.
Verified on a real AArch64 blob: fresh load 35 functions based at 0x8002440,
reopen 35 again, and the CLI rejects an unaligned or non-numeric --base.
tests: +6 project (options recorded, paragraph conversion, file round-trip,
add() takes them, an ELF passes nothing, hex-string base). 39/0 project, 195/0
scenarios, 30/0 project UI, 36/0 index, 27/0 pool.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The idalib worker is the only backend now, so remove the dead HTTP/supervisor
surface entirely (~2200 lines):
* deleted idatui/client.py (the IDAClient HTTP/JSON-RPC transport + session
manager), idatui/tui.py (the old mcp TUI entry, superseded by launch.py),
spawn.sh, and systemd/ (the supervisor unit).
* deleted the mcp-only tests (stress_client, smoke_client, test_keepalive,
stress_paging, rpc_smoke, serverctl.sh, pane_smoke, test_domain) -- the worker
pilot (tests/test_scenarios.py) supersedes them.
* migrated the tmux RPC harness (idatui/pane.py) to the worker: it spawns
`idatui.launch <binary> --rpc <sock>` instead of the mcp `idatui.tui`, drops
the supervisor auto-start/ensure machinery, and reaps our own worker
(idatui/worker.py) instead of ida_pro_mcp.idalib_server. --db/--url/--no-
ensure-server are gone; --open is required.
* __init__ / __main__ / domain no longer import client (exceptions come from
errors.py, the domain client hint is WorkerClient); pyproject points both
console scripts at idatui.launch; README + ida-tui header describe the
worker-only flow.
What stays (by design): the ida_pro_mcp *package* (the worker reuses its @tool
functions in-process) and server/patch_server.py (the worker injects its custom
tools on startup). Verified: whole package imports + IdaTui constructs + pilot
lists 31 scenarios. The worker pilot (134 pass / 2 known flakes) is the E2E gate.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
The worker reuses ida-pro-mcp's @tool functions, but our custom tools
(heads/read_raw/resolve_names/func_types/del_type/set_lvar_type) only exist
because server/patch_server.py injected them into the installed api_types.py --
which historically only ran via spawn.sh. On a fresh box (or worker-only setup
with spawn.sh gone) those tools would be missing and heads/listing would break.
_ensure_tools_injected() now runs patch_server.main() (idempotent, IDA-free) at
the top of _open_and_register, before ida_pro_mcp.ida_mcp is imported, so the
worker guarantees its own tool surface. Verified under /usr/bin/python: injection
runs and heads/read_raw/resolve_names/func_types land in api_types.py. This is
the prerequisite for deleting spawn.sh from the worker path.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The MCP server sets structuredContent = result if isinstance(result, dict) else
{"result": result} (zeromcp mcp.py:832), and domain.py parses that exact shape —
e.g. function_of() reads payload["result"] from lookup_funcs, which returns a
bare list. Our worker returned the raw list, so function_of got a non-dict and
returned None: hence "F5 — cursor is not inside a defined function" on Tab.
Replicate the rule in the worker's dispatch: dict passes through, anything else
(list/scalar) is wrapped as {"result": ...}. Fixes function_of and every other
list-returning tool (resolve_names, xref_query, list_funcs) in one shot.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The worker's stderr was swallowed by the TUI, so an open failure showed only
"worker exited during startup (code 1)". Now:
* WorkerClient captures the worker's stdout+stderr to /tmp/idatui-worker-*.log
and, on a startup exit, surfaces the last meaningful line in the error (the
worker prints a clean 'WORKER-FATAL: ...' marker; _log_tail prefers it).
* worker.py wraps main() to print that marker + traceback before exiting 1, and
gives an ACTIONABLE open error: "failed to open <bin>: the .i64 is likely held
by a running ida-mcp worker (pkill -f idalib) or wedged (delete .id0/.id1/
.id2/.nam/.til)". Also calls ida_auto.auto_wait() after open to fully match
ida-mcp's session manager (open_database + auto_wait).
Root cause of the reported failure is almost certainly a leftover ida-mcp worker
still holding bash's .i64 from earlier --backend mcp runs: idalib can't open a
database another process has locked. Fix: pkill -f idalib, then retry
--backend worker; the error message now says so instead of "code 1".
|
|
|
step 1
First concrete step off the mcp HTTP transport. Instead of reimplementing ~25
tools, reuse ida-pro-mcp's tool *functions* verbatim and replace only the
transport + process management:
* idatui/worker.py — opens ONE database in-process on the main thread (as idalib
requires), imports ida_pro_mcp (which registers every stock + our patched-in
custom tool against MCP_SERVER), then serves MCP_SERVER.tools.methods[name]
(**args) over a unix socket with length-prefixed pickle. Serial on the main
thread (idalib is single-threaded; tools run inline through execute_sync).
Session-management tools (idb_open/idb_save/server_health/idb_list) are shimmed
since the worker *is* the single session.
* idatui/worker_client.py — WorkerClient exposes the exact surface the app/domain
use on the client (call/call_envelope/connect/set_db/resolve_db/list_sessions/
health/keepalive/close) and returns byte-identical payloads (the worker calls
the same functions IDAClient.call ultimately hits). So domain.py and the app
are UNCHANGED — you just construct a WorkerClient instead of an IDAClient.
Calls are serialized under a lock over one socket; keepalive is a no-op (the
worker is ours and never idles out).
Not wired into the app yet — the mcp path is fully intact.
Verified without idalib: pickle framing round-trips arbitrary payloads incl raw
bytes; WorkerClient has full IDAClient surface; call_envelope produces the
result.structuredContent shape domain.decompile() reads. The idalib E2E
(experiments/worker_smoke.py drives the real domain.Program read path through the
worker) is written but couldn't run here — this sandbox has degraded to reaping
any idalib spawn; the underlying unix-socket protocol already ran clean in the
inproc_spike bench (~50us/call), and the worker dispatches the same tool
functions the HTTP path does, so shapes match by construction.
Next: stand up progress reporting during analysis, then flip _connect/_reconnect
to build a WorkerClient behind a flag and run the pilot suite against it.
|