Sign inSign up

biniamfd/ghidra-headless-rest

By biniamfd

•Updated 2 months ago

https://github.com/biniamf/ai-reverse-engineering/

Image
Security
0

1.6K

biniamfd/ghidra-headless-rest repository overview

⁠Ghidra Headless REST API

Local headless Ghidra REST API — analysis, decompilation, xrefs, strings, and deterministic attack-surface scoring over HTTP.

A local, single-user REST service that wraps Ghidra analyzeHeadless. Upload a binary, and query functions, decompilation, xrefs, imports, strings, types, globals, a bounded call graph, and a deterministic Attack Surface triage index — over HTTP, with no GUI. Usable directly, or as the backend for the Rev·Deck AI reverse-engineering WebUI.

Uploaded binaries are analyzed, never executed.

Source / WebUI: https://github.com/biniamf/ai-reverse-engineering/⁠

⁠Quick start

docker run --rm -p 9090:9090 \
  -v "$(pwd)/data:/data/ghidra_projects" \
  biniamfd/ghidra-headless-rest:latest

Recommended for local use (loopback bind + hardening):

docker run --rm -p 127.0.0.1:9090:9090 \
  -v "$(pwd)/data:/data/ghidra_projects" \
  --security-opt no-new-privileges:true \
  biniamfd/ghidra-headless-rest:latest

Reproducible pin (immutable digest):

docker run --rm -p 127.0.0.1:9090:9090 \
  -v "$(pwd)/data:/data/ghidra_projects" \
  biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3a8448d8ed969079b452306e806f36079c3ddd298f4a618d6e2f1442d
  • Base URL: http://localhost:9090
  • Runs non-root (UID/GID 10001); container listens on 0.0.0.0:9090.
  • Analysis artifacts persist under the mounted /data/ghidra_projects.
  • Ghidra 11.3.2 · analyzer ghidra-11.3.2 · artifact schema 2.1.
⁠Tags
TagMeaning
latestCurrent stable (= 1.2.1)
1.2.1Pinned current release
1.0.0Previous stable (rollback)
0.2-legacyPre-1.0 build (rollback)

⁠Versions & capabilities

GET /v1/health          → { "status": "ok" }
GET /v1/version         → service / API / artifact / analyzer versions
GET /v1/capabilities    → advertised features + tool names

/v1/capabilities reports capabilities (service flags) and features (client-facing flags: summary, types, globals, annotations, callgraph, multipart_upload, attack_surface) so clients can feature-detect.

⁠Legacy tools (unchanged, backward compatible)

POST JSON to /tools/<name>. These names, request fields, and response container types are frozen for compatibility.

EndpointMethodDescriptionParametersReturns
/tools/analyzePOSTUpload a base64-encoded binary and start analysis.file_b64 (string, required)
filename (string, required)
job_id (string)
status (string)
/tools/statusPOSTGet status for a job.job_id (string, required)job_id (string)
status (string) – queued | running | done | failed | cancelled | interrupted
/tools/list_functionsPOSTList discovered functions.job_id (string, required)
offset/limit (int, optional)
functions (array) of { name, addr, size, signature, … }
/tools/decompile_functionPOSTDecompiled pseudocode for a function.job_id (string, required)
addr (string, required — hex, padded or unpadded)
Decompiled C text (text/plain)
/tools/get_xrefsPOSTCallers and callees for a function.job_id (string, required)
addr (string, required)
addr (string)
xrefs (object) – to / from
/tools/list_importsPOSTImported symbols/libraries.job_id (string, required)imports (array) of { name, address, library, … }
/tools/list_stringsPOSTExtracted printable strings.job_id (string, required)
min_length (int, optional)
count (int)
strings (array)
/tools/query_artifactsPOSTSubstring/regex query over artifacts.job_id (string, required)
query (string, required)
regex (bool, optional)
count (int)
matches (array)

Note: status returns done (not completed). Direct legacy routes POST /analyze, POST /analyze_b64, GET /jobs, GET /status/{job_id}, GET /results/{job_id}/…, and POST /query also remain available.

⁠v1 REST API (new)

⁠Job lifecycle
POST   /v1/jobs                    multipart upload (field: file) ?persist=&force=
POST   /v1/jobs/base64             { file_b64, filename, persist, force }
GET    /v1/jobs                    paginated job list
GET    /v1/jobs/{id}               job metadata
POST   /v1/jobs/{id}/cancel        cancel a queued/running job
DELETE /v1/jobs/{id}               delete a terminal job + its artifacts

