
Statisches Deobfuskierungs-Toolkit für kompilierten V8-JavaScript-Bytecode, mit Schwerpunkt auf JSCeal-Payloads. Bietet musterbasierte Filter, Entflachung des Kontrollflusses, String-Rekonstruktion und optionale LLM-gestützte Funktionsumbenennung für die Analyse.
Dieses Tool dient der statischen Deobfuskierung kompilierter V8-JavaScript-Bytecodes, die mit javascript-obfuscator geschützt wurden.
Es arbeitet mit Pseudocode, der von View8 erzeugt wird, und nicht mit dem ursprünglichen JavaScript-Quellcode. Das Projekt wurde an JSCeal-Payloads entwickelt und getestet.
Die Filter sind musterorientiert und in erster Linie als Forschungswerkzeug und Referenzimplementierung gedacht. Das Tool ist kein universeller JavaScript-Deobfuskator, rekonstruiert nicht den ursprünglichen Quellcode und erzeugt kein ausführbares JavaScript. Seine Ausgabe bleibt View8-Pseudocode, der für statische Inspektion, Suche, Vergleich und den Export von Funktionsbäumen gedacht ist.
pickle. Das Laden einer bösartigen oder nicht vertrauenswürdigen .pkl-Datei kann Code ausführen. Laden Sie nur serialisierte Dateien, die Sie lokal mit View8 erzeugt haben.requirements.txt;brotli für den Linux-Stapel-Entpackungs-Workflow;Erstellen Sie eine isolierte Python-Umgebung:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
Das OpenAI-Backend in deobf_ai.py erfordert zusätzlich das OpenAI-Python-Paket:
python3 -m pip install openai
Das Anthropic-Backend verwendet die HTTP-API über requests. Das Ollama-Backend erwartet einen erreichbaren Ollama-Server.
Der ursprüngliche JSCeal-app.jsc-Payload ist Brotli-komprimiert. Unter Linux kann er mit dem Dienstprogramm brotli dekomprimiert werden:
brotli -d app.jsc -o app.decompressed.jsc
Der Stapel-Workflow unter scripts/ führt diesen Schritt mit scripts/unpack_all.sh aus.
Unter Windows oder wenn das Befehlszeilenprogramm brotli nicht verfügbar ist, kann der enthaltene Node.js-Helfer als Fallback verwendet werden. Er dekomprimiert nur die Eingabe und führt sie nicht aus:
node Utils/decompress-jsc.js app.jsc
Er schreibt:
app.jsc.decompressed.jsc
Der V8-Code-Cache ist versionsspezifisch. Verwenden Sie einen Disassembler, der für dieselbe V8-Version wie der Payload erstellt wurde.
Die während der Entwicklung verwendeten JSCeal-Beispiele basierten auf V8 10.2.154.26. Standard-Disassembler aus einem nicht verwandten V8-Build funktionieren nicht korrekt.
Der Quellbaum enthält den Disassembler-Quellcode und die erforderlichen V8-Patches unter:
Utils/disasm/v8dasm.cpp
Utils/disasm/patches/
Ein vorgefertigtes Linux-Binary wird mit der Projektversion verteilt, während der Quellbaum den Quellcode und die Patches enthält, die für einen Neubau erforderlich sind. Eine detaillierte Beschreibung finden Sie im Projekt-Wiki. Nachdem Sie das passende v8dasm erhalten oder erstellt haben, führen Sie Folgendes aus:
/path/to/v8dasm app.decompressed.jsc > app.jsc.disasm.txt
Führen Sie die disassemblierte Datei view8.py zu und erzeugen Sie sowohl serialisierte Ausgabe für die weitere Verarbeitung als auch menschenlesbaren Pseudocode:
mkdir -p decompiled
python3 View8/view8.py \
--input_format disassembled \
--inp app.jsc.disasm.txt \
--normalize \
--out decompiled/app.dec.txt \
--export_format decompiled serialized
Dies erzeugt:
decompiled/app.dec.txt
decompiled/app.dec.pkl
Die Option --normalize macht generierte Funktionskennungen über wiederholte Disassemblierungs- und Dekompilierungsläufe hinweg reproduzierbar.
Es gibt separate Filter für die einzelnen Obfuskierungsebenen. Sie können zusammen auf die serialisierte View8-Ausgabe mit deobf_all.py angewendet werden:
mkdir -p deobfuscated
python3 deobf_all.py \
--inp decompiled/app.dec.pkl \
--out deobfuscated/app.deobf.txt \
--export_format decompiled serialized
Der Standard-Zeichenkettenfilter ist Variante 2, die von der Mehrheit der analysierten JSCeal-Payloads verwendet wird. Um das einfachere Zeichenkettenschema explizit auszuwählen, fügen Sie hinzu:
--str_deobf 1
Typische Ausgaben sind:
deobfuscated/app.deobf.txt
deobfuscated/app.deobf.pkl
deobfuscated/app.deobf.txt.strings.txt
decompiled/app.dec.resolved_funcs.csv
Die CSV-Datei der aufgelösten Funktionen ist ein beispielspezifischer Cache. Wenn sie fehlt, stellt der Zeichenketten-Pass die erforderliche Decoder-Konfiguration wieder her, schreibt die CSV und fährt im selben Lauf mit der Deobfuskierung der Zeichenketten fort. Spätere Läufe verwenden den Cache erneut und sind normalerweise schneller.
Verwenden Sie eine CSV-Datei aufgelöster Funktionen nicht mit einem anderen dekompilierten Payload wieder.
Nachdem alle strukturellen Deobfuskierungsfilter angewendet wurden, kann deobf_ai.py Namen vorschlagen, die das Funktionsverhalten beschreiben. Es unterstützt Anthropic-, OpenAI- und Ollama-Backends.
Übergeben Sie das Modell explizit, damit Läufe reproduzierbar bleiben.
export ANTHROPIC_API_KEY='...'
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend anthropic \
--model '<model-id>' \
--export_format decompiled serialized
export OPENAI_API_KEY='...'
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend openai \
--model '<model-id>' \
--export_format decompiled serialized
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend ollama \
--model '<local-model>' \
--ollama_url http://localhost:11434 \
--export_format decompiled serialized
Im Standardmodus erstellt der Umbenenner einen Direktaufruf-Baum, der von der Einstiegsfunktion ausgeht, und benennt nur Funktionen um, die über Aufrufe erreicht werden. Fügen Sie --greedy hinzu, um alle sichtbaren Funktionsreferenzen einzuschließen, einschließlich Callbacks und zugewiesener Handler.
Die erzeugte zweispaltige CSV dient als Cache und ermöglicht die Fortsetzung eines unterbrochenen Laufs. Wählen Sie einen vorhandenen Cache explizit mit --csv aus:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--csv deobfuscated/app.deobf.renamed_funcs.greedy.example-model.csv \
--llm_backend anthropic \
--model '<model-id>' \
--greedy \
--export_format decompiled serialized
Im normalen Modus wird die CSV als potenziell partieller Cache behandelt. Zwischengespeicherte Namen werden zuerst angewendet, Funktionen, die bereits vom Cache abgedeckt sind, werden aus dem ausgewählten Aufruf- oder Referenzbaum entfernt, und das LLM wird nur für Funktionen aufgerufen, die ungelöst bleiben. Wenn die CSV diesen Baum vollständig abdeckt, ist kein API-Schlüssel oder keine LLM-Verbindung erforderlich. Wenn sie nur einen Teil des Baums abdeckt, wird das ausgewählte Backend initialisiert und die neu generierten Zuordnungen werden an dieselbe CSV angehängt.
Verwenden Sie denselben Baummodus, der beim Erstellen der CSV verwendet wurde. Eine CSV, die aus einem --greedy-Lauf erzeugt wurde, benötigt normalerweise erneut --greedy, wenn das Ziel darin besteht, diesen Lauf fortzusetzen, anstatt nur die Direktaufruf-Teilmenge wiederzuverwenden.
Verwenden Sie --apply-csv-only, wenn die CSV bereits die Beschriftungen enthält, die Sie anwenden möchten, einschließlich überprüfter, bearbeiteter, importierter oder neu basierter Zuordnungen:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--csv renamed_functions.normalized.csv \
--apply-csv-only \
--export_format decompiled serialized
Dieser Modus:
--csv;--func kombiniert werden.Zeilen, deren ursprüngliche Funktionskennung in der Eingabe nicht vorhanden ist, werden ignoriert. Der Befehl schlägt fehl, wenn die CSV keine Zuordnungen enthält, die auf die geladene Datei anwendbar sind.
Verwenden Sie --func mit der exakten vollständigen Funktionskennung, um eine fokussierte semantische Analyse einer deobfuskierten Funktion anzufordern:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--func func_example_0x100001234 \
--llm_backend anthropic \
--model '<model-id>'
Die Analyse umfasst einen vorgeschlagenen Namen, eine Verhaltenszusammenfassung, Eingaben und Rückgabewert, Nebenwirkungen, schrittweise Logik, bereinigten Pseudocode, unterstützende Beweise und ungelöste Unsicherheiten. Die Angabe von --csv fügt zwischengespeicherte semantische Namen als Kontext für Referenzen innerhalb der ausgewählten Funktion hinzu, ohne das geladene Korpus zu ändern. Verwenden Sie --analysis-out analysis/function.md, um den Bericht als Markdown zu speichern. Fuzzy-Übereinstimmungen werden nur als Vorschläge ausgegeben; die angeforderte Funktionskennung muss exakt übereinstimmen.
Verwenden Sie --help für Optionen zur Steuerung von Temperatur, Batch-Verarbeitung, Anthropic-Denkmodus, Token-Limits und benutzerdefinierten CSV-Pfaden.
LLM-generierte Namen sind Navigationshilfen, keine Beweise. Überprüfen Sie sie immer gegen den deobfuskierten Rumpf.
Deobfuskiertes JSCeal-Output ist normalerweise sehr groß. Laden Sie die serialisierte Ausgabe zurück in View8 und teilen Sie sie in kleinere Funktionsbäume auf.
Fügen Sie in dieser Phase --scope 0 hinzu. Die Bereichsausbreitung wurde bereits vom Deobfuskator durchgeführt, und eine Wiederholung kann Werte falsch ausbreiten.
Ein Baum basierend auf Deklarationsbeziehungen:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/declarers \
--export_format decompiled \
--tree start \
--scope 0
Eine kompakte Direktaufruf-Übersicht:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/calls \
--export_format decompiled \
--tree start \
--scope 0 \
--split_mode calls \
--inline_depth 1 \
--split_depth 5
Ein breiterer Referenzbaum, einschließlich Callbacks und zugewiesener Handler:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/references \
--export_format decompiled \
--tree start \
--scope 0 \
--split_mode references \
--inline_depth 1 \
--split_depth 3
Verschiedene JSC-Dateien können unterschiedliche Zeichenketten-Obfuskierungsmodi verwenden.
Der einfachste beobachtete Modus verwendet Indexverschiebung und wird von deobf_str1.py behandelt. Der häufigste JSCeal-Modus verwendet Base64, RC4, Chunked Strings und transformierte Indizes; er wird von deobf_str2.py behandelt.
Die vollständige Pipeline wählt standardmäßig Variante 2. Die Filter können auch unabhängig zum Testen ausgeführt werden.
deobf_str2.pyVerwenden Sie --help, um alle verfügbaren Modi und Optionen anzuzeigen:
python3 deobf_str2.py --help
Ein direkter Zeichenketten-Deobfuskierungslauf kann gestartet werden mit:
python3 deobf_str2.py \
--inp decompiled/app.dec.pkl \
--out work/app.strings.txt \
--export_format decompiled serialized
Während des Laufs identifiziert das Skript die Zeichenketten-Decoder-Funktionen, lädt alle gültigen zwischengespeicherten Konfigurationen, löst fehlende auf, speichert die resultierende CSV und dekodiert die Zeichenketten. Ein zweiter Lauf ist nicht erforderlich.
Wenn deobf_str2.py direkt verwendet wird, lautet sein Standard-CSV-Name resolved_funcs.csv. Wählen Sie einen beispielspezifischen Pfad mit --csv oder -c:
python3 deobf_str2.py \
--inp decompiled/app.dec.pkl \
--out work/app.strings.txt \
--csv decompiled/app.dec.resolved_funcs.csv \
--export_format decompiled serialized \
--verbosity 1
Wenn Sie einzelne Filter verketten, bewahren Sie die serialisierte Ausgabe zwischen den Stufen auf, damit spätere Pässe weiterhin auf View8-Objekten arbeiten können.
deobf_all.py wendet die folgenden Stufen in dieser Reihenfolge an:
LLM-gestützte Funktionsumbenennung ist optional und wird separat nach der strukturellen Deobfuskierung ausgeführt.
Das Repository enthält einen vollständigen Hilfs-Workflow unter scripts/. Alle Skripte werden in einem Verzeichnis gehalten und beziehen dieselbe zentralisierte Konfiguration.
scripts/config.sh gemeinsame Tool- und Arbeitsbereichspfade
scripts/copy_payloads.sh JSCeal app.jsc-Dateien sammeln und MD5-benennen
scripts/unpack_all.sh Brotli-Dekomprimierung
scripts/disasm_all.sh Stapel-V8-Disassemblierung
scripts/decompile_all.sh Stapel-View8-Dekompilierung
scripts/deobfuscate_all.sh Stapel-Deobfuskierung mit kombiniertem Log
scripts/run_unattended.sh Getrennte Deobfuskierung und Validierung
scripts/collect_output.sh Decoder-Caches und Zeichenkettenlisten sammeln
Die mitgelieferte scripts/config.sh enthält Pfade aus einer Beispielumgebung:
JSC_DEOBF_ROOT="$HOME/jsc_deobfuscator"
V8DASM="$HOME/code/v8/v8dasm"
Bearbeiten Sie diese Datei einmal, um den Installationspfad, den passenden V8-Disassembler, Arbeitsbereichsverzeichnisse, externe Befehle, Log-Pfade und das Erntelayout zu konfigurieren. Der Arbeitsbereich standardmäßig auf das Verzeichnis, aus dem das Hilfsskript gestartet wird.
Jeder Wert kann auch über eine Umgebungsvariable überschrieben werden. JSC_HELPER_CONFIG kann eine andere Konfigurationsdatei auswählen.
Ein typischer Stapellauf ist:
scripts/copy_payloads.sh
scripts/unpack_all.sh
scripts/disasm_all.sh
scripts/decompile_all.sh
scripts/deobfuscate_all.sh
scripts/collect_output.sh
Die Skripte bewahren die für das JSCeal-Korpus verwendeten Konventionen, einschließlich der Behandlung entdeckter app.jsc-Dateien als Brotli-komprimierte Payloads und ihrer Benennung nach MD5. Lesen Sie scripts/README.md, bevor Sie den Workflow auf nicht verwandte Beispiele anwenden.
Für einen langen Stapel startet scripts/run_unattended.sh die Deobfuskierung mit nohup, schreibt Zeitstempel-Log-, PID- und Statusdateien und validiert jede erzeugte Ausgabe auf ungelöste Referenzen auf zwischengespeicherte Zeichenketten-Decoder-Funktionen:
scripts/run_unattended.sh
Ausgewählte Beispiele können explizit angegeben werden:
scripts/run_unattended.sh \
decompiled/sample1.dec.pkl \
decompiled/sample2.dec.pkl
View8/ View8-Dekompilierer und Funktionsbaum-Exporteur
Utils/decompress-jsc.js Brotli-Dekomprimierungs-Fallback für Windows
Utils/disasm/v8dasm.cpp V8-Disassembler-Quellcode
Utils/disasm/patches/ Vom Disassembler benötigte V8-Patches
Utils/check_unresolved_decoder_references.py
Ausgabe-Validierungshelfer
deobf_all.py Vollständige Standard-Deobfuskierungspipeline
deobf_str1.py Einfacher Zeichenketten-Indexverschiebungs-Filter
deobf_str2.py RC4/Base64-Zeichenkettenfilter mit Indexwiederherstellung
deobf_scope2.py Bereichs- und Wörterbuchausbreitung
deobf_unflattener.py Kontrollfluss-Entflachung
deobf_replace_ops.py Proxy- und Operations-Wrapper-Ersetzung
deobf_globals.py Globale Ausbreitung
deobf_inline_temporaries.py Konservative Endbereinigung
deobf_ai.py Optionale LLM-gestützte Funktionsumbenennung
scripts/ Konfigurierbare Stapel- und Validierungshelfer
Jeder Hauptpass kann separat zum Testen ausgeführt werden. Führen Sie das ausgewählte Skript mit --help aus, um seine vollständige Schnittstelle zu sehen:
python3 deobf_str1.py --help
python3 deobf_str2.py --help
python3 deobf_scope2.py --help
python3 deobf_unflattener.py --help
python3 deobf_replace_ops.py --help
python3 deobf_globals.py --help
python3 deobf_inline_temporaries.py --help
javascript-obfuscator-Ausgaben beobachtet wurden. Neue Varianten können zusätzliche Detektoren oder Transformationen erfordern.Die Pipeline wurde gegen das JSCeal-Korpus regressionstestet, das in der begleitenden Forschung verwendet wurde. Grundlegende Release-Prüfungen umfassen:
python3 -m compileall -q .
python3 deobf_all.py --help
python3 deobf_str2.py --help
python3 deobf_ai.py --help
python3 View8/view8.py --help
Überprüfen Sie für jedes Korpusbeispiel, dass der Lauf:
.pkl- als auch .txt-Ausgabe schreibt;Das unbeaufsichtigte Hilfsskript automatisiert die endgültige Decoder-Referenzvalidierung.
javascript-obfuscator generiert werden.Der für dieses Projekt erstellte JSC-Deobfuskator-Quellcode ist unter der
GNU General Public License, Version 2 oder (nach Ihrer Wahl) jeder späteren Version
(GPL-2.0-or-later) lizenziert. Siehe LICENSE für den vollständigen Lizenztext.
Copyright (C) 2026 Aleksandra "Hasherezade" Doniec @ Check Point Research.
Das View8-Untermodul ist ein separates Projekt. Von Dritten abgeleitetes Disassembler-Material
unter Utils/disasm/ behält seine vorhandene Herkunft bei und wird durch den obigen
Copyright-Hinweis nicht neu lizenziert.