diff options
Diffstat (limited to 'idatui/rpc.py')
| -rw-r--r-- | idatui/rpc.py | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/idatui/rpc.py b/idatui/rpc.py index 59eec86..7741f48 100644 --- a/idatui/rpc.py +++ b/idatui/rpc.py @@ -72,6 +72,9 @@ METHODS = { "search": "{term,direction?=1} incremental search in the code view", "select": "{index?} choose the highlighted/nth item in the open modal", "save": "persist the .i64 (Ctrl+S)", + "trace": "{seek|goto|step,over?} navigate the execution trace (seek '!50' = percent)", + "binaries": "-> project binaries {label,active,resident,indexed} (project mode)", + "switch": "{binary,addr?} make another project binary active (addr also jumps)", "close": "dismiss a modal (Escape)", "move": "{dir,n?=1} fast movement (down/up/.../pagedown)", "cursor": "{line?,col?} set the code-pane cursor directly", @@ -168,12 +171,14 @@ def snapshot(app) -> dict[str, Any]: pass return { "active": app._active, - "pref": app._pref, + "pref": app._code_mode(), # kept for wire compat; a constant now "function": ({"ea": cur.ea, "name": cur.name} if cur else None), "cursor": _cursor_info(app, w), "status": st, "filter": app._filter_term, + "binary": app._binary, # None outside project mode "nav_depth": len(app._nav), + "hops": list(getattr(app, "_hops", [])), "dirty": bool(app._dirty), "modal": _modal_snapshot(app), **_readiness(app), @@ -526,6 +531,74 @@ class RpcServer: if method == "functions": return functions(app, params.get("filter"), int(params.get("limit", 50))) + # -- projects ------------------------------------------------------ # + if method == "trace": + if app._trace is None: + raise ValueError("no trace loaded (launch with --trace FILE)") + t = app._trace + if "seek" in params: + v = params["seek"] + # "!50" seeks a percentage, like Tenet's timestamp shell. + if isinstance(v, str) and v.startswith("!"): + idx = int(float(v[1:]) * (t.length - 1) / 100.0) + else: + idx = int(str(v).replace(",", ""), 0) if isinstance(v, str) else int(v) + app._seek(idx) + elif "goto" in params: # first execution of an address/name + tgt = params["goto"] + ea = (int(str(tgt), 0) if str(tgt).lower().startswith("0x") + else app.program.resolve(str(tgt))) + first = t.first_execution(ea) + if first is None: + raise ValueError(f"{tgt} never executed in this trace") + app._seek(first) + elif "step" in params: + n = int(params.get("step") or 1) + over = bool(params.get("over")) + for _ in range(abs(n)): + (app._step_over if over else app._step)(1 if n > 0 else -1) + await settle(app, timeout=float(params.get("timeout", 20.0))) + snap = snapshot(app) + snap["trace"] = {"idx": app._t, "length": t.length, + "pc": hex(t.ip(app._t)), + "changed": sorted(t.changed(app._t))} + return snap + + if method == "binaries": + if app._project is None: + raise ValueError("not a project session (launch with --project)") + counts = app._index.counts() if app._index is not None else {} + resident = set(app._pool.resident()) if app._pool is not None else set() + return {"active": app._binary, "hops": list(app._hops), + "binaries": [{"label": r.label, "source": r.source, + "active": r.label == app._binary, + "resident": r.label in resident, + "indexed": int(counts.get(r.label, 0))} + for r in app._project.refs]} + + if method == "switch": + if app._project is None: + raise ValueError("not a project session (launch with --project)") + label = str(params.get("binary") or params.get("label") or "") + if app._project.by_label(label) is None: + have = ", ".join(r.label for r in app._project.refs) + raise ValueError(f"no such binary {label!r} (have: {have})") + addr = params.get("addr") + if label == app._binary and addr is None: + return snapshot(app) + if addr is None: + app._switch_binary(label) + else: + # Same path a project search hit takes, so it records a hop and + # Esc comes back here. + app._switch_then_goto(label, int(str(addr), 0) + if isinstance(addr, str) else int(addr)) + await settle(app, lambda: app._binary == label + and app._func_index is not None + and app._func_index.complete, + timeout=float(params.get("timeout", 300.0))) + return snapshot(app) + # -- structured introspection (heavy: run off the UI loop) -------- # loop = asyncio.get_running_loop() if method == "pseudocode": |
