commit 57597e7d6d040ece76630f6e6ca40c8a12d521dc Author: hermes Date: Tue Jul 28 12:01:28 2026 +0000 docs: define initial ghidra-cli product diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..babfde6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/target/ +*.log +*.gpr +*.rep/ +.direnv/ +.envrc + diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..2a51c56 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,43 @@ +# Headless Reverse Engineering + +This context describes how users ask reproducible, read-only questions about +binaries through Ghidra without managing Ghidra's internal project model. + +## Language + +**Sample**: +An immutable sequence of input bytes identified by a cryptographic content +digest. Identical bytes are the same Sample even when they have different file +names or locations. +_Avoid_: Binary, file, target + +**Analysis Profile**: +The declared analysis inputs other than the Sample, including the Ghidra +version, analyzer configuration, language selection, and tool protocol version. +_Avoid_: Configuration, settings + +**Analysis**: +The reusable body of facts Ghidra derives from one Sample under one Analysis +Profile. +_Avoid_: Project, database, workspace + +**Query**: +A read-only question evaluated against an Analysis. A Query cannot change the +Sample or Analysis. +_Avoid_: Command, job, operation + +**Function Selector**: +An unambiguous reference used to choose a function by address or resolved +symbol name. +_Avoid_: Function name, function ID + +**Artifact**: +A durable, schema-versioned representation of a Query result tied to its +Analysis. +_Avoid_: Output, report, result file + +**Analysis Store**: +The tool-owned collection of reusable Analyses and Artifacts. It is disposable +and never contains the authoritative Sample. +_Avoid_: Project directory, cache directory, workspace + diff --git a/PRD.md b/PRD.md new file mode 100644 index 0000000..78958c2 --- /dev/null +++ b/PRD.md @@ -0,0 +1,368 @@ +# Product Requirements Document + +## ghidra-cli + +- Status: Draft +- Revision: 0.1 +- Audience: maintainers and early users + +## Summary + +`ghidra-cli` is a small Rust command-line interface for reproducible, read-only +analysis and decompilation through Ghidra. It hides Ghidra project management +and Java process details behind commands that accept a sample path directly. + +The first release favors a narrow, reliable workflow over a broad command +surface. It is intended to work equally well for a person at a terminal and an +automation agent consuming structured output. + +## Problem + +Ghidra supports headless analysis, but its native interface exposes concepts +such as project directories, project names, import and process modes, script +paths, and JVM configuration. Answering a simple question about a sample +therefore requires orchestration knowledge unrelated to reverse engineering. + +Existing wrappers commonly address this by becoming persistent services with +background JVMs, sockets, job queues, project lifecycle commands, custom query +languages, and mutating operations. Those capabilities are useful in mature +interactive systems, but they increase the number of states that can fail and +make a small tool harder to trust. + +## Product principles + +1. A sample path is enough. Users do not manage Ghidra projects. +2. Read-only by default and throughout the first release. +3. Content determines identity. Moving or renaming a sample does not invalidate + compatible analysis. +4. Structured output is a public interface, not formatted log text. +5. Every failure is explicit, typed, and actionable. +6. No background process is required for the initial command set. +7. Concurrency is introduced only with a concrete concurrent workflow. +8. Ghidra remains the analysis engine; Rust owns orchestration and policy. + +## Users + +### Human analyst + +Wants to inspect an unfamiliar executable, discover functions, and decompile a +selected function without opening the Ghidra GUI or learning its project model. + +### Automation agent + +Needs deterministic commands, bounded output, stable JSON schemas, meaningful +exit codes, and enough provenance to explain where a result came from. + +### Maintainer + +Needs a small dependency graph, reproducible fixtures, a clear Java boundary, +and compatibility failures that are discovered by tests rather than users. + +## Goals for the first release + +- Diagnose whether the local Ghidra and JDK installation is usable. +- Inspect basic sample and analysis metadata. +- List discovered functions with stable identifiers. +- Decompile one selected function. +- Reuse compatible analysis without requiring user-managed project names. +- Emit concise terminal output and versioned JSON. +- Bound Ghidra execution by configurable time and memory limits. +- Operate without network access after dependencies are installed. + +## Non-goals for the first release + +- Replacing Ghidra's decompiler or analysis engine. +- A daemon, TCP bridge, HTTP service, or MCP server. +- Multiple concurrent Ghidra analyses. +- An interactive shell. +- Binary patching or exporting modified executables. +- Renaming symbols, applying types, or changing function signatures. +- Arbitrary Java or Python script execution. +- A custom filtering or query language. +- Automatically downloading Ghidra or a JDK. +- Managing user-visible Ghidra projects. +- Supporting every historical Ghidra version. + +## Command-line experience + +### Installation diagnosis + +```console +$ ghidra-cli doctor +Ghidra 12.1: ready +JDK 21: ready +Analysis store: writable +``` + +`doctor` must not modify a Sample or create an Analysis. It may create and +remove a temporary file inside the Analysis Store to verify writability. + +### Inspect a sample + +```console +$ ghidra-cli inspect ./sample +Sample: 8d969eef... +Format: ELF +Language: x86:LE:64 +Functions: 42 +``` + +If no compatible Analysis exists, the command creates one automatically. The +same bytes at another path reuse the Analysis. Different bytes at the same path +create a different Analysis. + +### List functions + +```console +$ ghidra-cli functions ./sample +ADDRESS SIZE NAME +0x00401000 112 _start +0x00401120 248 main +``` + +The command supports an explicit result limit. It does not implement a +filtering expression language in the first release; JSON consumers can use +existing tools such as `jq`. + +### Decompile a function + +```console +$ ghidra-cli decompile ./sample main +int main(int argc, char **argv) +{ + ... +} +``` + +A function may be selected by address or a uniquely resolved symbol. An +ambiguous symbol is an error and returns the candidates instead of silently +choosing one. + +### Structured output + +```console +$ ghidra-cli --format json inspect ./sample +``` + +JSON uses a versioned envelope: + +```json +{ + "schema_version": 1, + "kind": "inspection", + "provenance": { + "sample_sha256": "", + "ghidra_version": "", + "analysis_profile": "" + }, + "data": {}, + "warnings": [] +} +``` + +Standard output contains only the requested result. Diagnostics and progress +belong on standard error. + +## Functional requirements + +### Sample identity + +- Compute a SHA-256 digest from the Sample bytes. +- Detect if the Sample changes while it is being read. +- Treat identical bytes as the same Sample regardless of path. +- Never write to the Sample. +- Record the observed path as provenance, not identity. + +### Analysis compatibility + +An Analysis is reusable only when its Sample digest and Analysis Profile match. +At minimum, the Analysis Profile includes: + +- Ghidra version. +- Ghidra language and compiler specification. +- Enabled analyzer configuration. +- Java adapter protocol version. + +An incompatible Analysis is preserved until normal retention policy removes it; +it is not overwritten in place. + +### Analysis execution + +- Invoke the official Ghidra headless launcher as a child process. +- Pass requests to a small, bundled Java adapter. +- Exchange structured request and response files rather than scrape logs. +- Capture Ghidra logs separately for diagnostics. +- Terminate the child process after each command in the first release. +- Apply a configurable wall-clock timeout. +- Report whether termination was graceful or forced. + +### Analysis Store + +- Use a platform-appropriate user data location by default. +- Allow an explicit store path through one documented option. +- Use atomic writes for manifests and Artifacts. +- Detect incomplete Analysis creation after interruption. +- Serialize writers for the same Sample and Analysis Profile. +- Treat the entire store as disposable. + +### Output behavior + +- Default to concise human-readable output on a terminal. +- Support explicit JSON output. +- Never change JSON shape based on terminal detection. +- Include schema version and provenance in every JSON response. +- Use hexadecimal addresses with an explicit address space where necessary. +- Place progress and logs on standard error. + +### Errors and exit status + +Distinct error categories must cover: + +- Invalid arguments. +- Missing or unreadable Sample. +- Unsupported or ambiguous format/language. +- Missing or incompatible Ghidra/JDK installation. +- Analysis timeout. +- Ghidra analysis failure. +- Function not found or function selector ambiguous. +- Invalid or incompatible cached Analysis. +- Internal protocol violation. + +The exact numeric exit-code allocation is a design task before implementation. + +## Architecture boundary + +### Rust CLI responsibilities + +- Argument parsing and validation. +- Sample hashing and stable identity. +- Analysis Profile construction. +- Analysis Store layout, locking, and atomicity. +- Child-process lifecycle, timeouts, and log capture. +- Typed request and response schemas. +- Human and JSON rendering. +- Exit-code policy. + +### Java adapter responsibilities + +- Use supported Ghidra APIs. +- Import and analyze a Sample. +- Resolve Function Selectors. +- Extract program and function metadata. +- Invoke the Ghidra decompiler. +- Serialize only the response defined by the protocol. + +The Java adapter must not listen on a network socket, download dependencies, or +execute user-provided scripts. + +## Synchronous execution decision + +The first release uses a synchronous command lifecycle because each invocation +coordinates one Ghidra child process and returns one result. Ghidra performs the +CPU-intensive work; an asynchronous Rust runtime would not make that work +faster. + +The design must not prevent a future asynchronous frontend. A server, parallel +batch analysis, interactive session, or responsive cancellation channel would +constitute a concrete reason to add an async runtime. + +## Security and safety + +- Treat every Sample and every Ghidra-produced value as untrusted input. +- Do not execute the Sample. +- Do not provide arbitrary script execution. +- Do not require network access during analysis. +- Recommend a sandbox or container for untrusted Samples. +- Avoid including Sample bytes or extracted secrets in routine logs. +- Validate all paths crossing the Rust/Java boundary. +- Apply bounded output, memory, and execution-time policies. +- Document the supported Ghidra version and relevant security advisories. + +## Performance expectations + +Correctness and reproducibility take priority over startup latency in the first +release. + +- A compatible cached Analysis should avoid repeating auto-analysis. +- Metadata Queries should reuse exported Artifacts where correctness permits. +- Targeted decompilation may start a new JVM. +- The CLI should explain whether it reused or created an Analysis when verbose + diagnostics are enabled. + +Persistent workers and interactive sessions are deferred until measured usage +shows JVM startup to be the dominant usability problem. + +## Test strategy + +- Unit-test Sample identity, profile identity, store layout, schema validation, + selector parsing, and error mapping without Ghidra. +- Test process lifecycle against a controllable fake child process. +- Maintain tiny, source-controlled fixture programs with known properties. +- Compile fixtures reproducibly for supported architectures. +- Run integration tests against a pinned supported Ghidra release. +- Compare JSON against reviewed golden fixtures after normalizing unstable + values. +- Verify interruption leaves no Analysis that can be mistaken for complete. +- Verify identical bytes at different paths reuse one compatible Analysis. +- Verify changed bytes at the same path do not reuse the old Analysis. + +## Delivery milestones + +### Milestone 0: design + +- Review this PRD and the domain language. +- Choose the project and executable name. +- Choose a license. +- Pin the initially supported Ghidra and JDK versions. +- Define protocol schemas and exit codes. + +### Milestone 1: vertical inspection slice + +- Create the Rust crate and minimal Java adapter. +- Implement `doctor`. +- Implement `inspect`. +- Prove Rust to `analyzeHeadless` to Java to JSON end to end. + +### Milestone 2: function discovery + +- Implement `functions`. +- Define stable address and Function Selector representations. +- Add reproducible native fixtures. + +### Milestone 3: targeted decompilation + +- Implement `decompile`. +- Add ambiguity handling and decompiler time limits. +- Stabilize the first public JSON schema. + +### Milestone 4: packaging + +- Provide a reproducible Nix development and test environment. +- Document installation and sandboxed use. +- Publish the first pre-release. + +## Release acceptance criteria + +The first release is acceptable when: + +- A new user can run `doctor`, `inspect`, `functions`, and `decompile` without + manually creating a Ghidra project. +- Repeating a Query against an unchanged Sample reuses compatible analysis. +- All commands provide versioned JSON and documented exit behavior. +- An interrupted analysis cannot be mistaken for a complete one. +- Tests exercise a real pinned Ghidra release and known fixture binaries. +- No first-release command mutates the Sample or Analysis. +- No analysis command requires a network connection. + +## Open decisions + +- Final repository and executable name. +- License. +- Initially supported Ghidra patch release. +- Whether the first supported host is Linux-only. +- Default Analysis Store retention policy. +- Numeric exit-code allocation. +- JSON Schema publication and compatibility policy. +- Whether Nix is the sole supported installation path for the first + pre-release. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..42e1a39 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# ghidra-cli + +A small, dependable command-line interface for read-only Ghidra analysis. + +The project is currently in its design phase. Its first objective is to make +common reverse-engineering questions easy to ask without exposing Ghidra +project management, JVM lifecycle, or scripting details to the user. + +Proposed usage: + +```console +ghidra-cli doctor +ghidra-cli inspect ./sample +ghidra-cli functions ./sample +ghidra-cli decompile ./sample main +``` + +Each command will identify the sample by its content, reuse compatible cached +analysis, and produce human-readable or versioned JSON output. + +## Documents + +- [PRD.md](./PRD.md) defines the product requirements and initial delivery + scope. +- [CONTEXT.md](./CONTEXT.md) defines the domain language used throughout the + project. + +## Status + +Documentation first. There is intentionally no Rust crate yet. Implementation +begins after the initial requirements and interface are reviewed. + +## License + +No license has been selected yet. Until one is added, all rights are reserved. +