REx@Skill – Agentische Reverse-Engineering-Ausführungsfähigkeit zur Entdeckung von Schwachstellen in Binärdateien
![]()
Ghidra · radare2 · rizin · qemu-user · angr · z3 · AFL++ · valgrind · capa · Unicorn · Triton · Frida · semgrep · clang · Python · Nix
alle fixiert, alle reproduzierbar
1 · Claude Code — überspringen, falls bereits vorhanden.```bash curl -fsSL https://claude.ai/install.sh | bash
**2 · REx@Skill** — 1 Skill, 12 Referenzen, **7 Subagenten**, **33 Skripte**.```bash
curl -fsSL https://raw.githubusercontent.com/tihanyin/REx-skill/main/install.sh | sh
3 · Eine Binärdatei analysieren.```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> schreibt ausschließlich nach <code>~/.claude/</code> — den Skill und seine 33 Pipeline-Skripte, die 7 Agents, <code>/re-analyze</code>.<br>Es installiert Claude Code nie für dich; es sichert alles, was es überschreiben würde, und <code>--uninstall</code> entfernt es sauber.</sub>
</div>
<br>
<details>
<summary><b>Lieber klonen?</b> · <i>oder woanders installieren, oder entfernen</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 verwendet modernste Tools, die von Reverse-Engineering-Experten zusammengestellt wurden. Jedes einzelne ist in der Sammlung, weil es sich seinen Platz anhand bekannter Benchmarks und echter Binäranalyse-Herausforderungen verdient hat — nicht, weil es bequem zu installieren war. Nichts hier ist neu implementiert; die eigentliche Aufgabe des Skills ist zu wissen, welches Tool die vor ihm liegende Frage beantwortet und was seine Antwort wert ist.
| Tool | Was es tut |
|---|---|
| Ghidra | verwandelt ein kompiliertes Binärprogramm zurück in lesbares C |
| radare2 / rizin | ein zweiter Decompiler, der zur Gegenprüfung des ersten dient |
| qemu-user | führt ARM-, MIPS-, PowerPC- und RISC-V-Binärprogramme auf einer normalen x86-Maschine aus |
| z3 | ein mathematischer Solver — beweist, ob ein Index seinen Puffer verlassen oder ein Divisor null sein kann |
| angr | ermittelt, welche Eingabe eine bestimmte Codezeile erreichen würde |
| AFL++ | wirft Millionen generierter Eingaben auf das Programm, um es zum Absturz zu bringen |
| valgrind | fängt Speicherfehler ab, die sonst keinen sichtbaren Fehler verursachen |
| libdislocator | lässt das Lesen eines Bytes über einen Puffer hinaus sofort abstürzen |
| capa | listet auf, was das Binärprogramm kann: verschlüsseln, Sockets öffnen, in Prozesse injizieren |
| floss | findet versteckten Text, den einfaches strings übersieht |
| Unicorn | führt eine einzelne Funktion mit von dir gewählten Eingaben aus, ohne das Programm auszuführen |
| Triton | verfolgt, wohin angreiferkontrollierte Daten während eines Laufs gelangen |
| Frida | beobachtet und verändert ein Programm während seiner Ausführung |
| semgrep / cppcheck | durchsuchen das dekompilierte C nach bekannten schlechten Mustern |
| pwntools | die Hilfsbibliothek für Offsets, ELF-Parsing und Exploit-Arbeit |
Dies sind die exakten Versionen, mit denen es gebaut und gemessen wurde:
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
Jedes einzelne ist optional. scripts/capabilities.sh meldet, was diese Maschine hat,
jedes Skript nennt das Tool, das es nicht finden kann, und ein fehlendes Tool verengt die Analyse
auf limitations, anstatt stillschweigend zu scheitern.
Drei Befehle und jedes der oben genannten Tools ist in deinem PATH, exakt in diesen Versionen —
nichts, dem man nachjagen müsste, nichts, das halb konfiguriert zurückbleibt.```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** | klont das Repository oder aktualisiert es, falls du bereits eines hast — der obige Ein-Zeilen-Skill-Installer hinterlässt **keinen** Klon, er arbeitet mit einem temporären Checkout und entfernt ihn, daher brauchen die Toolchain und die Skripte einen eigenen |
| **1** | installiert Nix, den Paketmanager, der das Pinning übernimmt — dann **öffne ein neues Terminal**. *Hast du bereits Nix? Überspringe es.* Ein erneutes Ausführen des Installers über eine bestehende Installation schlägt mit `Found existing plan in /nix/receipt.json` fehl, was bedeutet, dass er sich weigert, das anzutasten, was du bereits hast, und kein Fehler ist, den man beheben müsste |
| **2** | betritt die Shell, vom Repository-Stamm aus, damit `scripts/` zur Hand bleibt. Beim ersten Mal wird viel heruntergeladen; jedes weitere Mal dauert Sekunden |
| **3** | bestätigt es: `ghidra pyghidra r2 rizin`, `qemu-user architectures: 7`, `angr`, `z3`, `afl` — statt der `MISS`-Zeilen, die eine nackte Maschine liefert |
`exit` setzt deinen `PATH` exakt so zurück, wie er war. *Getestet auf Ubuntu 22.04.5 LTS
(x86-64), Determinate Nix 3.22.4.*
<details>
<summary><b>Warum überhaupt die Toolchain pinnen?</b></summary>
- **Vergleichbare Durchläufe.** Die Ausgabe des Dekompilers *ist* die Eingabe des
Analysten. Zwei Personen mit zwei Ghidra-Versionen führen nicht dasselbe
Experiment durch, und keiner kann das Ergebnis des anderen überprüfen.
- **Nichts auf deiner Maschine installiert.** Nix hält jedes Paket unter einem Hash
dessen, was es gebaut hat, sodass das Betreten der Shell nur den `PATH` ändert und
sonst nichts. Verlasse die Shell und dein System ist exakt so wie zuvor.
- **Es funktioniert auch in fünf Jahren noch.** Drei gepinnte Revisionen bauen die
gesamte Toolchain neu auf, was eine veröffentlichte Zahl später überprüfbar macht.
Drei Revisionen bauen die gesamte Toolchain neu auf, auf jeder Maschine, zu jedem
beliebigen Zeitpunkt in der Zukunft:```
nixpkgs ffb3c9b700e759be2ef13237c9d8f953b32a1e46
nixpkgs-angr ac62194c3917d5f474c1a844b6fd6da2db95077d
capa-rules v9.4.0
devshell/flake.lock ist die maßgebliche Quelle; devshell/DEVSHELL.md
listet jedes Tool mit seiner Version und seinem Zweck auf.
REx@Skill ist eine Methode, um zu entscheiden, ob eine Binärdatei einen Defekt aufweist — und ihn zu beweisen. Sieben Subagenten führen sie aus und teilen sich ein Evidenzverzeichnis.
Ein Agent verwandelt die Binärdatei in Evidenz: dekompiliertes C, Disassembly, Strings, eine Karte davon, welcher Code welchen erreicht, und was passiert, wenn man sie tatsächlich ausführt. Fünf Agenten lesen diese Evidenz dann gleichzeitig, jeder jagt eine andere Art von Defekt, und keiner von ihnen kann sehen, was die anderen gefunden haben — fünf Leser, die sich einen Dekompiler teilen, machen dieselben Fehler, also erkauft man sich durch ihre Trennung fünf unabhängige Lesarten statt einer fünfmal wiederholten Meinung. Ein siebter liest zuerst den Code, dann ihre Erkenntnisse, und entscheidet, welche davon Bestand haben.
Nichts wird als Bug gemeldet, es sei denn, vier Dinge werden benannt und in der Binärdatei verortet: wo angreifergesteuerte Daten eindringen (Source), die Operation, die sie brechen können (Sink), die Prüfung, die sie hätte aufhalten sollen (Broken Guard), und wer geschädigt wird (Affected Principal). Fehlt eines davon, geht es als unbewiesener Hinweis hinaus, nicht als Finding.
Jeder Lauf liefert dieselben drei Dinge: die Findings, was ausgeschlossen wurde und warum, und was der Host nicht ausführen konnte.
Das Skillset wurde über zehn Architekturen hinweg getestet — x86-64, i686, ARM, AArch64, MIPS und MIPS64 in beiden Endianness-Varianten, 32-Bit-PowerPC, RISC-V und Apple arm64 — auf ELF, PE und Mach-O, Firmware und Roh-Blobs. Nichts begrenzt es darauf: jede Architektur, die dein Dekompiler liften kann, liegt im Geltungsbereich.
| Agent | Läuft | In einer Zeile | |
|---|---|---|---|
| 01 | re-recon | zuerst, allein | Extrahiert die Evidenz. Jagt keine Bugs — ein selbstsicheres Finding hier ist der Fehlermodus. |
| 02 | re-bughunt | immer | Baut den stärksten ehrlichen Fall auf, dass ein Defekt existiert. Zählt jeden Sink auf. |
| 03 | re-safety | immer | Versucht, ihn als korrekt zu beweisen, und meldet jede Verpflichtung, die er nicht erfüllen kann. |
| 04 | re-arithmetic | wenn indiziert oder dimensioniert wird | Größe, Index, Breite und Vorzeichenbehaftung über Aufrufgrenzen hinweg — durch einen Solver entladen, nicht im Kopf von jemandem. |
| 05 | re-lifecycle | wenn allokiert wird | Allokation, Free, Ownership, Init und Fehlerpfade. Der Fehlerpfad ist der, den niemand getestet hat. |
| 06 | re-logic | wenn authentifiziert wird | Autorisierung, Zustandsmaschinen, Krypto, Validate-here-use-there. Keine Signatur, nach der man greppen kann. |
| 07 | re-reconcile | zuletzt, allein | Liest den Code, bevor er irgendjemandes Schlussfolgerungen liest, und entscheidet dann und berichtet. |
Ein Verzeichnis pro Binärdatei, benannt <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
**Warum der Hash im Namen steht.** Zwei Builds eines Programms teilen einen Dateinamen, aber nicht einen
SHA-256, sodass Beweis und Binärdatei nicht stillschweigend auseinanderdriften können: Kompiliere das Ziel neu und
du bekommst ein neues Verzeichnis statt eines verschmutzten.
`pipeline_status.py` prüft diesen Baum und meldet, welche Stufen nie ausgeführt wurden — denn eine
Stufe, die nie ausgeführt wurde, hinterlässt keinen Fehler, nur ein fehlendes Verzeichnis, das
genau wie „ausgeführt, nichts gefunden" aussieht.
</details>
---
## 4. Tool-Inventar — welches Skript ruft was auf
#### Dekompilieren und lesen
| Tool | Version | Verwendet von | Für |
|---|---|---|---|
| **Ghidra** | 12.1.2 | `ghidra_export.py` `batch_decompile.sh` `decompile_addr.py` | der Dekompiler — das tragende Tool |
| **pyghidra** | 3.1.0 | dieselben drei | dessen Headless-Betrieb |
| radare2 | 6.2.0 | `run_tools.sh` `strings_report.py` `brief.py` | JSON aus jedem Befehl |
| rizin | 0.9.1 | `capabilities.sh` `preflight.sh` | ein **zweiter** Dekompiler — Gegenprobe |
| binutils | 2.46 | `triage.py` `inventory.py` `run_tools.sh` | readelf, objdump, nm, strings, size |
| file | 5.48 | jeder Einstiegspunkt | erster Befehl, jedes Mal |
#### Triage — was ist es, was kann es
| Tool | Version | Verwendet von | Für |
|---|---|---|---|
| **capa** | 9.4.0 | `run_tools.sh` `brief.py` | Fähigkeiten aus Regeln, mit Adressen |
| **floss** | 3.1.1 | `strings_report.py` | Strings, die `strings` nicht sehen kann |
| yara | 4.5.7 | `run_tools.sh` `analyze.sh` | Packer, Krypto-Konstanten, Bibliotheksversionen |
| detect-it-easy | 3.21 | `run_tools.sh` | Packer- und Compiler-Identifikation |
| checksec | pwntools | `triage.py` `brief.py` | NX / RELRO / Canary / PIE |
#### Ausführen — der größte einzelne Hebel
| Tool | Version | Verwendet von | Für |
|---|---|---|---|
| **qemu-user** | 11.1.0 | `dynamic_probe.py` `quick_dynamic.sh` | Binärdateien fremder Architekturen ausführen |
| **9 Cross-Sysroots** | glibc | `setup_sysroots.sh` | ohne sie kann qemu nicht einmal eine fremde dynamische Binärdatei laden |
| gdb / ltrace / strace | 17.2 | `capabilities.sh` meldet sie | Traces; `ltrace` ist das am meisten unterschätzte Tool hier |
> In einem gemessenen Vergleich erzielte dasselbe Modell mit
> verfügbarer Ausführung etwa **3× den Recall** als ohne. Diese Zeile ist der Grund, warum die Sysroots mit dem Flake ausgeliefert werden.
#### Stille Bugs laut machen
| Tool | Version | Verwendet von | Für |
|---|---|---|---|
| **valgrind** | 3.27.1 | `sanitize_run.sh` | das Nächste zu ASan für eine Binärdatei, die du nicht neu bauen kannst |
| **AFL++** | 5.00c | `fuzz_target.sh` `quick_dynamic.sh` | coverage-gesteuertes Fuzzing, QEMU-Modus |
| libdislocator | mit AFL++ | `sanitize_run.sh` `quick_dynamic.sh` | Seite pro Allokation — verwandelt einen stillen OOB-Read in einen Fault |
| clang | 21.1.8 | `triage.py` | `-fsanitize=...` auf geliftetem Code |
#### Lösen und emulieren
| Tool | Version | Verwendet von | Für |
|---|---|---|---|
| **z3** | 4.16.0 | `check_bound.py` | entkräftet eine Bounds-Behauptung — 9 Modi plus eine generische Escape-Hatch |
| **angr** | 9.2.154 | `symfn.py` | symbolischer Harness pro Funktion: Hijack, OOB-Write, Division durch Null |
| **unicorn** | 2.1.4 | `emulate.py` | EINE Funktion isoliert mit von dir gewählten Eingaben ausführen |
| triton | 3.7.0 | verfügbar | konkolische Ausführung und Taint über einen konkreten Trace |
| pwntools | 4.15.0 | `brief.py` `run_tools.sh` | `cyclic()`-Offsets, ELF/GOT-Parsing |
#### Statische Analyse über dekompiliertes C
| Tool | Version | Verwendet von | Für |
|---|---|---|---|
| cppcheck | 2.21.1 | `run_tools.sh` `analyze.sh` | toleriert Code, der nicht kompiliert — Dekompiler-Ausgabe tut das nicht |
| semgrep | 1.172.0 | `run_tools.sh` `analyze.sh` | Musterregeln, kein Build nötig |
| flawfinder | 2.0.20 | `capabilities.sh` | lexikalisch; ein grep mit Meinungen |
<details>
<summary><b>Ebenfalls in der Shell</b> — Firmware, Formate, Ausnutzbarkeit</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
Vollständiges Inventar mit jeder Version: [`devshell/DEVSHELL.md`](https://github.com/tihanyin/rex-skill/blob/main/devshell/DEVSHELL.md)
</details>
---
## 5. Verwende es mit etwas anderem als Claude
`general-skill/SKILL-RE.md` ist **eine eigenständige Datei** — 3 750 Zeilen reines
Markdown mit `name`/`description`-Frontmatter. Alles, was das Claude-Bundle hat,
in einem Stück: derselbe Evidenzstandard, dieselbe Pipeline, dieselben Regeln zum
Fuzzing einer fremden Architektur. Klone das Repo, sodass `scripts/` daneben liegt, dann:
| Runner | Wie |
|---|---|
| **Codex** | richte `AGENTS.md` darauf, oder füge es als System-Prompt ein |
| **opencode** | `{"instructions": ["SKILL-RE.md"]}` in `opencode.json` |
| **Cursor / Windsurf** | lege es als Projektregel ab |
| **Eine einfache API-Schleife** | es ist nur Markdown — stelle es voran |
| **Ein Mensch** | es liest sich wie ein Lehrbuch; das war der Punkt |
> **Warum zwei Formen?** Das Claude-Bundle ist ein 16-KB-Kern plus zwölf Referenzdateien,
> die bei Bedarf geladen werden — kleiner Kontext, bis eine bestimmte Frage ein bestimmtes
> Kapitel braucht. Nur Claude Code folgt diesen Zeigern, also bekommt jeder andere Runner die
> einzelne Datei stattdessen.
---
---
## 6. Was drin ist```
.
├── 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
Die Skill läuft überall, wo Claude Code läuft — Linux, macOS, WSL. Sie ist Markdown:
der Kern, 12 Referenzen, 7 Subagenten und /re-analyze. Nichts daran ist
plattformspezifisch, und die Skripte sind portables Python und Bash. Was variiert, sind
die Tools darunter.
Die DEVSHELL wird auf Linux gebaut und getestet — x86-64 (Ubuntu 22.04.5 LTS) und aarch64. macOS ist der Punkt, an dem es dünn wird, weil elf der Tools auf Darwin gar nicht existieren:
qemu-user übersetzt Linux-Syscalls, ist also per Definition Linux, und die
neun Cross-Architecture-Sysroots gehören dazu.
Was das auf einem Mac übrig lässt. Jede statische Stufe: Ghidra-Dekompilierung, radare2 und rizin, angr und z3, die statischen Analysatoren, Triage, Strings und Erreichbarkeit. Was verloren geht, ist die Ausführung — die dynamische Sonde, Sanitizer-Läufe, Fuzzing und jedes Fremdarchitektur-Binary. Das ist ein echter Verlust: ein Absturz ist der stärkste Beweis, den diese Methodik hat, und nichts Statisches ersetzt ihn.
Nichts gibt etwas anderes vor. scripts/capabilities.sh meldet, was der Host
tatsächlich kann, scripts/pipeline_status.py markiert die Stufe als abwesend, und die
Kosten landen in den limitations des Berichts. Eine Stufe, die nicht lief, wird nie
als eine Stufe gemeldet, die lief und nichts fand — siehe §9.1 der Skill.
Kurz gesagt: auf Linux analysieren. Lesen, planen und den Bericht schreiben überall.
Norbert Tihanyi · x.com/@TihanyiNorbert
ein Fund = Source · Sink · gebrochener Guard · betroffener Principal.
alles darunter ist eine Hypothese.
capabilities.sh | was dieser Host tatsächlich kann — vor der Planung prüfen |
analyze.sh | die gesamte Pipeline der Reihe nach, Schritte 0-5, dann übergibt sie |
batch_analyze.sh | dieselbe Pipeline über ein Verzeichnis von Zielen |
pipeline_status.py | einen Evidenzbaum prüfen: welche Stufen liefen, und was jede Abwesenheit kostet |
overview.py | die Gestalt eines Programms: Zählungen, Aufrufbaum, Sinks, Sources |
brief.py | die Ausgabe jedes Tools für ein Ziel, konsolidiert, Lücken benannt |
fn.py | eine Funktion lesen statt der gesamten Dekompilierung |
reach.py | Source-→-Sink-Pfade über den Aufrufgraphen |
bounds_worklist.py | die arithmetischen Behauptungen, die abgetragen werden müssen, in drei Stufen |
check_bound.py | eine davon mit z3 abtragen — 9 Modi plus eine generische Notluke |
symfn.py | symbolischer Harness für eine Funktion |
emulate.py | eine Funktion isoliert mit von dir gewählten Eingaben ausführen |
quick_dynamic.sh | einfach ausführen: keine Eingabe, dann Eingaben, die die meisten Dinge brechen |
fuzz_target.sh | Fuzzing gezielt auf den Kanal, den das Programm tatsächlich liest |
sanitize_run.sh | feindselige Allokatoren — einen stillen Heap-Bug zum Absturz bringen |
sanitize.py | modellgerichteten Text unter Quarantäne stellen, bevor irgendetwas ihn liest |
| auf macOS nicht vorhanden | qemu-user · gdb · gef · ltrace · strace · valgrind · AFL++ · honggfuzz · frida · bpftrace · rr |
| außerdem | der gepinnte Build von capa besteht seine eigene Testsuite auf Darwin nicht |
| und | argv-Fuzzing interponiert __libc_start_main, was glibc ist — es gibt kein macOS-Äquivalent |