
Rétro-ingénierie assistée par IA avec Ghidra
Rev·Deck est une station de travail d'analyse statique locale, mono-utilisateur. Elle associe une interface web orientée preuves à un copilote LLM s'appuyant sur un binaire analysé par un service Ghidra sans tête : parcourez directement les preuves déterministes (fonctions, chaînes, imports, références croisées, graphe d'appels borné), ou posez à l'assistant des questions bornées dont les affirmations factuelles doivent citer des preuves vérifiables.
Les binaires analysés ne sont jamais exécutés. Le navigateur ne communique qu'avec cette application Flask ; l'application relaie des requêtes validées et typées vers le service Ghidra.
https://github.com/user-attachments/assets/fba14dc5-7ad5-4137-9349-ed824da64fbe
cp .env.example .env # set API_BASE and MODEL_NAME; set API_KEY if required
docker compose up --build
Docker Compose lit .env automatiquement pour l'interpolation. Il échoue avant de démarrer si API_BASE ou MODEL_NAME est absent ; API_KEY=not-used reste valable pour les fournisseurs locaux/sans clé. La pile démarre les deux services. Ouvrez http://127.0.0.1:5000.
Pour exécuter uniquement le service 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
Pour un épinglage reproductible, utilisez le digest de la version testée au lieu 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
biniamfd/ghidra-headless-rest:latest.Copiez .env.example vers .env et remplissez ces champs ; consultez ce fichier pour la liste complète et les valeurs par défaut.
Rev·Deck communique avec tout point de terminaison Chat Completions compatible OpenAI via le SDK OpenAI, configuré entièrement par API_BASE / API_KEY / MODEL_NAME. Il n'y a ni en-tête, ni paramètre, ni logique de modèle spécifique à un fournisseur : un serveur local Ollama (API_BASE=http://127.0.0.1:11434/v1), un point de terminaison auto-hébergé vLLM/llama.cpp/LM Studio, OpenAI lui-même, ou une passerelle comme OpenRouter fonctionnent tous de la même manière.
Exemples de réglages fournisseur dans .env (utilisez des espaces réservés, ne commettez jamais de vraies clés) :
# 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
Par défaut (LLM_STREAM=auto), l'assistant demande une réponse en streaming et relaie les jetons vers le navigateur au fur et à mesure. Le streaming offre aussi une meilleure garantie d'annulation : lorsque vous arrêtez une réponse (ou fermez l'onglet), Rev·Deck ferme rapidement le flux du fournisseur sous-jacent et n'effectue plus aucun cycle d'outil ou de modèle ; la génération en amont est ainsi interrompue plutôt que laissée s'exécuter jusqu'au bout en arrière-plan.
Mises en garde :
auto, si le fournisseur rejette la requête en streaming avec une erreur de compatibilité (HTTP 400/404/405/422) avant tout contenu ou sortie d'appel d'outil, Rev·Deck retombe une seule fois sur un appel bloquant et s'en souvient pour le reste du processus. Les erreurs d'authentification (401/403), de limite de débit (429) et de serveur (5xx) ne sont pas traitées comme des problèmes de compatibilité et sont remontées comme erreurs plutôt que réessayées en silence. Définissez LLM_STREAM=false pour ignorer complètement le streaming, ou LLM_STREAM=true pour l'exiger (sans repli).Ouvrez l'application et téléversez un binaire pour démarrer une tâche d'analyse. Tout contenu manifestement en texte brut demande confirmation avant d'être envoyé à Ghidra ; n'utilisez la dérogation explicite binaire brut que lorsque le contenu est intentionnellement du firmware/des données plutôt qu'un format exécutable. Une fois l'analyse terminée, basculez entre deux onglets de l'espace de travail :
Les deux modes acceptent un budget d'étapes par tâche, ainsi qu'une option Sans limite d'étapes qui s'exécute jusqu'à la fin de la tâche (toujours plafonnée par MAX_STEP_BUDGET pour qu'un modèle en boucle ne puisse pas s'emballer). Si une exécution atteint son budget, elle rapporte des résultats partiels et propose Continuer — ce qui reprend la même conversation en utilisant les preuves déjà récupérées, sans refaire les appels d'outils terminés. Le coût augmente avec le nombre d'appels d'outils/modèle, donc des budgets plus élevés coûtent plus cher.
Workflows disponibles :
Chaque tâche d'analyse dispose d'un chat Principal plus de sous-fils de discussion ciblés facultatifs. Choisissez Nouvelle sous-investigation, saisissez un brief d'une ligne, et travaillez avec un contexte de conversation neuf sur le même binaire et les mêmes outils en lecture seule. Les historiques des fils restent isolés, et un seul fil diffuse en streaming à la fois.
Lorsque le travail ciblé est prêt, choisissez Renvoyer la conclusion au parent. Rev·Deck effectue un seul appel de modèle borné sur ce sous-fil uniquement, valide ses citations de preuves et ajoute une carte de conclusion marquée par provenance au parent. La branche complète reste rouvrable, tandis que le contexte parent ne reçoit que la conclusion compacte—pas la transcription de la branche. Une carte renvoyée sans aucune citation validée est explicitement marquée non vérifiée.
Les réponses de l'assistant citent les preuves en ligne comme [function:0xADDR], [string:0xADDR], ou [import:name]. Les citations sont vérifiées par rapport à ce qui a réellement été récupéré pendant le tour ; une citation qui ne correspond pas est signalée « (non vérifiée) » et doit être traitée comme une affirmation non confirmée, pas comme un fait.
Les diagrammes Mermaid dans la sortie de l'assistant (par ex. esquisses de graphe d'appels) s'affichent dans un cadre sandbox sans accès réseau externe.
Le navigateur ne communique qu'avec l'application web Rev·Deck. Rev·Deck coordonne le LLM configuré et le service Ghidra sans tête, puis présente les preuves résultantes et l'activité de l'agent dans un seul espace de travail.
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
Ouvrez http://127.0.0.1:5000. Docker Compose lit .env automatiquement ; l'exécution depuis les sources nécessite de l'exporter comme indiqué ci-dessus. Le serveur de développement Flask convient pour un usage local ; l'image Docker exécute Gunicorn.
Cet outil est conçu pour un analyste de confiance sur sa propre machine — pas pour un hébergement multi-utilisateur ou public. Par défaut, l'application et le service Ghidra n'écoutent que sur 127.0.0.1, le mode débogage est désactivé, les binaires téléversés ne sont jamais exécutés, et la clé du fournisseur LLM reste côté serveur.
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
/readyz renvoie 503 — le service Ghidra est injoignable sur GHIDRA_API_BASE, ou API_BASE/MODEL_NAME n'est pas défini.API_BASE/API_KEY/MODEL_NAME et que le fournisseur est joignable dans les limites de LLM_TIMEOUT.MAX_UPLOAD_BYTES.ANALYSIS_TIMEOUT du conteneur Ghidra (par exemple 5400 pour les binaires C++/Android de plus de 10 000 fonctions) et re-téléversez. n'est pas lié.| Variable | Défaut | Signification |
|---|
API_BASE | requis | URL de base compatible OpenAI (http/https). Compose échoue rapidement si absent. |
API_KEY | not-used | Clé du fournisseur. Jamais journalisée ni envoyée au navigateur ; not-used est valable pour les fournisseurs locaux sans clé. |
MODEL_NAME | requis | Identifiant de modèle attendu par le point de terminaison configuré. Compose échoue rapidement si absent. |
LLM_STREAM | auto | Transport de streaming : auto (streaming, repli en mode bloquant une fois en cas d'erreur de compatibilité avant émission), true (toujours streamer), false (toujours bloquant). |
GHIDRA_API_BASE | http://127.0.0.1:9090 | URL de base du service Ghidra. |
GHIDRA_IMAGE | biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3... | Version testée épinglée par digest immuable. :latest résout également ce digest ; à surcharger pour épingler une autre version. |
HOST / PORT | 127.0.0.1 / 5000 | Adresse d'écoute du serveur de développement. |
MAX_UPLOAD_BYTES | 104857600 | Limite de taille de téléversement. |
CHATS_DIR | webui/chats | Répertoire de l'historique des conversations. |
| Workflow | Objectif | Nécessite une adresse de fonction cible |
|---|
program_triage | Résumer l'objectif probable du programme à partir des métadonnées, imports, chaînes et fonctions. | Non |
suspicious_behavior | D'abord exposer les indicateurs déterministes, puis des hypothèses bornées et clairement étiquetées. | Non |
selected_function | Décompiler une fonction et l'expliquer avec ses appelants/appelés. | Oui |
call_chain | Explorer un voisinage borné du graphe d'appels natif/synthétisé à partir d'une fonction de départ. | Oui |
attack_surface_triage | Lire la couverture/le top-K du score déterministe, puis inspecter en profondeur au plus trois candidats ; les scores sont des priorités, pas des verdicts. | Non |
vulnerability_hypothesis | Sélectionner un candidat borné et présenter preuves, contre-preuves et questions ouvertes ; ne confirme jamais automatiquement. | Non |
LLM_TIMEOUT