Upload returns { job_id, status:"queued" }, or an immediate { status:"done", cache_hit:true, reused_job_id } when an identical binary was already analyzed (SHA-256 dedup). Poll until a terminal state (done | failed | cancelled | interrupted).

⁠Results & analysis
GET /v1/results/{id}/summary                       program summary + counts
GET /v1/results/{id}/functions?offset=&limit=&q=   paginated, with global search
GET /v1/results/{id}/function/{addr}/decompile     structured decompile { addr, status, content }
GET /v1/results/{id}/xrefs/{addr}                  normalized cross-references
GET /v1/results/{id}/graph/{addr}?depth=&limit=    bounded call-graph neighborhood
GET /v1/results/{id}/imports?offset=&limit=        grouped + flat imports
GET /v1/results/{id}/strings?offset=&limit=&min_length=
GET /v1/results/{id}/types?offset=&limit=          structs/enums/unions/typedefs/…
GET /v1/results/{id}/globals?offset=&limit=        global symbols
GET /v1/results/{id}/hexdump/{start}?length=       bounded bytes from exported memory
POST /v1/query                                     bounded substring/regex query
  • Global search: ?q= on /functions matches by name, display name, or address (padded/unpadded, e.g. 0x00401000 == 0x401000) before pagination, so totals reflect the whole program, not one page.
  • Addresses are canonicalized; padded and unpadded forms resolve identically across legacy, tool, and v1 decompile routes.
  • Every large collection is paginated; responses are size-bounded.
  • /v1/jobs/{id}/summary, /v1/jobs/{id}/types, /v1/jobs/{id}/globals exist as aliases of the corresponding /v1/results/… routes.
⁠Analyst annotations (reversible sidecars)
GET   /v1/jobs/{id}/annotations
PUT   /v1/jobs/{id}/annotations              # whole document (If-Match / ETag)
PATCH /v1/jobs/{id}/annotations              # merge
PUT   /v1/jobs/{id}/annotations/{addr}       # single entity

Names/comments/tags/confidence are stored in a revisioned annotations.json sidecar with ETag/If-Match optimistic concurrency (stale writes → 409). The original Ghidra artifacts and project are never modified.

⁠Export
GET /v1/jobs/{id}/export     → deterministic, path-safe ZIP (artifacts + manifest)
⁠Attack Surface — security-sensitive function scoring

Every completed analysis produces a deterministic, explainable triage index ranking functions by security relevance (attack surface, memory safety, command/format/filesystem, crypto, indirect calls, Android/JNI native interop, etc.), stored as security_index.json + an indexed SQLite security.db.

GET  /v1/results/{id}/security/summary
GET  /v1/results/{id}/security/functions?band=&category=&min_score=&q=&rank=&sort=&order=&offset=&limit=
GET  /v1/results/{id}/security/functions/{addr}     # ranked function + signal evidence
POST /v1/jobs/{id}/rescore                          # rebuild index (no Ghidra re-run)
  • Bands: critical | high | medium | low. Every score carries inspectable signals with signed weights, confidence, and evidence references.
  • Existing/older jobs can be upgraded with rescore without re-running Ghidra; a missing/stale/corrupt index returns a bounded, explicit contract.

⚠️ The security score is a deterministic review-prioritization signal — not a vulnerability verdict, proof of exploitability, or an AI judgment. No LLM participates in scoring.

⁠Configuration (environment variables)

Common: DATA_DIR, MAX_UPLOAD_SIZE, MAX_CONCURRENT_ANALYSES, ANALYSIS_TIMEOUT, DECOMPILE_TIMEOUT, RETENTION_SECONDS, SECURITY_SCORER_ENABLED, SECURITY_SCORER_TIMEOUT, SECURITY_MAX_FUNCTIONS.

⁠Scope & safety

Local, single-user static analysis. Binaries are never executed. Bind to loopback for local use; public/multi-user hosting and authentication are out of scope. Runs non-root with a dedicated writable data volume.

⁠Example use case: AI reverse-engineering assistant

This service is standalone — you can drive it directly from curl, scripts, or your own tooling. One reference use case is the Rev·Deck AI assistant, where an LLM agent calls the /tools/* and /v1/* endpoints (with GHIDRA_API_BASE=http://localhost:9090) to analyze binaries conversationally. The architecture below is one integration example, not a requirement for using the API.

AI Assistant Architecture

Tag summary

Content type

Image

Digest

sha256:971591a3a…

Size

1 GB

Last updated

2 months ago

docker pull biniamfd/ghidra-headless-rest