
ai-reverse-engineering — 已更新!
使用Ghidra进行AI辅助逆向工程
Rev·Deck — 借助 Ghidra 的 AI 辅助逆向工程
Rev·Deck 是一款本地、单用户的静态分析工作站。它将证据优先的 Web 界面与 LLM 副驾驶(copilot)相结合,作用于由无头 Ghidra 服务分析过的二进制文件:你可以直接浏览确定性证据(函数、字符串、导入、交叉引用、有界调用图),也可以向助手提出有界问题,而助手的任何事实性论断都必须引用可检查的证据。
被分析的二进制文件绝不会被执行。浏览器只与本 Flask 应用通信;该应用将经过验证的类型化请求代理给 Ghidra 服务。
演示
https://github.com/user-attachments/assets/fba14dc5-7ad5-4137-9349-ed824da64fbe
快速开始(Docker)
cp .env.example .env # set API_BASE and MODEL_NAME; set API_KEY if required
docker compose up --build
Docker Compose 会自动读取 .env 进行变量插值。如果缺少 API_BASE 或 MODEL_NAME,它会在启动前直接失败;API_KEY=not-used 对本地/免密钥提供商仍然有效。整个服务栈会启动这两个服务。打开 http://127.0.0.1:5000。
如果只想运行 Ghidra 服务:
docker pull biniamfd/ghidra-headless-rest:latest # ensure the newest image
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
为了可复现地固定版本,请使用经过测试的发布版摘要(digest),而不是 latest:
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:1.2.1@sha256:971591a3a8448d8ed969079b452306e806f36079c3ddd298f4a618d6e2f1442d
先决条件
- Docker 与 Docker Compose(用于快速开始路径),或 Python 3.10+ 与 Node.js 18+(用于从源码运行)。
- 一个兼容 OpenAI 的 LLM 端点(本地或托管)以及模型名称。
- 公共 Ghidra 镜像:
biniamfd/ghidra-headless-rest:latest。
必需的环境变量
将 .env.example 复制为 .env 并填写以下变量;完整列表与默认值请参阅该文件。
| 变量 | 默认值 | 含义 |
|---|---|---|
API_BASE | 必需 | 兼容 OpenAI 的基础 URL(http/https)。缺失时 Compose 会在启动前失败。 |
API_KEY | not-used | 提供商密钥。绝不会被记入日志或发送到浏览器;not-used 对无密钥的本地提供商有效。 |
MODEL_NAME | 必需 | 所配置端点期望的模型 ID。缺失时 Compose 会在启动前失败。 |
LLM_STREAM | auto | 流式传输方式:auto(流式传输;在产生输出前遇到兼容性错误时回退为一次阻塞式调用)、true(始终流式)、false(始终阻塞)。 |
GHIDRA_API_BASE | http://127.0.0.1:9090 | Ghidra 服务的基础 URL。 |
GHIDRA_IMAGE | biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3... | 通过不可变摘要固定的、已经过测试的发布版本。:latest 也会解析到此摘要;可覆盖以固定其他发布版本。 |
HOST / PORT | 127.0.0.1 / 5000 | 开发服务器绑定地址。 |
MAX_UPLOAD_BYTES | 104857600 | 上传大小上限。 |
CHATS_DIR | webui/chats | 聊天历史记录目录。 |
LLM 提供商
Rev·Deck 通过 OpenAI SDK 与任何兼容 OpenAI 的 Chat Completions 端点通信,配置完全由 API_BASE / API_KEY / MODEL_NAME 决定。其中没有任何提供商特有的请求头、参数或模型逻辑:本地 Ollama 服务器(API_BASE=http://127.0.0.1:11434/v1)、自托管的 vLLM/llama.cpp/LM Studio 端点、OpenAI 本身,或是 OpenRouter 之类的网关,都能以相同方式工作。
.env 提供商配置示例(请使用占位符,切勿提交真实密钥):
# Ollama
API_BASE=http://127.0.0.1:11434/v1
API_KEY=not-used
MODEL_NAME=qwen3:8b
# OpenRouter
API_BASE=https://openrouter.ai/api/v1
API_KEY=replace-with-your-key
MODEL_NAME=anthropic/claude-opus-4.8
# OpenAI
API_BASE=https://api.openai.com/v1
API_KEY=replace-with-your-key
MODEL_NAME=replace-with-a-supported-model-id
# LM Studio, vLLM, or llama.cpp (adjust port/model to the server)
API_BASE=http://127.0.0.1:1234/v1
API_KEY=not-used
MODEL_NAME=replace-with-the-served-model-id
默认情况下(LLM_STREAM=auto),助手请求流式响应,并将令牌(token)实时转发到浏览器。流式传输还能提供更强的取消保证:当你停止响应(或关闭标签页)时,Rev·Deck 会立即关闭底层提供商流,并且不再进行任何工具或模型轮次,从而直接终止上游生成,而不是让它在后台继续运行到完成。
注意事项:
- 计费。 取消会在我们这一侧立即关闭流,但某些托管提供商仍然会对它们已经生成的令牌(或整个补全)计费,无论客户端是否提前断开连接。该保证针对的是不再做更多工作,而非提供商的计费策略。
- 兼容性。 并非每个兼容 OpenAI 的端点都接受「流式 + 工具」。在
auto模式下,如果提供商在产生任何内容或工具调用输出之前,以兼容性错误(HTTP 400/404/405/422)拒绝流式请求,Rev·Deck 会回退为一次阻塞式调用,并在本次进程的剩余时间内记住该选择。认证(401/403)、限流(429)和服务器(5xx)错误不会被视为兼容性问题,而是作为错误呈现,不会被静默重试。设置LLM_STREAM=false可完全跳过流式传输;设置LLM_STREAM=true则强制要求流式传输(无回退)。
使用方法
打开应用并上传二进制文件即可开始一次分析任务。明显是纯文本的内容在发送给 Ghidra 之前会要求确认;只有当内容确实是有意上传的固件/数据而非可执行格式时,才使用显式的原始二进制(raw-binary)覆盖选项。分析完成后,可在两个工作区标签页之间切换:
- 分析(Analysis) — 确定性证据视图:摘要、函数(筛选/分页)、导入、字符串、查询视图、函数检查器(伪代码、交叉引用、有界调用图、十六进制转储),以及——当所连接的 Ghidra 服务支持时——类型、全局变量、sidecar 注解、归档导出,以及带可解释正面/缓解信号与证据覆盖率的确定性攻击面排名。
- 聊天(Chat) — 助手,有以下两种模式:
- 副驾驶(Copilot)(默认):每条消息执行一个有界的步骤/工具调用,适用于临时性问题。
- 自主(Autonomous):启动一个命名且设有预算的工作流,由它自行运行多个有界步骤,并在工作时显示实时活动时间线。
两种模式都接受按任务设置的步骤预算(step budget),以及**无步骤限制(No step limit)选项——后者会一直运行到任务结束(仍受 MAX_STEP_BUDGET 上限约束,因此循环模型也无法无限运行)。如果一次运行达到预算,它会报告部分结果,并提供继续(Continue)**选项——该选项使用已检索到的证据恢复同一对话,而不会重做已完成的工具调用。成本随工具/模型调用次数增加而增长,因此预算越高,花费越多。
可用工作流:
| 工作流 | 用途 | 是否需要目标函数地址 |
|---|---|---|
program_triage | 根据元数据、导入、字符串和函数总结程序可能的目的。 | 否 |
suspicious_behavior | 先呈现确定性指标,再给出有界且标注清晰的假设。 | 否 |
selected_function | 反编译某个函数,并结合其调用者/被调用者进行解释。 | 是 |
call_chain | 从起始函数出发,探索一个有界的原生/合成调用图邻域。 | 是 |
attack_surface_triage | 读取确定性评分的覆盖率/top-K,然后深入检查至多三个候选;评分是优先级,而非定论。 | 否 |
vulnerability_hypothesis | 选择一个有界候选,并呈现证据、反证和待解决的问题;绝不自动确认。 | 否 |
聚焦子调查
每个分析任务都有一个主(Main)聊天,外加可选的聚焦子线程。选择新建子调查(New sub-investigation),输入一行简报,即可在同一个二进制文件和同一组只读工具之上,以全新的对话上下文开展工作。线程历史保持彼此隔离,并且同一时间只有一个线程在进行流式输出。
当聚焦工作完成后,选择将结论返回父线程(Return conclusion to parent)。Rev·Deck 仅针对该子线程执行一次有界模型调用,验证其证据引用,并向父线程添加一张带有来源标记的结论卡片。完整的分支仍然可以重新打开,而父线程上下文只会收到简洁的结论——而非分支的完整记录。未通过验证引用的返回卡片会被明确标记为未验证(unverified)。
助手的回答会以内联方式引用证据,如 [function:0xADDR]、[string:0xADDR] 或 [import:name]。引用会与当轮对话中实际检索到的内容进行核对;不匹配的引用会被标记为“(unverified)”,应视为未经证实的说法,而非事实。
助手输出中的 Mermaid 图表(例如调用图草图)会在沙箱化框架中渲染,且无法访问外部网络。
架构
浏览器只与 Rev·Deck Web 应用通信。Rev·Deck 协调所配置的 LLM 与无头 Ghidra 服务,然后将得到的证据与智能体活动呈现在同一个工作区中。
从源码运行
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
npm ci && npm run vendor # one-time: vendors the pinned Mermaid runtime
cp .env.example .env # edit API_BASE / MODEL_NAME / API_KEY
set -a; source .env; set +a # plain Python does not load .env automatically
# Start the separate Ghidra service, then:
python webui/app.py
打开 http://127.0.0.1:5000。Docker Compose 会自动读取 .env;从源码运行则需要按上文所示的方式导出它。Flask 开发服务器适用于本地使用;Docker 镜像运行 Gunicorn。
安全性 / 仅限本地的边界
本项目是为单个受信任的分析师在自己的机器上使用而设计的——并非用于多用户或公共托管。默认情况下,应用与 Ghidra 服务只绑定到 127.0.0.1,调试模式关闭,上传的二进制文件绝不会被执行,LLM 提供商密钥也始终保留在服务端。
测试
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest
node --test "webui/static/js/tests/**/*.test.mjs"
npm ci && npm run vendor:verify # verifies the vendored Mermaid bundle's integrity
故障排除
- “Service offline”(服务离线)/
/readyz返回 503 — 无法在GHIDRA_API_BASE访问 Ghidra 服务,或API_BASE/MODEL_NAME未设置。 - 类型/全局变量/注解显示“requires v1” — 所连接的 Ghidra 服务未声明该能力;这在旧版服务上属于预期情况。
- 聊天立即报错 — 请检查
API_BASE/API_KEY/MODEL_NAME,并确认提供商在LLM_TIMEOUT内可达。 - 上传因文件过大而被拒绝 — 请调高
MAX_UPLOAD_BYTES。 - 上传内容看起来像纯文本 — Rev·Deck 会先询问再将其发送给 Ghidra;只有当这是有意为之(确实要上传原始二进制)时才继续。
- 大型分析超时 — 请调高 Ghidra 容器的
ANALYSIS_TIMEOUT(例如,针对 1 万+ 个函数的 C++/Android 二进制可设为5400),然后重新上传。LLM_TIMEOUT与此无关。 - 某条引用显示“(unverified)” — 模型引用了它实际上从未检索到的证据;请将该说法视为未经证实的假设。