summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/docs/13-GHIDRA-CONTROL.md
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/docs/13-GHIDRA-CONTROL.md')
-rw-r--r--debug/risc-v-gsp-rm-vm/docs/13-GHIDRA-CONTROL.md154
1 files changed, 154 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/docs/13-GHIDRA-CONTROL.md b/debug/risc-v-gsp-rm-vm/docs/13-GHIDRA-CONTROL.md
new file mode 100644
index 0000000..26e534d
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/docs/13-GHIDRA-CONTROL.md
@@ -0,0 +1,154 @@
+# 13 — Ghidra control bridge
+
+> **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status.
+
+The bridge lets a GhidraScript drive a live green-eye session: execute the
+target until a point **chosen in the decompiler**, set/clear breakpoints, read
+memory and registers, make remote calls, and export hit lists back into the
+Ghidra project. Nothing about the firmware has to be rebuilt or re-hosted to
+answer "does execution actually reach this function?".
+
+```
+Ghidra (Java script) ── JSON lines over localhost ──► greeneye gdbridge ──► Session ──► QEMU
+ ▲ │
+ └──────── greeneye_hits.tsv ── MarkGreenEyeHits.java ◄──── recorded session ┘
+```
+
+## 1. Quickstart
+
+```bash
+# Terminal 1: a live target + bridge (records by default)
+./greeneye.sh ghidra --kernel firmware/uproc_demo/build/uproc_demo.elf --bridge-port 7000
+# prints: ghidra bridge on 127.0.0.1:7000
+
+# Terminal 2: Ghidra headless, run until the decompiler's address
+analyzeHeadless <proj> <name> -process <program> -noanalysis \
+ -scriptPath open-iov/debug/risc-v-gsp-rm-vm/ghidra \
+ -postScript GreenEyeControl.java 7000 0x800000ce
+```
+
+In the GUI, load `GreenEyeControl.java` from the Script Manager and supply the
+port in the script arguments; with no address argument it uses the current
+cursor (`currentAddress`).
+
+## 2. `GreenEyeControl.java` functions
+
+All functions exist on the script class and can be called from the script's
+own `run()` or from other scripts via the bridge `send()` method.
+
+| Function | Effect |
+|---|---|
+| `ping()` | `pong` if the bridge is alive |
+| `info()` | session name, pc, symbol count, recording path |
+| `resolve(name)` | symbol or `name+0xNN` → runtime address |
+| `setBreakpointAt(hexAddr)` / `setBreakpointHere()` | install a hook (prints on hit) |
+| `clearBreakpointAt(longAddr)` | remove breakpoints at an address |
+| `runUntil(hexAddr)` / `runUntilHere()` | resume and stop at the address; returns `{addr, symbol, kind}` |
+| `readMemoryHere(size)` | hex bytes at the cursor |
+| `readRegister(name)` / `writeRegister(name, value)` | register access |
+| `callFunction(hexAddr, args...)` | Frida-like remote call; returns `a0` |
+| `snapshotHere(size)` | record a memory snapshot in the session |
+| `listHits()` | breakpoint hit counts from the session |
+| `exportHits(path)` | write `greeneye_hits.tsv` for `MarkGreenEyeHits.java` |
+| `send(cmd, argsJson)` / `resultOf(response)` | raw bridge access for new workflows |
+
+Example additions inside `run()`:
+
+```java
+setBreakpointAt("0x800000ce");
+println(runUntil("0x800000ce")); // {addr:..., symbol:"rpc_sum", kind:"breakpoint"}
+println(readMemoryHere(16));
+println(callFunction("0x80000048", 0x1111L)); // rpc_ping -> 0xaffe
+println(exportHits("/tmp/greeneye_hits.tsv"));
+```
+
+## 3. Bridge protocol (specification)
+
+One JSON object per line; one response per request, same `id`.
+
+Request: `{"id": <int>, "cmd": "<name>", "args": {...}}`
+Response: `{"id": <int>, "ok": true, "result": ...}` or
+`{"id": <int>, "ok": false, "error": "..."}`.
+
+| `cmd` | args | result |
+|---|---|---|
+| `ping` | — | `"pong"` |
+| `info` | — | `{name, pc, symbols, recording}` |
+| `resolve` | `{name}` | address (int) |
+| `bp_set` | `{target, kind?}` | address |
+| `bp_clear` | `{addr}` | `true` |
+| `bp_list` | — | `[{addr, label, hits, kind}]` |
+| `watch_set` | `{target, size?, kind?}` | address |
+| `run_until` | `{target, timeout?}` | `{addr, symbol, kind}` |
+| `run` | `{duration?, max_stops?}` | stops |
+| `step` | `{count?}` | `[pc, ...]` |
+| `read_mem` | `{addr, size?}` | hex string |
+| `write_mem` | `{addr, hex}` | `true` |
+| `read_reg` | `{name}` | int |
+| `write_reg` | `{name, value}` | `true` |
+| `call` | `{target, args?, timeout?}` | return value |
+| `snapshot` | `{addr, size, label?}` | label |
+| `note` | `{text}` | `true` |
+| `hits` | — | `[{addr, hits, symbol}]` |
+| `export_hits` | `{path}` | absolute path |
+| `close` | — | `true` (closes the connection and stops the bridge) |
+
+`target`/`addr` accept `"0x..."`, a symbol name, or `name+0xNN`.
+
+## 4. Workflows
+
+### 4.1 Execute until here
+1. Start `greeneye ghidra` on the firmware image and load symbols
+ (`symbols export` from the Ghidra index) so hits are named.
+2. Put the cursor on a function in Ghidra and run `GreenEyeControl.java`.
+3. `runUntilHere()` sets a temporary breakpoint, services intervening stops
+ (including your hooks/watchpoints), and reports the symbol resolved at the
+ stop. If the function is never reached, the bridge returns a timeout error.
+
+### 4.2 Annotate the project with runtime evidence
+1. Run a session (any CLI/scripted path) and `exportHits("/tmp/hits.tsv")`.
+2. `MarkGreenEyeHits.java /tmp/hits.tsv` adds plate comments and bookmarks in
+ the Ghidra project.
+3. Optionally export a symbol DB from Ghidra (`ExportForGreenEye.java`) and
+ feed it back with `symbols export --index`.
+
+### 4.3 Remote-call driven experiments
+Use `callFunction()` to invoke firmware or injected-service functions from
+the decompiler (unit-test style), then inspect memory with
+`readMemoryHere()` and the recorded session.
+
+## 5. Address conventions
+
+Ghidra shows **program addresses** (e.g. libcuda file vaddr + image base).
+green-eye resolves **runtime addresses**. For firmware linked at its load
+address these coincide; otherwise export symbols with the right bias
+(docs/05-GHIDRA-BRIDGE.md) and prefer `resolve(name)` over hardcoded numbers.
+
+## 6. Limitations (v1)
+
+* Request/response only: the script pulls state; the bridge does not push
+ asynchronous hit events. Poll `hits`/`bp_list` between commands.
+* One command at a time per connection; multiple clients are accepted but are
+ not synchronised with each other.
+* `watch_set` exists on the bridge but has no dedicated Java wrapper yet —
+ call `send("watch_set", ...)` directly.
+* The bridge is unauthenticated **by design**: localhost only, never expose
+ it on a network interface.
+
+## 7. Troubleshooting
+
+| Symptom | Cause / fix |
+|---|---|
+| `Connection refused` | bridge not started, wrong port, or it exited (`close`) |
+| `green-eye bridge closed the connection` | the server was stopped or the target session closed |
+| timeout on `run_until` | address not executed; check symbol bias and that the target is not wedged |
+| empty `hits` | the session was started without recording; use the CLI (records by default) |
+| Java compile error on script load | Ghidra API drift; the script targets Ghidra 11/12 `GhidraScript` |
+
+## 8. Roadmap
+
+* Push notifications for breakpoint hits (server → Ghidra).
+* Decompiler annotations from live watchpoints (value changes per field).
+* A bridge client that lets multiple scripts share a session with a command
+ queue.
+* Direct MCP exposure of the same command set (roadmap R3).