diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 09:13:00 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 09:13:00 -0400 |
| commit | 1e701396b1b6d204dfd8e5a8ddcd3007038029ac (patch) | |
| tree | 8dde895cf9d3e66ce912d01cb1a72ff674dc850f /debug/risc-v-gsp-rm-vm/ghidra | |
| parent | ab48e44832db55fdedadcf44f55fa9904ca36f2f (diff) | |
| download | open-iov-1e701396b1b6d204dfd8e5a8ddcd3007038029ac.tar.gz open-iov-1e701396b1b6d204dfd8e5a8ddcd3007038029ac.zip | |
debug: green-eye wave 2 - injection SDK, Frida-like calls, Ghidra control bridge, long-term docs
Adds to debug/risc-v-gsp-rm-vm/:
- Session.call / run_until: Frida-like remote function calls and run-to-cursor,
with register restoration; verified live (rpc_ping -> 0xaffe).
- symbol+offset target resolution (gsp_ctrl+0x10) across all APIs and the REPL.
- Injection SDK (sdk/) and 'greeneye inject': position-independent RV64 service
blobs (verified zero relocations), loaded into RAM, registered in the demo
handler table, and invoked either directly or through the firmware mailbox
dispatcher. Demo ABI extended: mailbox 16..23 selects slots, arg0 carries the
service command, arg2 added (struct now 0x190; schema updated).
- Ghidra control bridge (greeneye ghidra + GreenEyeControl.java): run-until-here,
breakpoints, memory/register access, remote calls, hit export; verified
end-to-end with Ghidra headless (run_until reached rpc_sum from a GhidraScript).
- MarkGreenEyeHits.java fixed for the Ghidra 12 createBookmark API; verified
headless together with ExportForGreenEye.java.
- Analyzer renders a QEMU first-fault table for probe sessions (fault count,
epc with firmware vaddr translation, access address/size, reason).
- Tests now 35 green, including injection, Ghidra bridge, remote call, run-until
and REPL command smoke tests.
- Documentation: docs/11-16 (GSP RM service developer guide, injection SDK
specification, Ghidra control, adversarial interoperability guide, agentic
GSP IDE, custom simulator design) and GREEN-EYE-ROADMAP.md (long-term H1-H7
program); doc maps and the project RISC-V VM doc updated.
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/ghidra')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/ghidra/GreenEyeControl.java | 187 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java | 3 |
2 files changed, 188 insertions, 2 deletions
diff --git a/debug/risc-v-gsp-rm-vm/ghidra/GreenEyeControl.java b/debug/risc-v-gsp-rm-vm/ghidra/GreenEyeControl.java new file mode 100644 index 0000000..abeee82 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/ghidra/GreenEyeControl.java @@ -0,0 +1,187 @@ +// GreenEyeControl - drive a live green-eye session from Ghidra. +// +// The script connects to the green-eye JSON-lines bridge (start it with +// `./greeneye.sh ghidra [--kernel <elf>]`), and exposes the operations a +// reverse engineer needs while reading the decompiler: +// +// ping() - check the bridge +// info() - session name, pc, symbol count, recording path +// resolve(name) - symbol/expression -> runtime address +// setBreakpointHere() - software breakpoint at currentAddress +// setBreakpointAt(addr) / clearBreakpointAt(addr) +// runUntilHere() - resume and stop when execution reaches currentAddress +// runUntil(addr) - same for an explicit address (hex string) +// readMemoryHere(size) - hex bytes at currentAddress +// readRegister(name) / writeRegister(name, value) +// callFunction(addr, 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 +// +// Usage (Ghidra headless): +// analyzeHeadless <proj> <name> -process <program> -noanalysis \ +// -scriptPath debug/risc-v-gsp-rm-vm/ghidra \ +// -postScript GreenEyeControl.java <bridge-port> [address] +// +// The optional address argument makes the script run until that address and +// print the hits; in the GUI, omit it to use the current cursor address. +// +//@category OpenIOV +//@menupath Tools.OpenIOV.GreenEyeControl + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.Address; + +public class GreenEyeControl extends GhidraScript { + + private Socket socket; + private BufferedReader reader; + private PrintWriter writer; + private int requestId = 0; + + private static final Pattern OK = Pattern.compile("\"ok\"\\s*:\\s*(true|false)"); + private static final Pattern RESULT = Pattern.compile("\"result\"\\s*:\\s*(\\{.*\\}|\\[.*\\]|\"[^\"]*\"|[^,}]+)"); + + @Override + public void run() throws Exception { + String[] args = getScriptArgs(); + if (args.length < 1) { + println("usage: GreenEyeControl.java <bridge-port> [address]"); + return; + } + int port = Integer.parseInt(args[0]); + try { + connect(port); + println("green-eye: " + info()); + String target = args.length > 1 ? args[1] : null; + if (target == null && currentAddress != null) { + target = "0x" + Long.toHexString(currentAddress.getOffset()); + } + if (target != null) { + println("green-eye run_until " + target + " -> " + runUntil(target)); + println("green-eye hits: " + listHits()); + } + } finally { + disconnect(); + } + } + + // -- connection -------------------------------------------------------- + + public void connect(int port) throws Exception { + socket = new Socket("127.0.0.1", port); + reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + writer = new PrintWriter(socket.getOutputStream(), true); + } + + public void disconnect() throws Exception { + if (socket != null) { + socket.close(); + } + } + + /** Send one bridge command and return the raw response line. */ + public String send(String cmd, String argsJson) throws Exception { + requestId += 1; + String request = "{\"id\":" + requestId + ",\"cmd\":\"" + cmd + "\",\"args\":" + + (argsJson == null || argsJson.isEmpty() ? "{}" : argsJson) + "}"; + writer.println(request); + String response = reader.readLine(); + if (response == null) { + throw new Exception("green-eye bridge closed the connection"); + } + Matcher ok = OK.matcher(response); + if (ok.find() && "false".equals(ok.group(1))) { + throw new Exception("green-eye error: " + response); + } + return response; + } + + /** Extract the `result` field as a string (numbers, objects or lists). */ + public String resultOf(String response) { + Matcher matcher = RESULT.matcher(response); + return matcher.find() ? matcher.group(1) : response; + } + + // -- documented operations -------------------------------------------- + + public String ping() throws Exception { + return resultOf(send("ping", null)); + } + + public String info() throws Exception { + return resultOf(send("info", null)); + } + + public String resolve(String name) throws Exception { + return resultOf(send("resolve", "{\"name\":\"" + name + "\"}")); + } + + public String setBreakpointAt(String address) throws Exception { + return resultOf(send("bp_set", "{\"target\":\"" + address + "\"}")); + } + + public String setBreakpointHere() throws Exception { + return setBreakpointAt(addressHex(currentAddress)); + } + + public String clearBreakpointAt(long address) throws Exception { + return resultOf(send("bp_clear", "{\"addr\":" + address + "}")); + } + + public String runUntil(String address) throws Exception { + return resultOf(send("run_until", "{\"target\":\"" + address + "\",\"timeout\":10}")); + } + + public String runUntilHere() throws Exception { + return runUntil(addressHex(currentAddress)); + } + + public String readMemoryHere(int size) throws Exception { + return resultOf(send("read_mem", "{\"addr\":\"" + addressHex(currentAddress) + "\",\"size\":" + size + "}")); + } + + public String readRegister(String name) throws Exception { + return resultOf(send("read_reg", "{\"name\":\"" + name + "\"}")); + } + + public String writeRegister(String name, long value) throws Exception { + return resultOf(send("write_reg", "{\"name\":\"" + name + "\",\"value\":" + value + "}")); + } + + public String callFunction(String address, long... args) throws Exception { + StringBuilder json = new StringBuilder("{\"target\":\"" + address + "\",\"args\":["); + for (int i = 0; i < args.length; i++) { + if (i > 0) { + json.append(","); + } + json.append(args[i]); + } + json.append("]}"); + return resultOf(send("call", json.toString())); + } + + public String snapshotHere(int size) throws Exception { + return resultOf(send("snapshot", + "{\"addr\":\"" + addressHex(currentAddress) + "\",\"size\":" + size + ",\"label\":\"ghidra\"}")); + } + + public String listHits() throws Exception { + return resultOf(send("hits", null)); + } + + public String exportHits(String path) throws Exception { + return resultOf(send("export_hits", "{\"path\":\"" + path + "\"}")); + } + + private String addressHex(Address address) { + return "0x" + Long.toHexString(address.getOffset()); + } +} diff --git a/debug/risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java b/debug/risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java index c99c3e1..b140ddd 100644 --- a/debug/risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java +++ b/debug/risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java @@ -70,8 +70,7 @@ public class MarkGreenEyeHits extends GhidraScript { } else { missed++; } - createBookmark(address, "Note", "green-eye", - hits + " runtime hit(s)" + (symbol.isEmpty() ? "" : " " + symbol)); + createBookmark(address, "Note", "green-eye"); } } println("green-eye: annotated " + marked + " functions (" + missed |
