
Dekompiliert serialisierten V8-Bytecode (JSC-Dateien) in hochwertigen, lesbaren JavaScript-ähnlichen Code, mit Unterstützung für mehrere V8-Versionen, Baumausgabe und deterministischer Funktionsbenennung für die Malware-Analyse.
View8 ist ein statisches Analysetool, das dazu entwickelt wurde, serialisierte V8-Bytecode-Objekte (JSC-Dateien) in hochwertigen, lesbaren Code zu dekompilieren. Um diese serialisierten Objekte zu parsen und zu disassemblieren, verwendet View8 eine gepatchte, kompilierte V8-Binärdatei. Dadurch erzeugt View8 eine textuelle Ausgabe, die JavaScript ähnelt.
9.4.146.24 (verwendet in Node V16.x)10.2.154.26 (verwendet in Node V18.x)11.3.244.8 (verwendet in Node V20.x)Für kompilierte Versionen besuchen Sie die Releases-Seite.
--inp, : Der Name der Eingabedatei.-i--out, -o: Pfad zur Ausgabe. Abhängig von den gewählten Optionen kann die Ausgabe eine einzelne Datei oder eine Verzeichnisstruktur sein.--input_format, -f: Gibt das Eingabeformat an. Optionen sind:
raw: Die Eingabe ist eine rohe JSC-Datei.disassembled: Die Eingabedatei ist bereits disassembliert.serialized: Die Eingabe ist bereits dekompiliert und in einem serialisierten Format gespeichert. Das aktuelle serialisierte Format ist Python pickle; verwenden Sie nur vertrauenswürdige Eingaben.--export_format, -e: Gibt das/die Exportformat(e) an. Optionen sind v8_opcode, translated, decompiled und serialized. Mehrere Optionen können kombiniert werden. Standard: decompiled.--path, -p: Pfad zur Disassembler-Binärdatei. Erforderlich, wenn die Eingabe im Raw-Format vorliegt und View8 den passenden Disassembler nicht automatisch finden kann.--scope: Bereichsargumente propagieren. Standard: 1.--normalize: Ersetzt adressbasierte Funktionsbezeichner durch deterministische Namen, die auf der Parse-Reihenfolge basieren.--normalize-map [CSV]: Schreibt bei Verwendung von --normalize eine CSV-Zuordnung von jedem ursprünglichen Funktionsnamen zu seinem normalisierten Namen. Wenn der Pfad weggelassen wird, leitet View8 <output>.name_map.csv aus --out ab, oder aus --inp, wenn kein Ausgabepfad festgelegt ist.--tree, -t: Teilt die Ausgabe in eine Baumstruktur auf, anstatt alle Funktionen in einer Datei zu speichern. Geben Sie die Funktion an, die als Baumwurzel verwendet werden soll. Um mit der standardmäßigen Hauptfunktion zu beginnen, verwenden Sie start.--split_mode: Baum-Aufteilungsmodus. Optionen sind declarers, calls und references. Standard: declarers.--inline_depth, -d: In den Modi calls und references werden Funktionen, die von der gewählten Baumwurzel bis zur Tiefe N erreichbar sind, in die Hauptbaumdatei aufgenommen. Tiefe 0 bedeutet nur die gewählte Wurzel; Tiefe 1 umfasst direkte Aufgerufene/Referenzen; Tiefe 2 umfasst deren Kinder. Wird nicht mit declarers verwendet.--inline_branch_limit, -l: Im Baummodus werden vollständige untergeordnete Zweige mit höchstens N Funktionen in die Hauptbaumdatei eingefügt, wenn untergeordnete Zweige durch --inline_depth eingeschlossen werden. Größere Zweige werden separat gespeichert.--split_depth: In den Modi calls und references wird begrenzt, wie tief exportierte Nutzungszweige durchlaufen werden. Standard: 4.--include, -n: Datei, die Funktionen enthält, die in die Ausgabe aufgenommen werden sollen.--exclude, -x: Datei, die Funktionen enthält, die von der Ausgabe ausgeschlossen werden sollen.--func: Zeigt eine ausgewählte Funktion an.--show_all: Zeigt im Funktionsanzeigemodus auch Zeilen an, die als verborgen markiert sind.--verbosity, -v: Ausführlichkeitsgrad. Akzeptierter Bereich: 0 bis 3.Um eine V8-Bytecode-Datei zu dekompilieren und den dekompilierten Code zu exportieren:
python view8.py -i input_file -o output_fileStandardmäßig erkennt View8 die V8-Bytecode-Version der Eingabedatei mit VersionDetector.exe und sucht automatisch nach einer kompatiblen Disassembler-Binärdatei im Ordner Bin. Dies kann geändert werden, indem Sie mit der Option --path oder -p eine andere Disassembler-Binärdatei angeben:
python view8.py -i input_file -o output_file --path /path/to/disassemblerUm den Disassemblierungsprozess zu überspringen und eine bereits disassemblierte Datei als Eingabe bereitzustellen, verwenden Sie die Option --input_format disassembled oder -f disassembled:
python view8.py -i input_file -o output_file -f disassembledVerwenden Sie --normalize, um adressbasierte Funktionsnamen durch deterministische Bezeichner zu ersetzen. Um die Beziehung zwischen den ursprünglichen und normalisierten Namen zu bewahren, fügen Sie --normalize-map hinzu:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map \
--out decompiled/sample.dec.txt \
--export_format decompiled serializedDies schreibt decompiled/sample.dec.name_map.csv mit den folgenden Spalten:
original_name,normalized_name
func_start_0x268514e9dcd9,func_start_0x100000000
func_rne_0x268514eb0779,func_rne_0x100000001Ein expliziter CSV-Pfad kann ebenfalls angegeben werden:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map mappings/sample.names.csv \
--out decompiled/sample.dec.txtManchmal ist es nützlich, die Datei in ein serialisiertes Format zu dekompilieren, das die geparsten Objekte und Strukturen bewahrt. Diese Art der Ausgabe kann einfacher nachzuverarbeiten sein als ein Textformat, beispielsweise während einer weiteren Entschleierung. Um eine serialisierte Ausgabe zu erstellen, verwenden Sie das Exportformat serialized:
python view8.py -i input_file -o output_file -e serializedSicherheitswarnung: Das aktuelle serialisierte Format ist eine Python-pickle-Datei (.pkl). Das Entpacken von Daten aus nicht vertrauenswürdigen Quellen kann beliebigen Code ausführen. Laden Sie nur serialisierte Dateien, die Sie selbst erstellt haben.
Um eine serialisierte Ausgabe zurückzuladen und in einem anderen Format zu exportieren, verwenden Sie --input_format serialized oder -f serialized:
python view8.py -i input_file -o output_file -f serializedGeben Sie das/die Exportformat(e) mit der Option --export_format oder -e an. Sie können mehrere Formate kombinieren:
v8_opcodetranslateddecompiledserializedUm beispielsweise sowohl V8-Opcodes als auch dekompilierten Code nebeneinander zu exportieren:
python view8.py -i input_file -o output_file -e v8_opcode decompiledStandardmäßig wird das Format decompiled verwendet.
Bei großen gebündelten Payloads kann das Schreiben aller dekompilierten Funktionen in eine einzige Datei die Analyse erschweren. Die Baumausgabe teilt die gewählte Wurzel und verwandte Funktionen in eine Verzeichnisstruktur auf.
Verwenden Sie --tree, um die Baumwurzel auszuwählen:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_out/ \
--tree startDer spezielle Wert start bedeutet die standardmäßige Hauptfunktion, die von View8 wiederhergestellt wurde.
Der Baum kann mit einem von drei Modi aufgeteilt werden:
declarers: Folgt lexikalischen Deklarationsbeziehungen, d. h. welche Funktionen innerhalb oder unter einer anderen Funktion deklariert sind. Dies ist nützlich, um gebündelte oder modulartige Strukturen zu verstehen, stellt jedoch keinen Ausführungsfluss dar.calls: Folgt direkten Funktionsaufrufen, wie z. B. func_x(...). Dies ist nützlich, um das Ausführungsgerüst aus einer gewählten Wurzel wiederherzustellen.references: Folgt allen sichtbaren Funktionsreferenzen, nicht nur direkten Aufrufen. Dies umfasst Callbacks, exportierte Handler, Routen-Handler, Objekteigenschaften und andere zugewiesene Funktionen. Dieser Modus ist nützlich, um Fähigkeitsoberflächen zu entdecken, kann aber viel größere Bäume erzeugen als calls.Beispiel:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode callsDie Hauptbaumdatei enthält immer die gewählte Wurzelfunktion.
In den Modi calls und references steuert --inline_depth, wie viele Graphebenen in die Hauptdatei aufgenommen werden:
--inline_depth 0 -> nur Wurzel
--inline_depth 1 -> Wurzel + direkte Aufgerufene/Referenzen
--inline_depth 2 -> Wurzel + direkte Aufgerufene/Referenzen + deren KinderDer folgende Befehl erstellt beispielsweise eine kompakte Ausführungsübersicht, die die Wurzel und ihre direkten Aufgerufenen enthält:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1Um eine zusätzliche Aufrufebene einzuschließen:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2--inline_depth wird nur mit den Modi calls und references unterstützt. Es wird nicht mit declarers verwendet.
Große untergeordnete Zweige werden in separate Dateien gespeichert. In den Modi calls und references steuert --split_depth, wie tief exportierte Nutzungszweige durchlaufen werden:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5Die Option --inline_branch_limit steuert, ob kleine vollständige untergeordnete Zweige ebenfalls in die Hauptdatei aufgenommen werden:
--inline_branch_limit 3Dies bedeutet, dass vollständige untergeordnete Zweige mit höchstens 3 Funktionen in die Hauptdatei eingefügt werden können. Größere Zweige werden separat gespeichert.
In den Modi calls und references werden untergeordnete Zweige nur eingefügt, wenn das gewählte --inline_depth bereits untergeordnete Funktionen einschließt. Beispielsweise bedeutet --inline_depth 0 nur Wurzel, sodass untergeordnete Zweige nicht eingefügt werden, selbst wenn sie klein sind.
Für eine kompakte Ausführungsübersicht:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5Für eine breitere architektonische Übersicht:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2 \
--split_depth 5Für exportierte Callbacks, Handler, Routen und Fähigkeitsoberflächen:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_refs/ \
--tree start \
--split_mode references \
--inline_depth 1 \
--split_depth 3Für lexikalische oder modulartige Strukturen:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_declarers/ \
--tree start \
--split_mode declarers \
--split_depth 3Um eine ausgewählte Funktion anzuzeigen, verwenden Sie --func:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_nameUm auch Zeilen anzuzeigen, die als verborgen markiert sind:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_name \
--show_allDie V8-Bytecode-Version wird als Hash am Anfang der Datei gespeichert. Im Folgenden sind die Optionen für VersionDetector.exe aufgeführt:
-h: Ruft eine Version ab und gibt deren Hash zurück.-d: Ruft einen Hash im Little-Endian-Format ab und gibt die entsprechende Version per Brute-Force zurück.-f: Ruft eine Datei ab und gibt deren Version zurück.