
BianryNinja-Plugin zur Identifizierung von Schwachstellen in dekompilierten Binärdateien mit sowohl programmatischen Scans als auch LLM-Unterstützung.
LLM-gestützte Schwachstellenanalyse für Binary Ninja.
VulnFanatic-NG fügt ein Seitenpanel hinzu, das die aktuelle Binärdatei scannt und ein LLM — standardmäßig ein lokal gehostetes OpenAI-kompatibles Modell, alternativ Anthropic Claude, Google Gemini oder Azure OpenAI (siehe LLM-Backends) — bewerten lässt, ob verdächtiger Code tatsächlich verwundbar ist. Es arbeitet hauptsächlich mit der Decompiler-Ausgabe (HLIL) von Binary Ninja, greift bei Bedarf auf Assembler zurück und meldet nur bestätigte Probleme mit klickbaren Verweisen zurück zum Code.
Ein Scan läuft in bis zu drei Phasen ab (Phase 3 ist optional und nur online):
Findet Aufrufstellen gefährlicher Funktionen, die in
rules/phase1_rules.json definiert sind — strcpy,
memcpy, sprintf/Formatstrings, system, alloca, scanf, Command/Exec-
APIs, schwache Zufallszahlen, die free/delete-Familie (Use-after-Free / Double-Free),
Einlesen nicht vertrauenswürdiger Eingaben in feste Puffer (recv/read/fread/ReadFile),
SQL-Injection (sqlite3_exec/mysql_query/PQexec), deaktivierte TLS-
Zertifikatsprüfung (SSL_CTX_set_verify/curl), SSRF und fehlerhaftes
Rechte-Management (setuid/setresgid), die memset/bzero-Familie und
Vergleiche mit einer angreiferkontrollierten Länge (memcmp/strncmp →
Authentifizierungs-Bypass), über C/C++, Win32 und (best effort) Rust FFI hinweg. Abgedeckt
sind auch gehärtete _chk- (FORTIFY) und Annex-K-_s-Varianten. Gebundene
formatierte Ausgabefunktionen (snprintf und Varianten) haben ihre eigene standardmäßig sichere
Regel, sodass ein korrektes Größenargument nicht als Überlauf gemeldet wird. Aufrufstellen
werden auf drei Arten gefunden: direkte Aufrufe der benannten Symbole; Aufrufe über
Forwarding-Thunks / PLT-Stubs (die tatsächlichen Aufrufer werden wiederhergestellt, damit
ein Import, der nur über einen Stub erreicht wird, nicht übersehen wird); und — sofern
vulnfanatic.scanIndirectCalls nicht deaktiviert ist — indirekte Aufrufe, die über einen
Funktionszeiger oder eine Vtable dispatched werden und von Binary Ninja einer gefährlichen Funktion
zugeordnet wurden.
Für jede Aufrufstelle wird ein interprozeduraler, decompiler-zentrierter Kontext
aufgebaut, der auf ein Token-Limit begrenzt ist (Standard 100k):
__*_chk- und grenzgeprüfte *_s-Varianten haben zusätzliche führende
Argumente, wodurch sich die Position von Format/Größe/Ziel verschiebt,s->buf
auf die tatsächliche Arraygröße des Felds auflöst statt auf die Zeigergröße von s;
Strukturdefinitionen im Typbereich tragen ebenfalls Bygrößen pro Feld,0x40
oder begrenzt auf [0, 0xff]), die das Modell als Ground Truth verwendet, wenn es eine
Größe mit einer Pufferkapazität vergleicht, statt zu raten,vulnfanatic.includeStackLayout),if/Schleifen-/-Bedingungen, die den Aufruf absichern),Dieser Kontext plus ein regelspezifischer Prompt wird an das Modell gesendet, das eine strukturierte Bewertung zurückgibt. Nicht-Probleme werden verworfen. Die Prompts sind auf ein starkes lokales Code-Modell (z. B. Qwen2.5-Coder) abgestimmt und weisen es an, den gesamten Fluss zu analysieren und nur JSON auszugeben.
Das Modell wird angewiesen, Recall zu bevorzugen — plausible, sicherheitsrelevante Probleme zu melden und Unsicherheit über eine Confidence auszudrücken, anstatt etwas zu verwerfen, das es nicht vollständig beweisen kann. Es zeigt seine Arbeit in einem Scratchpad, das die wörtlichen Code-Snippets zitiert, auf die es sich gestützt hat (die Eingabequelle, jede Absicherung, die Größe/Länge, den relevanten Typ und die Senke), das beim Befund gespeichert wird, damit du die Argumentation prüfen kannst.
Jeder Befund trägt eine Confidence (hoch/mittel/niedrig): hoch = die gesamte Kette ist im
Kontext sichtbar; mittel = wahrscheinlich, mit ein oder zwei abgeleiteten Verbindungen; niedrig = ein
Anhaltspunkt, der eine manuelle Prüfung wert ist. Das ist die zentrale Kennzahl (die Schweregradschätzung
des Modells ist ein sekundäres Feld). Setze vulnfanatic.minConfidence, um alles unterhalb einer Schwelle zu verwerfen.
Standardmäßig bevorzugt VulnFanatic-NG Recall (echte Probleme finden). Wenn du zu viele False Positives bekommst, verschärfe die Einstellung mit einer der folgenden Optionen:
vulnfanatic.validationPass (Standard aus) — führt einen zweiten LLM-Durchlauf aus, der
jeden markierten Befund anhand desselben Kontexts gegenprüft (die Scratchpad-Snippets verifiziert
und den Fluss erneut nachverfolgt) und das Urteil oder die Konfidenz korrigieren kann.
Verdoppelt die LLM-Aufrufe für markierte Kandidaten.
vulnfanatic.validatorModel (plus validatorProvider / validatorBaseUrl /
validatorApiKey), um den zweiten Durchlauf auf einem anderen Modell auszuführen. Eine zweite
Meinung ist von einem unabhängigen Modell weitaus nützlicher — es teilt weniger
blinde Flecken und bestätigt das erste Urteil viel seltener nur pro forma (Modelle neigen dazu,
ihre eigenen Antworten zu bevorzugen). Ein gutes Muster ist eine Kaskade: ein schnelles Modell als
Analytiker (breiter Recall) und dein stärkstes Modell als Validator, das nur bei
markierten Kandidaten läuft. Lasse das Validator-Modell leer, um mit dem Analytiker-Modell zu validieren.
Der Validator sollte mindestens so leistungsfähig sein wie der Analytiker — ein
schwächeres fügt meist nur falsche Ablehnungen hinzu. Alles außer Provider/Base-URL/Schlüssel/
Modell wird von den Analytiker-Verbindungseinstellungen übernommen; ein leerer Validator-Schlüssel
verwendet den Analytiker-Schlüssel erneut; und wenn der Validator-Endpunkt nicht erreichbar ist, bleibt das
erste Urteil erhalten (der Befund geht durch einen Validator-Ausfall nie verloren).vulnfanatic.minConfidence (Standard low) — auf / anheben, um nur
stärkere Befunde zu melden.Geschwindigkeit. Der meiste Aufwand pro Aufruf steckt im geschriebenen Reasoning, daher
steuert vulnfanatic.verdictReasoning, wie viel das Modell schreibt:
concise (Standard) — eine kurze Begründung mit 1–3 Sätzen, ohne wörtlichen Code. Deutlich
schneller als full bei kaum Genauigkeitsverlust; du kannst auch
vulnfanatic.maxResponseTokens senken.full — das detaillierte Scratchpad mit zitierten Snippets (am besten prüfbar, am langsamsten).none — nur das Urteil. Am schnellsten; kombiniere es mit einem reasoning-fähigen Backend
(vulnfanatic.reasoningEffort), damit das interne Denken des Modells die Arbeit erledigt.
Bei einem einfachen lokalen Modell verliert none an Genauigkeit (überhaupt keine Chain-of-Thought).Unterstützende Präzisionsfunktionen, die immer aktiv sind (sie informieren das Modell, ohne Befunde zu unterdrücken):
_s- (Annex K) und
_chk- (FORTIFY) Varianten sowie längenbegrenzte APIs als sicher, sofern das Größenargument
selbst nicht falsch ist.Der Scan Offline-Button führt Phase 1 ohne Modell aus — rein programmatische
Heuristiken, die im offline-Block jeder Regel in phase1_rules.json deklariert sind. Er
markiert gefährliche Aufrufstellen und eliminiert die offensichtlich sicheren, wobei eine
heuristische Confidence vergeben wird:
memcpy/memmove mit konstanter Länge, ein
strcpy aus einer konstanten Zeichenkette, ein printf mit konstantem Format, ein system
mit konstantem Befehl usw. — Aufrufe, deren maßgebliches Argument eine Compile-Zeit-Konstante
ist und daher nicht angreiferkontrolliert sein kann. „Konstant" umfasst Werte, die Binary Ninjas
Value-Set-Analyse stromaufwärts auf eine feste Zahl festgenagelt hat, nicht nur literale Argumente.strlen/Größen-
Vergleich, if (len < …)), irgendwo im Fluss — einschließlich in den entlang des Pfads aufgerufenen
Funktionen — sodass es möglicherweise bereits behandelt wird. (Ein Zweig, der die Variable nur erwähnt,
ohne sie zu vergleichen, zählt nicht mehr und entfernt damit eine Quelle unechter Herabstufungen.)Die Heuristiken verwenden ein kleines deklaratives Vokabular in den Regeln
(constant_safe_args, eliminate_if_all_args_constant, format_arg_lookup,
length_guard_vars, base_confidence, skip), das von Python-Prädikaten ausgewertet wird —
kein eingebetteter Code zum exec. Die meisten Regeln haben eine Offline-Definition
(Overflow, Format-String, Command-Exec, scanf, Pfadbehandlung, schwache RNG, schwaches numerisches Parsen,
Rechteänderungen, Allokationsgröße, …). Nur die beiden Kategorien, die wirklich semantische Analyse
benötigen, werden offline übersprungen und dem LLM überlassen: die free/delete-Familie
(Use-after-Free / Double-Free, die Pointer-Lifetime-Tracking benötigt) und TLS-
Verifizierung (der Fehler ist ein spezifischer konstanter Wert wie SSL_VERIFY_NONE). Die
Offline-Zusammenfassung berichtet, wie viele Stellen markiert / eliminiert / übersprungen (LLM nötig) /
fehlgeschlagen wurden, sodass die Zählungen aufgehen. Das ist eine schnelle Triage; für echte Bewertung — und
für die übersprungenen Kategorien — führe den vollständigen LLM-Scan aus.
Offline-Befunde bauen weiterhin denselben vollständigen interprozeduralen Kontext auf, den ein
Online-Scan senden würde (nur für die markierten Stellen) und speichern ihn, sodass sie nach der
Triage genau wie Online-Befunde als Fine-Tuning-Daten exportiert werden können. Deaktiviere
vulnfanatic.offlineBuildContext, wenn du maximale Offline-Geschwindigkeit möchtest.
Läuft nur, wenn die Binärdatei echte Symbole / Variablennamen zu haben scheint. Lokalisiert
sicherheitsrelevante Funktionen, die in
rules/phase2_rules.json definiert sind —
Authentifizierung, Kryptografie (einschl. schwacher Algorithmen), Signatur-/Zertifikats-
prüfung, Sitzungs-/Token-Behandlung, Zugriffskontrolle, Umgang mit Geheimnissen/Schlüsseln,
Eingabevalidierung, nicht-konstantzeitlicher Geheimnisvergleich und unsichere
Deserialisierung — abgeglichen über Funktionsnamen und referenzierte Zeichenketten und anschließend vom Modell geprüft.
Ein Firmware-Härtungs-Audit gegen Fault-Injection (Spannungs-/Takt-/EM-Glitching)
und Seitenkanal-Angriffe (Timing/Power), basierend auf Hardware-Angriffs-Mitigationsleitlinien.
Anders als die Phasen 1–2 (die Bugs finden) meldet Phase 3 eine fehlende oder verletzte
Härtungskontrolle an einer sicherheitskritischen Funktion — zum Beispiel:
Default-Fail-Zweige, doppelt geprüfte Sicherheitsentscheidungen, Post-Loop-Zähler-
Validierung, Zustandskonstanten mit hoher Hamming-Distanz (statt einfachem 0/1), konstantzeitlicher
Voll-Längen-Geheimnisvergleich, Zugriff/Löschen von Geheimnissen mit randomisiertem Offset,
Encrypt-then-Verify (Anti-DFA), Control-Flow-Integrity-Zähler, Vermeidung von User-Land-Krypto
und der direkte Umgang mit rohem Schlüsselmaterial
(rules/phase3_rules.json).
Da Compiler-Optimierungen Quelltext-Ebene-Schutzmaßnahmen entfernen können, werden diese Kontrollen am besten in der kompilierten Binärdatei verifiziert — genau das prüft dieser Scan. Phase 3 ist nur-LLM (online), symbol-gesteuert und standardmäßig deaktiviert; aktiviere sie pro Scan mit dem Kontrollkästchen Phase 3 auf dem New-Scan-Tab (sie läuft nie im Offline-Modus).
Befunde werden in einer Tabelle aufgelistet (Status, Konfidenz, Phase, CWE, Funktion, Adresse, Titel) mit einem Detailbereich, der die Erklärung, das Analyse-Scratchpad und die Validierungsnotizen zeigt. Doppelklicke eine Zeile, um die Binäransicht zum Code zu navigieren.
Jeder Befund startet als Untriaged. Rechtsklicke eine Zeile, um ihren Status zu setzen — Mark as Real Issue, Mark as False Positive oder Mark as Untriaged. Jede Statusänderung öffnet ein „Provide reason:"-Textfeld (der Grund wird mit dem Befund gespeichert). Die Tabelle macht den Status offensichtlich: Echte Probleme sind grün/fett und werden nach oben sortiert, False Positives sind grau/durchgestrichen und werden nach unten sortiert, Untriaged liegen dazwischen mit ihrer Konfidenzfarbe. Eine Zusammenfassungszeile zeigt die Zählungen.
Jeder Ergebnis-Tab hat einen Export triaged (fine-tuning)…-Button, der nur
die triagierten Befunde (Real Issue + False Positive) als OpenAI-Chat-Format-JSONL
für Fine-Tuning exportiert: Jedes Beispiel paart den ursprünglichen System+User-Prompt mit dem
menschenkorrigierten Urteil als Assistant-Ziel (ein False Positive lehrt
is_vulnerable=false mit deinem Grund; ein Real Issue verstärkt is_vulnerable=true),
sodass du die Genauigkeit des Modells für deine Binärdateien iterativ verbessern kannst.
Der pro Befund im Detailbereich angezeigte Kontext (und zur Rekonstruktion der
Fine-Tuning-Prompts verwendete) wird standardmäßig vollständig aufbewahrt — gesteuert durch
vulnfanatic.storedContextChars (0 = unbegrenzt; setze eine positive Obergrenze, z. B. 4000,
um das BNDB-Wachstum zu begrenzen, auf Kosten der Kontexttreue).
Das Panel ist tabbasiert. Der erste Tab ist immer New Scan, wo du festlegst:
<Zeitstempel> <Modus>, z. B.
2026-06-15 14:03:50 offline),dann drückst du Start Scan oder Scan Offline. Jeder Lauf öffnet seinen eigenen Ergebnis-Tab, und Befunde streamen live hinein. Alle Scans werden in der BNDB gespeichert, sodass du z. B. einen Offline-Scan behalten und später einen Online-Scan hinzufügen oder Läufe mit verschiedenen Regelsets nebeneinander vergleichen kannst — sie erscheinen beim erneuten Öffnen der Datenbank wieder als Tabs. Das Schließen eines Tabs löscht diesen Scan dauerhaft aus der BNDB — zur Unfallvermeidung erscheint eine Bestätigung, die das Ankreuzen von „I confirm that I will lose the results from forever." erfordert, bevor der Button Delete results forever aktiv wird. Export current scan… schreibt den ausgewählten Tab nach Markdown/JSON.
Jede geöffnete Binärdatei hat ihren eigenen unabhängigen Panel-Zustand — ihre eigenen Scan-Tabs und ihren laufenden Scan. Wenn du in einer Binärdatei einen Scan startest und zu einer anderen wechselst, zeigt das Panel die Ergebnisse der zweiten Binärdatei (und lässt sie separat scannen); der Scan der ersten Binärdatei läuft im Hintergrund weiter und ist intakt, wenn du zurückwechselst.
Der Paketordner dieses Plugins heißt vulnfanatic_ng (ein gültiger Python-Bezeichner —
Binary Ninja importiert den Plugin-Ordnernamen als Modul, daher würde ein Name mit Bindestrich
wie VulnFanatic-NG nicht geladen).
(Optional) Installiere genaues Token-Zählen in Binary Ninjas Python: ``` pip install tiktoken
Erstellen Sie einen Symlink oder kopieren Sie den Ordner vulnfanatic_ng
in Ihr Binary-Ninja-Benutzer-Plugins-Verzeichnis:
~/Library/Application Support/Binary Ninja/plugins/~/.binaryninja/plugins/%APPDATA%\Binary Ninja\plugins\Zum Beispiel unter macOS: ``` ln -s "$(pwd)/vulnfanatic_ng" "$HOME/Library/Application Support/Binary Ninja/plugins/vulnfanatic_ng"
Starten Sie Binary Ninja neu (oder führen Sie Reload Plugins aus). Ein VF-Symbol erscheint in der rechten Seitenleiste.
Öffnen Sie Settings (das Zahnrad / Edit ▸ Preferences ▸ Settings) und suchen Sie nach
vulnfanatic. Legen Sie mindestens Folgendes fest:
vulnfanatic.apiProvider wählt aus, wie Anforderungen erstellt und authentifiziert werden. Das Urteilsschema (und alle Regel-Prompts) ist über alle Anbieter hinweg identisch.
AWS Bedrock kann über den
openai-Anbieter über seinen OpenAI-kompatiblen Endpoint verwendet werden, sodass kein dediziertes Backend erforderlich ist.
Weitere nützliche Einstellungen: vulnfanatic.maxContextTokens (Standard 100000), vulnfanatic.maxResponseTokens, vulnfanatic.temperature, vulnfanatic.reasoningEffort (off/low/medium/high; Standard high — bittet das Modell, vor der Antwort nachzudenken, wo unterstützt; pro Anbieter abgebildet: openai/azure reasoning_effort, anthropic adaptives Denken + output_config.effort, google dynamisches thinkingConfig; wird automatisch entfernt und erneut versucht, wenn ein Modell es ablehnt), vulnfanatic.requestTimeoutSec, vulnfanatic.callPathMaxDepth / , (die dekompilierten Funktionskörper entlang des Aufrufpfads einbeziehen; Standard an) / (Obergrenze, Standard 12), (auch andere Funktionen einbeziehen, die entlang des Pfads aufgerufen werden und die Grenz-/Validierungsprüfungen enthalten können; Standard an) / (Obergrenze, Standard 12), (Struct-/Union-/Enum-Definitionen einbeziehen; Standard an) / (Obergrenze, Standard 24), (Aufrufargumente zurück durch ihre Erzeuger/Verbraucher verfolgen und diese Funktionskörper einbeziehen; Standard an) / (Obergrenze, Standard 8), (das Stack-Variablen-Layout der aufrufenden Funktion einbeziehen, wenn sie einen Puffer fester Größe hat; Standard an), (auch gefährliche Aufrufe erkennen, die über einen aufgelösten Funktionszeiger/Vtable ausgeführt werden; Standard an — für einen schnelleren Scan bei sehr großen Binaries deaktivieren), (den zweiten Kontrolldurchlauf ausführen; Standard aus) / / / / (den Validierungsdurchlauf auf einem separaten, unabhängigen Modell ausführen — leer = dasselbe Modell wie der Analyst) / (//; Befunde unterhalb dieses Werts verwerfen; Standard ), (Stellen, die das Modell nicht bewerten konnte, als Leads mit -Konfidenz „Unscored“ melden, anstatt sie zu verwerfen; Standard an), (Aufrufstellen mit ausschließlich konstanten Argumenten überspringen; Standard aus), (//; wie viel Begründung das Modell pro Urteil verfasst — der wichtigste Geschwindigkeitshebel; Standard ), / / (jede Phase aktivieren; Phase 3 ist nur online und wird normalerweise pro Scan über das Kontrollkästchen umgeschaltet statt hier), / , (tiktoken-Kodierung für Token-Schätzungen; fällt auf eine Zeichenheuristik zurück, wenn tiktoken nicht installiert ist), (vollständigen Kontext für Offline-Befunde erstellen, damit sie für das Feintuning exportiert werden können; Standard an), (ausführliche Pipeline-Ablaufverfolgung auf der Konsole; Standard aus) / (alle binäridentifizierenden Details schwärzen, damit das Protokoll geteilt werden kann — siehe unten), , (HTTPS-Zertifikate überprüfen; Standard an) / (CA-Bundle für HTTPS — siehe Troubleshooting, falls Sie erhalten), und / / (verweisen Sie auf Ihre eigenen Regeldateien, um Erkennungen und Prompts anzupassen).
Sicherheitshinweis: Der API-Schlüssel wird in den Einstellungen von Binary Ninja im Klartext gespeichert. Bevorzugen Sie für sensible Schlüssel die Überschreibung über Umgebungsvariablen.
Setzen Sie vulnfanatic.apiBaseUrl auf den Literalwert TEST, um ohne LLM auszuführen:
/tmp/vulnfanatic_ng/<binary>-<timestamp>/ geschrieben.Verwenden Sie dies, um genau zu prüfen und zu validieren, was VulnFanatic-NG an das Modell senden würde, und um mit den Regel-Prompts/dem Kontext zu iterieren, ohne Modellzeit zu verbrauchen.
Aktivieren Sie vulnfanatic.debugLogging, um eine ausführliche Schritt-für-Schritt-Ablaufverfolgung der Scan-Pipeline (sowohl online als auch offline) in das Binary-Ninja-Log/die Konsole auszugeben: jede Aufrufstelle, jede Überspringungs-/Eliminierungsentscheidung, Kontextaufbau (nur Größe), jede LLM-Anfrage (Anbieter/Modell/Endpoint, Wiederholungen, Fallbacks), jedes Urteil und jeden gemeldeten Befund. API-Schlüssel werden niemals protokolliert.
Während das Debug-Logging aktiviert ist, behält ein Online-Scan jeden Kandidaten in der Ergebnistabelle bei, anstatt diejenigen zu verwerfen, die nicht zu bestätigten Problemen werden; jeder wird mit einem Nur-Debug-Status markiert (abgeblendet, ans Ende sortiert):
Ein Debug-Scan zeigt also eine Zeile pro Kandidat in der /N-Gesamtzahl, und die Zusammenfassung meldet Probleme getrennt von den Zählungen für rejected/skipped/error. Sie können jede dieser Zeilen per Rechtsklick erneut als Real Issue oder False Positive einstufen (wodurch sie für den Fine-Tuning-Export geeignet wird). (Offline-Scans sind nicht betroffen — sie rufen das LLM nie auf.)
Unabhängig vom Debug-Modus: Wenn ein Modell eine nicht parsebare Antwort zurückgibt — ein verirrtes Token wie Gemmas <unused…>, Prosa statt JSON oder eine leere Nachricht (nur eine role, kein content) — führt der Client einen korrigierenden Wiederholungsversuch durch und fragt erneut nach JSON, allerdings mit deaktiviertem Structured-Output-Format; wenn das gelingt, bleibt das Format für den Rest des Scans deaktiviert. Der Client liest auch den Reasoning-Kanal (reasoning_content / reasoning), wenn content leer ist, sodass Reasoning-Modelle, die ihre Antwort dort ablegen, weiterhin funktionieren.
Der Fall der leeren Nachricht tritt häufig bei Reasoning-Modellen wie GPT-OSS / o1 auf, die über eine OpenAI-kompatible API bereitgestellt werden (z. B. mlx-community/gpt-oss-20b): Mit gesetztem response_format=json_object wird der Harmony-„final“-Antwortkanal oft unterdrückt und der Server gibt {"role": "assistant"} ohne Inhalt zurück. Diese Modelle können ihr gesamtes Ausgabebudget auch im Reasoning-Kanal aufbrauchen und mitten im Gedanken abgeschnitten werden, wobei sie Prosa ohne jegliches JSON zurückgeben. Der automatische Wiederholungsversuch behebt die Format-bezogenen Fälle; wenn es weiterhin auftritt, deaktivieren Sie vulnfanatic.sendJsonResponseFormat, senken Sie vulnfanatic.reasoningEffort (damit weniger Budget für das Denken aufgewendet wird) und/oder erhöhen Sie vulnfanatic.maxResponseTokens. Eine anhaltende <unused…>/Müll-Antwort bedeutet stattdessen meist, dass der Prompt das Kontextfenster des Modells überschreitet (setzen Sie vulnfanatic.modelContextWindow und/oder erhöhen Sie die Kontextlänge des Servers) oder dass das Modell für strikte JSON-Ausgabe ungeeignet ist (ein Code-Modell wie Qwen2.5-Coder verhält sich hier weitaus besser als Gemma).
Recall-erhaltender Fallback. Wenn ein Kandidat nach dem Wiederholungsversuch immer noch nicht bewertet werden kann, meldet ihn vulnfanatic.flagUnparseableResponses (Standard an) trotzdem als „Unscored“-Befund mit UNKNOWN-Konfidenz — ein Wert, der sich von low unterscheidet (das Modell hat nie ein Urteil erzeugt, es ist also kein Low-Confidence-Urteil) und der ans Ende sortiert wird — wobei die partielle Ausgabe des Modells als Erklärung erhalten bleibt, sodass Sie die Stelle nicht verlieren, sondern sie nur manuell prüfen. Deaktivieren Sie diese Option, um solche Stellen stattdessen zu verwerfen (sie erscheinen dann nur noch als Analysefehler oder als Debug-ERROR-Zeilen).
Aktivieren Sie außerdem vulnfanatic.debugAnonymous, um das Protokoll teilbar zu machen: Es schwärzt alles, was die analysierte Datei identifizieren könnte — Symbol-/Variablennamen und Adressen werden zu pro Lauf gesalzenen Hashes (innerhalb eines Laufs weiterhin konsistent, sodass der Ablauf verfolgbar bleibt), der Dateiname wird ausgeblendet, Befundtext wird durch <redacted> ersetzt, der LLM-Endpoint-Host wird gehasht, und dekompilierter Code / Prompts / Kontext werden nur als Größen protokolliert (niemals der Inhalt). So können Sie ein Debug-Protokoll senden, um ein Problem zu melden, ohne etwas über Ihre Binärdatei preiszugeben.
Befunde — einschließlich ihres False-Positive-Status — werden in der Binary-Ninja-Datenbank
gespeichert. Sie werden in die .bndb geschrieben, wenn Sie die Datenbank speichern (und
sofort geleert, wenn bereits eine .bndb existiert), sodass sie ein erneutes Öffnen überstehen.
Der Scan analysiert jede passende Aufrufstelle (ohne Obergrenze), was für lokale Modelle angemessen ist. Bei einem gehosteten/bezahlten Endpoint sollten Sie bei großen Binaries auf das Volumen achten.
Beide Regeldateien teilen sich einen Umschlag mit einem gemeinsamen system_prompt und
output_schema sowie einer Liste von rules. Kopieren Sie eine mitgelieferte Datei, bearbeiten Sie die
functions/keywords/prompts und verweisen Sie vulnfanatic.rulesPhase1Path /
vulnfanatic.rulesPhase2Path auf Ihre Kopie. Phase-1-Regeln gleichen über functions (exakt)
und name_regex ab; Phase-2-Regeln gleichen über name_keywords, name_regex und
string_keywords ab. Das prompt jeder Regel kann den Platzhalter {function} verwenden.
Die triagierten Exporte sind dafür ausgelegt, direkt wieder in das Modell eingespeist zu werden. Nachdem Sie
Befunde über mehrere Binaries triagiert und bei jeder auf Export triaged
(fine-tuning)… geklickt haben (die .jsonl-Dateien in einem Ordner gesammelt),
führt scripts/finetune_mlx.py ein MLX LoRA-
Feintuning darauf aus.```bash
pip install mlx-lm # Apple Silicon / macOS
python scripts/finetune_mlx.py ./exports
--model mlx-community/Qwen2.5-Coder-7B-Instruct-4bit
--adapter-path ./vf-adapters --iters 800
python scripts/finetune_mlx.py ./exports --model
--fuse --fused-path ./vf-qwen-coder-vuln
Das Skript erwartet als Positionsargument den **training-data-Ordner** und das
Basis-**`--model`** (lokaler Pfad oder MLX/HF-Repo-ID); weitere Parameter sind optional:
`--adapter-path`, `--valid-split` (0.1), `--iters`, `--batch-size` (automatisch begrenzt,
um in einen winzigen Split zu passen), `--num-layers`, `--learning-rate`, `--max-seq-length`
(`0` = **Auto-Fit** auf das längste Beispiel, begrenzt auf 16384; ein positiver Wert
erzwingt dies), `--fine-tune-type` (`lora`/`dora`/`full`), `--seed`, `--fuse`/`--fused-path`,
und `--dry-run` (Daten vorbereiten + den Befehl ausgeben, ohne zu trainieren). Alles nach einem
wörtlichen `--` wird unverändert an `mlx_lm lora` weitergegeben. Es führt rekursiv alle
`*.jsonl` im Ordner zusammen, validiert und **dedupliziert** die Chat-Beispiele, erstellt den
`train.jsonl`/`valid.jsonl`-Split, den MLX erwartet, und startet dann `python -m mlx_lm lora`
(und `mlx_lm fuse` mit `--fuse`).
Stellen Sie das Ergebnis mit einem OpenAI-kompatiblen Server bereit (`mlx_lm.server --model <path>`)
und richten Sie `vulnfanatic.apiBaseUrl` wieder darauf aus, um mit Ihrem angepassten Modell zu scannen.
> VulnFanatic-NG-Kontexte sind groß, daher wendet das Skript standardmäßig **Auto-Fit** auf
> `--max-seq-length` an, abgestimmt auf Ihr längstes Beispiel (aufgerundet, begrenzt auf **16384 Token**).
> Lange Sequenzen dominieren den Trainingsspeicher, daher kann ein großes Modell nahe dieser
> Obergrenze einem kleineren Mac den Speicher ausgehen lassen. Überschreiten Ihre Beispiele die
> Grenze, werden sie gekürzt — übergeben Sie vor dem Export einen höheren `--max-seq-length`-Wert
> (mehr Speicher) oder senken Sie `vulnfanatic.storedContextChars`. Wird das Training durch ein
> Signal abgebrochen (z. B. `exit -10` / SIGBUS), handelt es sich um einen Out-of-Memory-Absturz:
> Senken Sie `--max-seq-length`, fügen Sie `-- --grad-checkpoint` hinzu oder verwenden Sie ein kleineres Modell.
---
## Entwicklung & Tests
Das Plugin besitzt keinerlei erforderliche Drittanbieter-Abhängigkeiten. Reine Module
(`rules`, `tokens`, `llm`, `findings`, `settings`, `prototypes`) werden von einer
Offline-Testsuite abgedeckt, die weder Binary Ninja noch ein Netzwerk benötigt. Die
`tests/`-Suite befindet sich im Quellrepository des Projekts (sie wird nicht im
veröffentlichten Plugin mitgeliefert); führen Sie sie von dort aus. Vom
Paketverzeichnis aus können Sie weiterhin jedes Modul auf Syntax prüfen:```
python3 -m py_compile *.py ui/*.py
python3 -m unittest discover -s tests # from the source repository
Die Binary-Ninja-Module (context_builder, phase1, phase2) lassen sich ohne
Binary Ninja sauber importieren (ihr API-Zugriff ist abgesichert), benötigen aber
ein laufendes Binary Ninja, um ausgeführt zu werden.
argv[1]
per strcpy in einen festen Stack-Puffer kopiert und system() mit der Eingabe
aufruft). Kompilieren Sie es mit Symbolen, um auch Phase 2 zu testen.vulnfanatic.apiBaseUrl, vulnfanatic.apiKey und vulnfanatic.model.SSL: CERTIFICATE_VERIFY_FAILED ... unable to get local issuer certificate —
das Zertifikat des HTTPS-Endpunkts ist in Ordnung, aber das mitgelieferte Python von
Binary Ninja besitzt keinen CA-Bundle, um es zu verifizieren (häufig auf macOS und in
eingebetteten Pythons; Sie sehen das bei gehosteten Endpunkten wie AWS Bedrock,
Anthropic, Google, Azure). Beheben Sie das mit einer der folgenden Optionen, bevorzugt
in dieser Reihenfolge:
pip install certifi. VulnFanatic-NG erkennt es automatisch.vulnfanatic.caBundlePath auf eine
Bundle-Datei (oder ein Verzeichnis) — z. B. den Pfad, den python3 -m certifi
ausgibt, oder /etc/ssl/cert.pem.vulnfanatic.tlsVerify (nur für einen
vertrauenswürdigen bzw. internen Endpunkt oder einen selbstsignierten lokalen
Server — dies deaktiviert die Zertifikatsprüfung).HTTP 400 ... tokenizer.chat_template is not set — das Modell, das Sie
bereitstellen, hat keine Chat-Vorlage, daher kann der /chat/completions-Endpunkt
die Nachrichten nicht formatieren. VulnFanatic-NG greift automatisch für den Rest
des Scans auf den /completions-Endpunkt zurück, wenn dieser Fehler auftritt,
sodass der Scan fortgesetzt wird. Um die erste fehlgeschlagene Anfrage vollständig
zu vermeiden, setzen Sie vulnfanatic.apiMode auf completions. Alternativ beheben
Sie es serverseitig, indem Sie ein Modell bereitstellen, das eine Chat-Vorlage
mitbringt, oder übergeben Sie eine an Ihren Server — z. B. für vLLM:
--chat-template <template.jinja> (oder verwenden Sie eine
-Instruct/-Chat-Modellvariante). Die dedizierte Chat-Vorlage liefert in der
Regel bessere Ergebnisse als der abgeflachte Completions-Prompt.
No JSON object found ... response looks truncated — die Antwort des Modells
wurde abgeschnitten, bevor das JSON fertig war. Zwei Ursachen:
vulnfanatic.maxResponseTokens.maxResponseTokens ist. Lokale Server haben oft ein kleines Fenster (ollama nutzt
standardmäßig num_ctx=2048!). Beheben Sie es, indem Sie
vulnfanatic.modelContextWindow auf das Fenster Ihres Servers setzen (z. B.
ollama num_ctx, llama.cpp -c, vLLM --max-model-len) — VulnFanatic-NG begrenzt
dann automatisch den gesendeten Kontext, sodass Prompt + Antwort passen. Halten Sie
außerdem vulnfanatic.maxResponseTokens in einem vernünftigen Bereich (≈8192, nicht
65535) und/oder erhöhen Sie das Fenster des Servers. Sehr kleine Fenster (≤8k) können
den vollständigen interprozeduralen Kontext nicht aufnehmen; verwenden Sie ein
Modell/einen Server, das/der für 32k+ konfiguriert ist.IncompleteRead / Could not complete request ... after N attempt(s) — der
Server hat die Anfrage akzeptiert, aber die Verbindung geschlossen, bevor er die
vollständige Antwort gesendet hat. Das bedeutet fast immer, dass der Modell-Server
mitten in der Generierung abgestürzt oder hängengeblieben ist: Speichermangel (großer
Kontext + lange Ausgabe), ein interner/Worker-Timeout oder ein Proxy, der die
Verbindung zurücksetzt. VulnFanatic-NG versucht es einmal automatisch erneut und
überspringt dann diese Fundstelle. Prüfen Sie die eigenen Logs des Modell-Servers
auf die eigentliche Ursache; die Reduzierung von vulnfanatic.maxContextTokens
und/oder vulnfanatic.maxResponseTokens oder mehr Speicher / ein größeres
Kontextfenster für den Server behebt das Problem in der Regel.
vulnfanatic.phase2ForceEnable, um
auch namensbasierte Übereinstimmungen zu prüfen, oder
vulnfanatic.phase2RequireSymbols=off. Das Symbol-Gate ist eine Heuristik.response_format=json_object ab; der
Client toleriert das und extrahiert das JSON trotzdem. Deaktivieren Sie
vulnfanatic.sendJsonResponseFormat, wenn Ihr Server den Parameter vollständig
ablehnt.Apache-2.0 (© Martin Petran) — siehe plugin.json.
switchMAIN→ABCD→strcpy auch die Funktionen, die MAIN und ABCD sonst noch aufrufen), da
sie die Grenz-/Validierungsprüfungen enthalten können, die den gefährlichen Wert absichern
(vulnfanatic.includeCallPathSiblings, solange das Budget reicht), undrecv/read/getenv, die
in derselben Funktion aufgerufen werden).mediumhighvulnfanatic.skipConstantArgCalls (Standard aus) — Überschlagsklassen-Aufrufstellen
überspringen, deren Argumente alle Compile-Zeit-Konstanten sind.| Einstellung | Bedeutung |
|---|
vulnfanatic.apiProvider | Welches LLM-Backend aufgerufen werden soll: openai (Standard), anthropic, google oder azure. Siehe LLM-Backends unten. Alle Anbieter werden über die Python-Standardbibliothek angesprochen — es ist nichts zu pip install erforderlich. |
vulnfanatic.apiBaseUrl | Endpoint-Basis für den ausgewählten Anbieter (siehe Tabelle unten). Standard http://localhost:8080/v1. Setzen Sie ihn auf den Literalwert TEST, um den Testmodus zu aktivieren (siehe unten). |
vulnfanatic.apiKey | API-Schlüssel / Bearer-Token. Kann bei lokalen Servern leer bleiben. Wird durch die Umgebungsvariablen VULNFANATIC_API_KEY oder OPENAI_API_KEY überschrieben. |
vulnfanatic.model | Erforderlich (außer im Testmodus). Die Modellkennung (für azure den Deployment-Namen). |
vulnfanatic.apiMode | Nur openai: chat (Standard, /chat/completions) vs. completions (ein einzelner, flacher Prompt — für Basis-/Instruct-Modelle, die ohne Chat-Vorlage bereitgestellt werden). |
vulnfanatic.azureApiVersion | Nur azure: der api-version-Abfrageparameter (Standard 2024-10-21). |
| Provider | apiBaseUrl | Authentifizierung | Anmerkungen |
|---|
openai | Ihr Server, z. B. http://localhost:8080/v1 | Authorization: Bearer | OpenAI-kompatible Chat/Completions: lokales llama.cpp / ollama / vLLM, OpenAI und der OpenAI-kompatible Endpoint von AWS Bedrock. |
anthropic | leer → https://api.anthropic.com | x-api-key + anthropic-version | Claude Messages API (POST <base>/v1/messages). temperature wird nicht gesendet (aktuelle Claude-Modelle lehnen es ab). |
google | leer → https://generativelanguage.googleapis.com | API-Schlüssel in der URL | Gemini generateContent (<base>/v1beta/models/<model>:generateContent). |
azure | https://<resource>.openai.azure.com | api-key-Header | Azure OpenAI; setzen Sie model auf den Deployment-Namen und azureApiVersion auf Ihre API-Version. |
vulnfanatic.callPathMaxPathsvulnfanatic.callPathIncludeBodiesvulnfanatic.callPathMaxBodiesvulnfanatic.includeCallPathSiblingsvulnfanatic.callPathSiblingMaxBodiesvulnfanatic.includeDataTypesvulnfanatic.maxTypeDefsvulnfanatic.includeVariableDataflowvulnfanatic.dataflowMaxFunctionsvulnfanatic.includeStackLayoutvulnfanatic.scanIndirectCallsvulnfanatic.validationPassvulnfanatic.validatorModelvulnfanatic.validatorProvidervulnfanatic.validatorBaseUrlvulnfanatic.validatorApiKeyvulnfanatic.minConfidencelowmediumhighlowvulnfanatic.flagUnparseableResponsesUNKNOWNvulnfanatic.skipConstantArgCallsvulnfanatic.verdictReasoningconcisefullnoneconcisevulnfanatic.runPhase1vulnfanatic.runPhase2vulnfanatic.runPhase3vulnfanatic.phase2RequireSymbolsvulnfanatic.phase2ForceEnablevulnfanatic.tokenizerEncodingvulnfanatic.offlineBuildContextvulnfanatic.debugLoggingvulnfanatic.debugAnonymousvulnfanatic.sendJsonResponseFormatvulnfanatic.tlsVerifyvulnfanatic.caBundlePathCERTIFICATE_VERIFY_FAILEDvulnfanatic.rulesPhase1Pathvulnfanatic.rulesPhase2Pathvulnfanatic.rulesPhase3Path