
ai-reverse-engineering — Updated!
Engenharia Reversa Assistida por IA com Ghidra
Rev·Deck — Engenharia Reversa Assistida por IA com Ghidra
O Rev·Deck é uma estação de trabalho de análise estática, local e para um único usuário. Ele combina uma interface web focada em evidências com um copiloto de LLM sobre um binário analisado por um serviço Ghidra headless: navegue diretamente por evidências determinísticas (funções, strings, imports, referências cruzadas, um grafo de chamadas limitado) ou faça ao assistente perguntas limitadas cujas afirmações factuais devem citar evidências inspecionáveis.
Os binários analisados nunca são executados. O navegador fala apenas com este aplicativo Flask; o aplicativo faz proxy de solicitações validadas e tipadas para o serviço Ghidra.
Demo
https://github.com/user-attachments/assets/fba14dc5-7ad5-4137-9349-ed824da64fbe
Início Rápido (Docker)
cp .env.example .env # set API_BASE and MODEL_NAME; set API_KEY if required
docker compose up --build
O Docker Compose lê .env automaticamente para interpolação. Ele falha antes de iniciar se API_BASE ou MODEL_NAME estiver ausente; API_KEY=not-used permanece válido para provedores locais/sem chave. A pilha inicia ambos os serviços. Abra http://127.0.0.1:5000.
Para executar apenas o serviço 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
Para fixar uma versão de forma reproduzível, use o digest da versão testada em vez de 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
Pré-requisitos
- Docker e Docker Compose (para o caminho do Início Rápido), ou Python 3.10+ e Node.js 18+ (para executar a partir do código-fonte).
- Um endpoint de LLM compatível com OpenAI (local ou hospedado) e o nome do modelo.
- A imagem pública do Ghidra:
biniamfd/ghidra-headless-rest:latest.
Variáveis de ambiente essenciais
Copie .env.example para .env e preencha estes campos; veja esse arquivo para a lista completa e os padrões.
| Variável | Padrão | Significado |
|---|---|---|
API_BASE | obrigatório | URL base compatível com OpenAI (http/https). O Compose falha cedo quando ausente. |
API_KEY | not-used | Chave do provedor. Nunca é registrada em log nem enviada ao navegador; not-used é válida para provedores locais sem chave. |
MODEL_NAME | obrigatório | ID do modelo esperado pelo endpoint configurado. O Compose falha cedo quando ausente. |
LLM_STREAM | auto | Transporte de streaming: auto (transmitir em streaming, com fallback único para bloqueante em erro de compatibilidade antes da saída), true (sempre transmitir), false (sempre bloqueante). |
GHIDRA_API_BASE | http://127.0.0.1:9090 | URL base do serviço Ghidra. |
GHIDRA_IMAGE | biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3... | Versão testada fixada por digest imutável. :latest também resolve para este digest; substitua para fixar uma versão diferente. |
HOST / PORT | 127.0.0.1 / 5000 | Bind do servidor de desenvolvimento. |
MAX_UPLOAD_BYTES | 104857600 | Limite de tamanho do upload. |
CHATS_DIR | webui/chats | Diretório do histórico de conversas. |
Provedor de LLM
O Rev·Deck conversa com qualquer endpoint de Chat Completions compatível com OpenAI por meio do SDK da OpenAI, configurado inteiramente por API_BASE / API_KEY / MODEL_NAME. Não há cabeçalho, parâmetro ou lógica de modelo específica de provedor: um servidor local Ollama (API_BASE=http://127.0.0.1:11434/v1), um endpoint vLLM/llama.cpp/LM Studio auto-hospedado, a própria OpenAI ou um gateway como o OpenRouter funcionam todos da mesma forma.
Exemplo de configurações de provedor no .env (use placeholders; nunca envie chaves reais):
# 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
Por padrão (LLM_STREAM=auto), o assistente solicita uma resposta em streaming e retransmite os tokens ao navegador conforme eles chegam. O streaming também oferece uma garantia de cancelamento mais forte: quando você interrompe uma resposta (ou fecha a aba), o Rev·Deck fecha prontamente o fluxo subjacente do provedor e não realiza mais nenhuma rodada de ferramenta ou modelo; assim, a geração upstream é encerrada em vez de continuar rodando até a conclusão em segundo plano.
Ressalvas:
- Cobrança. Cancelar fecha o fluxo do nosso lado imediatamente, mas alguns provedores hospedados ainda cobram pelos tokens que já haviam gerado (ou pela conclusão inteira), independentemente de uma desconexão antecipada do cliente. A garantia diz respeito a não fazer mais trabalho, não à política de cobrança de um provedor.
- Compatibilidade. Nem todo endpoint compatível com OpenAI aceita streaming com ferramentas. Em
auto, se o provedor rejeitar a solicitação em streaming com um erro de compatibilidade (HTTP 400/404/405/422) antes de qualquer saída de conteúdo ou de chamada de ferramenta, o Rev·Deck recorre a uma única chamada bloqueante uma vez e lembra disso pelo restante do processo. Erros de autenticação (401/403), de limite de taxa (429) e de servidor (5xx) não são tratados como problemas de compatibilidade e são exibidos como erros em vez de serem repetidos silenciosamente. DefinaLLM_STREAM=falsepara pular o streaming por completo, ouLLM_STREAM=truepara exigi-lo (sem fallback).
Como usar
Abra o aplicativo e envie um binário para iniciar um trabalho de análise. Conteúdo que seja claramente texto simples pede confirmação antes de ser enviado ao Ghidra; use a substituição explícita de binário bruto somente quando o conteúdo for intencionalmente firmware/dados, e não um formato executável. Quando a análise for concluída, alterne entre duas abas do espaço de trabalho:
- Analysis — visualizações de evidências determinísticas: resumo, funções (filtrar/paginar), imports, strings, uma visualização de consulta, um inspetor de funções (pseudocódigo, referências cruzadas, grafo de chamadas limitado, hexdump) e — quando o serviço Ghidra conectado oferecer suporte — types, globals, anotações sidecar, exportação de arquivo e uma classificação determinística de Attack Surface com sinais positivos/mitigantes explicáveis e cobertura de evidências.
- Chat — o assistente, em um de dois modos:
- Copilot (padrão): um passo/chamada de ferramenta limitado por mensagem, para perguntas ad-hoc.
- Autonomous: inicie um workflow nomeado e com orçamento que executa vários passos limitados por conta própria e mostra uma linha do tempo de atividade ao vivo enquanto trabalha.
Ambos os modos aceitam um orçamento de passos por tarefa e uma opção No step limit, que executa até a tarefa terminar (ainda limitada por MAX_STEP_BUDGET para que um modelo em loop não possa fugir do controle). Se uma execução atingir seu orçamento, ela relata resultados parciais e oferece Continue — que retoma a mesma conversa usando as evidências já recuperadas, sem refazer chamadas de ferramenta concluídas. O custo cresce com o número de chamadas de ferramenta/modelo; portanto, orçamentos maiores custam mais.
Workflows disponíveis:
| Workflow | Propósito | Requer um endereço de função alvo |
|---|---|---|
program_triage | Resumir o provável propósito do programa a partir de metadados, imports, strings e funções. | Não |
suspicious_behavior | Apresentar indicadores determinísticos primeiro e, em seguida, hipóteses limitadas e claramente rotuladas. | Não |
selected_function | Descompilar uma função e explicá-la com seus chamadores/callees. | Sim |
call_chain | Explorar uma vizinhança limitada do grafo de chamadas nativo/sintetizado a partir de uma função inicial. | Sim |
attack_surface_triage | Ler a cobertura determinística da pontuação/top-K e, em seguida, inspecionar profundamente no máximo três candidatos; as pontuações são prioridades, não veredictos. | Não |
vulnerability_hypothesis | Selecionar um candidato limitado e apresentar evidências, contra-evidências e questões em aberto; nunca confirma automaticamente. | Não |
Sub-investigações focadas
Cada trabalho de análise tem um chat Main além de sub-threads focadas opcionais. Escolha New sub-investigation, insira um resumo de uma linha e trabalhe com um novo contexto de conversa sobre o mesmo binário e as mesmas ferramentas somente leitura. Os históricos das threads permanecem isolados, e apenas uma thread transmite por vez.
Quando o trabalho focado estiver pronto, escolha Return conclusion to parent. O Rev·Deck faz uma única chamada de modelo limitada somente sobre essa sub-thread, valida suas citações de evidências e adiciona um cartão de conclusão marcado com proveniência ao parent. O ramo completo permanece reaberto, enquanto o contexto do parent recebe apenas a conclusão compacta — não a transcrição do ramo. Um cartão retornado sem citações validadas é explicitamente marcado como não verificado.
As respostas do assistente citam evidências inline como [function:0xADDR], [string:0xADDR] ou [import:name]. As citações são verificadas contra o que foi efetivamente recuperado durante o turno; uma citação que não corresponde é sinalizada como "(unverified)" e deve ser tratada como uma afirmação não confirmada, não como fato.
Diagramas Mermaid na saída do assistente (por exemplo, esboços de grafo de chamadas) são renderizados em um quadro em sandbox, sem acesso à rede externa.
Arquitetura
O navegador fala apenas com o aplicativo web Rev·Deck. O Rev·Deck coordena o LLM configurado e o serviço Ghidra headless e, em seguida, apresenta as evidências resultantes e a atividade do agente em um único espaço de trabalho.
Executando a partir do código-fonte
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
Abra http://127.0.0.1:5000. O Docker Compose lê .env automaticamente; a execução a partir do código-fonte exige exportá-lo conforme mostrado acima. O servidor de desenvolvimento Flask é suficiente para uso local; a imagem Docker executa o Gunicorn.
Segurança / limite somente local
Isto foi projetado para um único analista confiável em sua própria máquina — não para hospedagem multiusuário ou pública. Por padrão, o aplicativo e o serviço Ghidra fazem bind somente em 127.0.0.1, o modo de depuração está desativado, binários enviados nunca são executados e a chave do provedor de LLM permanece no lado do servidor.
Testes
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
Solução de problemas
- "Service offline" /
/readyzretorna 503 — o serviço Ghidra está inacessível emGHIDRA_API_BASE, ouAPI_BASE/MODEL_NAMEnão está definido. - Types/Globals/Annotations mostram "requires v1" — o serviço Ghidra conectado não anuncia essa capacidade; esperado em serviços mais antigos.
- O chat falha imediatamente — verifique
API_BASE/API_KEY/MODEL_NAMEe se o provedor está acessível dentro deLLM_TIMEOUT. - Upload rejeitado por ser grande demais — aumente
MAX_UPLOAD_BYTES. - O upload parece texto simples — o Rev·Deck pergunta antes de enviá-lo ao Ghidra; continue como binário bruto somente quando for intencional.
- Análises grandes expiram (timeout) — aumente o
ANALYSIS_TIMEOUTdo contêiner do Ghidra (por exemplo,5400para binários C++/Android com mais de 10 mil funções) e reenvie o upload.LLM_TIMEOUTnão está relacionado. - Uma citação mostra "(unverified)" — o modelo citou evidências que nunca recuperou de fato; trate essa afirmação como uma hipótese não confirmada.