REx@Skill - Compétence d'exécution agentique de rétro-ingénierie pour la découverte de vulnérabilités binaires
![]()
Ghidra · radare2 · rizin · qemu-user · angr · z3 · AFL++ · valgrind · capa · Unicorn · Triton · Frida · semgrep · clang · Python · Nix
tous figés, tous reproductibles
1 · Claude Code — passez cette étape si vous l'avez déjà.```bash curl -fsSL https://claude.ai/install.sh | bash
**2 · REx@Skill** — 1 compétence, 12 références, **7 sous-agents**, **33 scripts**.```bash
curl -fsSL https://raw.githubusercontent.com/tihanyin/REx-skill/main/install.sh | sh
3 · analyser un binaire.```bash claude
/re-analyze path/to/binary # one target /re-analyze path/to/directory/ # a whole corpus, evidence gathered in parallel
<div align="center">
<sub><code>install.sh</code> n'écrit que dans <code>~/.claude/</code> — la skill et ses 33 scripts de pipeline, les 7 agents, <code>/re-analyze</code>.<br>Il n'installe jamais Claude Code à votre place ; il sauvegarde tout ce qu'il écraserait, et <code>--uninstall</code> le supprime proprement.</sub>
</div>
<br>
<details>
<summary><b>Vous préférez cloner ?</b> · <i>ou l'installer ailleurs, ou le supprimer</i></summary>```bash
git clone https://github.com/tihanyin/REx-skill && cd REx-skill
./install.sh # into ~/.claude
./install.sh --prefix ~/.config/claude # somewhere else
./install.sh --uninstall # take it back out
![]()
Ghidra · radare2 · rizin · qemu-user · angr · z3 · AFL++ · valgrind · capa
![]()
Unicorn · Triton · Frida · semgrep · clang · Python · Nix · Linux · Claude
REx@Skill utilise des outils de pointe rassemblés par des experts en rétro-ingénierie. Chacun est présent dans l'ensemble parce qu'il mérite sa place sur des benchmarks reconnus et de réels défis d'analyse binaire — non parce qu'il était pratique à installer. Rien ici n'est réimplémenté ; le travail propre de la compétence consiste à savoir quel outil répond à la question qui se présente, et ce que vaut sa réponse.
| Outil | Ce qu'il fait |
|---|---|
| Ghidra | retransforme un binaire compilé en C lisible |
| radare2 / rizin | un second décompilateur, utilisé pour recouper le premier |
| qemu-user | exécute des binaires ARM, MIPS, PowerPC et RISC-V sur une machine x86 normale |
| z3 | un solveur mathématique — prouve si un indice peut sortir de son tampon, ou si un diviseur peut être nul |
| angr | détermine quelle entrée permettrait d'atteindre une ligne de code donnée |
| AFL++ | envoie des millions d'entrées générées au programme pour le faire planter |
| valgrind | détecte les bugs mémoire qui autrement ne causent aucune erreur visible |
| libdislocator | fait planter immédiatement toute lecture d'un octet au-delà d'un tampon |
| capa | liste ce que le binaire peut faire : chiffrer, ouvrir des sockets, injecter dans des processus |
| floss | trouve du texte caché que strings seul manque |
| Unicorn | exécute une seule fonction sur les entrées de votre choix, sans exécuter le programme |
| Triton | suit où circulent les données contrôlées par l'attaquant pendant une exécution |
| Frida | observe et modifie un programme pendant son exécution |
| semgrep / cppcheck | analysent le C décompilé à la recherche de motifs problématiques connus |
| pwntools | la bibliothèque d'assistance pour les offsets, l'analyse ELF et le travail d'exploitation |
Voici les versions exactes avec lesquelles il a été construit et mesuré :
Ghidra 12.1.2 · radare2 6.2.0 · rizin 0.9.1 · qemu-user 11.1.0 + 9 cross-sysroots · angr 9.2.154 · z3 4.16.0 · AFL++ 5.00c · valgrind 3.27.1 · capa 9.4.0 · Unicorn 2.1.4 · Triton 3.7.0 · Frida 17.17.0 · clang 21.1.8 · semgrep 1.172.0 · floss 3.1.1 · yara 4.5.7 · binwalk 3.1.0 · pwntools 4.15.0 · cppcheck 2.21.1
Chacun est optionnel. scripts/capabilities.sh indique ce dont dispose cette machine,
chaque script nomme l'outil qu'il ne trouve pas, et un outil manquant restreint l'analyse
dans limitations plutôt que d'échouer silencieusement.
Trois commandes et tous les outils ci-dessus sont dans votre PATH, exactement à ces versions —
rien à chercher, rien laissé à moitié configuré.```bash
git clone https://github.com/tihanyin/REx-skill 2>/dev/null || git -C REx-skill pull
cd REx-skill
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm
nix develop ./devshell
scripts/capabilities.sh
| | |
|---|---|
| **0** | clone le dépôt, ou le met à jour si vous en avez déjà un — l'installateur de compétence en une ligne ci-dessus ne **laisse pas** de clone derrière lui, il travaille depuis une copie temporaire et la supprime, donc la chaîne d'outils et les scripts ont besoin du leur |
| **1** | installe Nix, le gestionnaire de paquets qui effectue l'épinglage — puis **ouvrez un nouveau terminal**. *Vous avez déjà Nix ? Passez cette étape.* Relancer l'installateur sur une installation existante échoue avec `Found existing plan in /nix/receipt.json`, ce qui signifie qu'il refuse de toucher à ce que vous avez déjà, et non une erreur à corriger |
| **2** | entre dans le shell, depuis la racine du dépôt pour que `scripts/` reste à portée de main. La première fois télécharge beaucoup ; toutes les fois suivantes prennent quelques secondes |
| **3** | le confirme : `ghidra pyghidra r2 rizin`, `qemu-user architectures: 7`, `angr`, `z3`, `afl` — au lieu des lignes `MISS` qu'une machine nue affiche |
`exit` restaure votre `PATH` exactement tel qu'il était. *Testé sur Ubuntu 22.04.5 LTS
(x86-64), Determinate Nix 3.22.4.*
<details>
<summary><b>Pourquoi épingler la chaîne d'outils ?</b></summary>
- **Des exécutions comparables.** La sortie du décompilateur *est* l'entrée de l'analyste. Deux personnes sur deux versions de Ghidra ne font pas la même expérience, et aucune ne peut vérifier le résultat de l'autre.
- **Rien d'installé sur votre machine.** Nix conserve chaque paquet sous un hash de ce qui l'a construit, donc entrer dans le shell modifie `PATH` et rien d'autre. Quittez le shell et votre système est exactement comme avant.
- **Cela fonctionne encore dans cinq ans.** Trois révisions épinglées reconstruisent toute la chaîne d'outils, ce qui rend un chiffre publié revérifiable plus tard.
Trois révisions reconstruisent toute la chaîne d'outils, sur n'importe quelle machine, à n'importe quel moment dans le futur :```
nixpkgs ffb3c9b700e759be2ef13237c9d8f953b32a1e46
nixpkgs-angr ac62194c3917d5f474c1a844b6fd6da2db95077d
capa-rules v9.4.0
devshell/flake.lock fait autorité ; devshell/DEVSHELL.md
liste chaque outil avec sa version et son utilité.
REx@Skill est une méthode pour déterminer si un binaire présente un défaut — et le prouver. Sept sous-agents l'exécutent, partageant un même répertoire de preuves.
Un agent transforme le binaire en preuves : C décompilé, désassemblage, chaînes, une carte de quel code atteint quel autre, et ce qui se passe lorsqu'on l'exécute réellement. Cinq agents lisent ensuite ces preuves en même temps, chacun traquant un type de défaut différent, et aucun d'eux ne peut voir ce que les autres ont trouvé — cinq lecteurs partageant un même décompilateur commettent les mêmes erreurs, donc les séparer permet d'obtenir cinq lectures indépendantes au lieu d'un avis répété cinq fois. Un septième lit le code d'abord, puis leurs conclusions, et décide lesquelles tiennent.
Rien n'est signalé comme bug à moins que quatre éléments soient nommés et localisés dans le binaire : où les données contrôlées par l'attaquant entrent (source), l'opération qu'elles peuvent casser (sink), la vérification qui aurait dû l'arrêter (garde défaillante), et qui est affecté (principal affecté). En manquer un et cela sort comme une piste non prouvée, pas comme une conclusion.
Chaque exécution livre les trois mêmes éléments : les conclusions, ce qui a été écarté et pourquoi, et ce que l'hôte n'a pas pu exécuter.
L'ensemble de compétences a été testé sur dix architectures — x86-64, i686, ARM, AArch64, MIPS et MIPS64 dans les deux endianness, PowerPC 32 bits, RISC-V et Apple arm64 — sur ELF, PE et Mach-O, firmware et blobs bruts. Rien ne le limite à cela : toute architecture que votre décompilateur peut traiter est dans le périmètre.
| Agent | Exécution | En une ligne | |
|---|---|---|---|
| 01 | re-recon | en premier, seul | Extrait les preuves. Ne traque pas les bugs — une conclusion assurée ici est le mode de défaillance. |
| 02 | re-bughunt | toujours | Construit le meilleur argument honnête qu'un défaut existe. Énumère chaque sink. |
| 03 | re-safety | toujours | Tente de prouver sa validité, et signale chaque obligation qu'il ne peut pas remplir. |
| 04 | re-arithmetic | s'il indexe ou dimensionne | Taille, index, largeur et signe à travers les frontières d'appel — résolu par un solveur, pas dans la tête de quelqu'un. |
| 05 | re-lifecycle | s'il alloue | Allocation, libération, propriété, init et chemins d'erreur. Le chemin d'erreur est celui que personne n'a testé. |
| 06 | re-logic | s'il authentifie | Autorisation, machines à états, crypto, valider-ici-utiliser-là. Aucune signature à rechercher. |
| 07 | re-reconcile | en dernier, seul | Lit le code avant de lire les conclusions de quiconque, puis arbitre et rapporte. |
Un répertoire par binaire, nommé <filename>-<first 8 hex of its SHA-256> :```
results/
├── index.json every sha256 analysed -> its directory
└── httpd-4f2a9c1e/ <- "httpd", sha256 4f2a9c1e...
├── decomp/ decompiled C ├── reach/ source -> sink paths
├── disasm/ disassembly, real VAs ├── bounds/ arithmetic to discharge
├── meta/ function map + base ├── sanitize/ hostile-allocator runs
├── strings/ inventory, by family ├── fuzz/ coverage-guided search
├── dynamic/ crafted-input battery ├── quarantine/ text aimed at YOU
└── notes/ the threat model └── pipeline.json what ran, what did not
**Pourquoi le hash figure dans le nom.** Deux compilations d'un programme partagent un nom de fichier mais pas un
SHA-256, de sorte que les preuves et le binaire ne peuvent pas dériver silencieusement : recompilez la cible et
vous obtenez un nouveau répertoire plutôt qu'un répertoire pollué.
`pipeline_status.py` audite cette arborescence et signale les étapes qui ne se sont jamais exécutées — car une
étape qui ne s'est jamais exécutée ne laisse aucune erreur derrière elle, seulement un répertoire absent, ce qui se lit
exactement comme « exécutée, rien trouvé ».
</details>
---
## 4. Inventaire des outils — quel script appelle quoi
#### Décompiler et lire
| Outil | Version | Utilisé par | Pour |
|---|---|---|---|
| **Ghidra** | 12.1.2 | `ghidra_export.py` `batch_decompile.sh` `decompile_addr.py` | le décompilateur — l'outil porteur |
| **pyghidra** | 3.1.0 | les trois mêmes | le piloter en mode headless |
| radare2 | 6.2.0 | `run_tools.sh` `strings_report.py` `brief.py` | du JSON en sortie de chaque commande |
| rizin | 0.9.1 | `capabilities.sh` `preflight.sh` | un **second** décompilateur — contre-vérification |
| binutils | 2.46 | `triage.py` `inventory.py` `run_tools.sh` | readelf, objdump, nm, strings, size |
| file | 5.48 | chaque point d'entrée | première commande, à chaque fois |
#### Triage — qu'est-ce que c'est, que peut-il faire
| Outil | Version | Utilisé par | Pour |
|---|---|---|---|
| **capa** | 9.4.0 | `run_tools.sh` `brief.py` | capacités issues de règles, avec adresses |
| **floss** | 3.1.1 | `strings_report.py` | les chaînes que `strings` ne peut pas voir |
| yara | 4.5.7 | `run_tools.sh` `analyze.sh` | packers, constantes cryptographiques, versions de bibliothèques |
| detect-it-easy | 3.21 | `run_tools.sh` | identification du packer et du compilateur |
| checksec | pwntools | `triage.py` `brief.py` | NX / RELRO / canary / PIE |
#### Exécuter — le plus grand levier unique
| Outil | Version | Utilisé par | Pour |
|---|---|---|---|
| **qemu-user** | 11.1.0 | `dynamic_probe.py` `quick_dynamic.sh` | exécuter des binaires d'architecture étrangère |
| **9 cross-sysroots** | glibc | `setup_sysroots.sh` | sans eux, qemu ne peut même pas charger un binaire dynamique étranger |
| gdb / ltrace / strace | 17.2 | `capabilities.sh` les signale | traces ; `ltrace` est l'outil le plus sous-utilisé ici |
> Sur une comparaison mesurée, le même modèle a obtenu environ **3× le rappel** avec
> l'exécution disponible que sans. Cette ligne est la raison pour laquelle les sysroots sont livrés avec le flake.
#### Rendre les bugs silencieux bruyants
| Outil | Version | Utilisé par | Pour |
|---|---|---|---|
| **valgrind** | 3.27.1 | `sanitize_run.sh` | ce qui se rapproche le plus d'ASan pour un binaire que vous ne pouvez pas recompiler |
| **AFL++** | 5.00c | `fuzz_target.sh` `quick_dynamic.sh` | fuzzing guidé par la couverture, mode QEMU |
| libdislocator | avec AFL++ | `sanitize_run.sh` `quick_dynamic.sh` | une page par allocation — transforme une lecture OOB silencieuse en faute |
| clang | 21.1.8 | `triage.py` | `-fsanitize=...` sur du code relevé |
#### Résoudre et émuler
| Outil | Version | Utilisé par | Pour |
|---|---|---|---|
| **z3** | 4.16.0 | `check_bound.py` | valide une affirmation de borne — 9 modes plus une échappatoire générique |
| **angr** | 9.2.154 | `symfn.py` | harnais symbolique par fonction : détournement, écriture OOB, division par zéro |
| **unicorn** | 2.1.4 | `emulate.py` | exécuter UNE fonction isolément sur les entrées de votre choix |
| triton | 3.7.0 | disponible | exécution concolique et teinture sur une trace concrète |
| pwntools | 4.15.0 | `brief.py` `run_tools.sh` | offsets `cyclic()`, analyse ELF/GOT |
#### Analyse statique sur du C décompilé
| Outil | Version | Utilisé par | Pour |
|---|---|---|---|
| cppcheck | 2.21.1 | `run_tools.sh` `analyze.sh` | tolère du code qui ne compile pas — la sortie d'un décompilateur ne compile pas |
| semgrep | 1.172.0 | `run_tools.sh` `analyze.sh` | règles par motifs, aucune compilation nécessaire |
| flawfinder | 2.0.20 | `capabilities.sh` | lexical ; un grep avec des opinions |
<details>
<summary><b>Également dans le shell</b> — firmware, formats, exploitabilité</summary>
`binwalk` 3.1.0 · `unsquashfs` · `sasquatch` · `jefferson` · `ubi_reader` ·
`kaitai-struct-compiler` 0.11 · `tshark` 4.6.8 · `hexyl` · `pev` 0.81 ·
`osslsigncode` · `diffoscope` 328 · `patchelf` 0.15.2 · `ROPgadget` 7.7 ·
`one_gadget` 1.9.0 · `honggfuzz` · `radamsa` 0.7 · `bitwuzla` 0.9.1 · `rr` 5.9.0 ·
`bpftrace` 0.26.0 · `upx` 5.2.0
Inventaire complet avec chaque version : [`devshell/DEVSHELL.md`](https://github.com/tihanyin/rex-skill/blob/main/devshell/DEVSHELL.md)
</details>
---
## 5. L'utiliser avec autre chose que Claude
`general-skill/SKILL-RE.md` est **un seul fichier autonome** — 3 750 lignes de Markdown
brut avec un frontmatter `name`/`description`. Tout ce que contient le bundle Claude,
en un seul morceau : la même norme de preuve, le même pipeline, les mêmes règles sur
le fuzzing d'une architecture étrangère. Clonez le dépôt pour que `scripts/` se trouve à côté, puis :
| Exécuteur | Comment |
|---|---|
| **Codex** | pointez `AGENTS.md` dessus, ou collez-le comme prompt système |
| **opencode** | `{"instructions": ["SKILL-RE.md"]}` dans `opencode.json` |
| **Cursor / Windsurf** | déposez-le comme règle de projet |
| **Une simple boucle API** | ce n'est que du Markdown — préfixez avec |
| **Un humain** | cela se lit comme un manuel ; c'était le but |
> **Pourquoi deux formes ?** Le bundle Claude est un cœur de 16 Ko plus douze fichiers de référence
> chargés à la demande — un contexte réduit jusqu'à ce qu'une question précise nécessite un chapitre
> précis. Seul Claude Code suit ces pointeurs, donc tous les autres exécuteurs reçoivent le
> fichier unique à la place.
---
---
## 6. Ce qu'il contient```
.
├── claude-skill/ the Claude Code form
│ ├── skills/reverse-engineering/
│ │ ├── SKILL.md 16 KB core, loaded on every trigger
│ │ └── references/ 12 files, pulled in on demand
│ ├── agents/ 7 subagents
│ └── commands/ /re-analyze — orchestrates all three phases
│
├── general-skill/ the portable form
│ ├── SKILL-RE.md the whole methodology, one file, 32 sections
│ └── AGENTS.md points any agent at it
│
├── scripts/ 32 tools — the pipeline and its parts
├── devshell/ flake.nix + flake.lock + DEVSHELL.md
├── images/ logo and figures
└── install.sh one command into ~/.claude
La compétence s'exécute partout où Claude Code s'exécute — Linux, macOS, WSL. C'est du markdown :
le cœur, 12 références, 7 sous-agents et /re-analyze. Rien dedans n'est
spécifique à une plateforme, et les scripts sont du Python et du bash portables. Ce qui varie, ce sont
les outils en dessous.
Le DEVSHELL est construit et testé sur Linux — x86-64 (Ubuntu 22.04.5 LTS) et aarch64. C'est sur macOS que ça s'amincit, car onze des outils n'existent pas du tout sur Darwin :
qemu-user traduit les appels système Linux, donc c'est du Linux par définition, et les
neuf sysroots multi-architectures vont avec.
Ce qu'il reste sur un Mac. Toutes les étapes statiques : décompilation Ghidra, radare2 et rizin, angr et z3, les analyseurs statiques, le triage, les chaînes et l'atteignabilité. Ce que vous perdez, c'est l'exécution — la sonde dynamique, les exécutions de sanitizers, le fuzzing, et tout binaire d'architecture étrangère. C'est une vraie perte : un crash est la preuve la plus forte dont dispose cette méthodologie, et rien de statique ne le remplace.
Rien ne prétend le contraire. scripts/capabilities.sh rapporte ce que l'hôte
peut réellement faire, scripts/pipeline_status.py marque l'étape comme absente, et le
coût atterrit dans les limitations du rapport. Une étape qui n'a pas tourné n'est jamais
rapportée comme une étape qui a tourné et n'a rien trouvé — voir §9.1 de la compétence.
En bref : analysez sur Linux. Lisez, planifiez et rédigez le rapport n'importe où.
Norbert Tihanyi · x.com/@TihanyiNorbert
une trouvaille = source · sink · garde brisée · principal affecté.
tout ce qui est moins que cela est une hypothèse.
capabilities.sh | ce que cet hôte peut réellement faire — à vérifier avant de planifier |
analyze.sh | tout le pipeline dans l'ordre, étapes 0-5, puis passe la main |
batch_analyze.sh | le même pipeline sur un répertoire de cibles |
pipeline_status.py | auditer un arbre de preuves : quelles étapes ont tourné, et ce que coûte chaque absence |
overview.py | la forme d'un programme : comptages, arbre d'appels, sinks, sources |
brief.py | la sortie de chaque outil pour une cible, consolidée, les lacunes nommées |
fn.py | lire une fonction au lieu de toute la décompilation |
reach.py | chemins source → sink sur le graphe d'appels |
bounds_worklist.py | les affirmations arithmétiques à décharger, en trois niveaux |
check_bound.py | en décharger une avec z3 — 9 modes plus une échappatoire générique |
symfn.py | harnais symbolique pour une fonction |
emulate.py | exécuter une fonction isolément sur les entrées de votre choix |
quick_dynamic.sh | il suffit de l'exécuter : sans entrée, puis avec des entrées qui cassent la plupart des choses |
fuzz_target.sh | fuzzing ciblé sur le canal que le programme lit réellement |
sanitize_run.sh | allocateurs hostiles — faire planter un bug de tas silencieux |
sanitize.py | mettre en quarantaine le texte dirigé par le modèle avant que quoi que ce soit ne le lise |
| absents sur macOS | qemu-user · gdb · gef · ltrace · strace · valgrind · AFL++ · honggfuzz · frida · bpftrace · rr |
| aussi | le build épinglé de capa ne passe pas sa propre suite de tests sur Darwin |
| et | le fuzzing d'argv interpose __libc_start_main, qui est de la glibc — il n'existe pas d'équivalent macOS |