
Decompila il bytecode V8 serializzato (file JSC) in codice JavaScript di alto livello leggibile e simile al codice originale, con supporto per più versioni di V8, output ad albero e denominazione deterministica delle funzioni per l'analisi di malware.
View8 è uno strumento di analisi statica progettato per decompilare oggetti bytecode V8 serializzati (file JSC) in codice leggibile ad alto livello. Per analizzare e disassemblare questi oggetti serializzati, View8 utilizza un binario V8 compilato e modificato. Di conseguenza, View8 produce un output testuale simile a JavaScript.
9.4.146.24 (usata in Node V16.x)10.2.154.26 (usata in Node V18.x)11.3.244.8 (usata in Node V20.x)Per le versioni compilate, visita la pagina delle release.
--inp, : Il nome del file di input.-i--out, -o: Percorso dell'output. A seconda delle opzioni selezionate, l'output può essere un singolo file o una struttura di directory.--input_format, -f: Specifica il formato di input. Le opzioni sono:
raw: l'input è un file JSC grezzo.disassembled: il file di input è già disassemblato.serialized: l'input è già decompilato e memorizzato in un formato serializzato. L'attuale formato serializzato è Python pickle; utilizzare solo input attendibili.--export_format, -e: Specifica i formati di esportazione. Le opzioni sono v8_opcode, translated, decompiled e serialized. È possibile combinare più opzioni. Predefinito: decompiled.--path, -p: Percorso del binario del disassembler. Richiesto se l'input è in formato grezzo e View8 non riesce a individuare automaticamente il disassembler corrispondente.--scope: Propaga gli argomenti di ambito. Predefinito: 1.--normalize: Sostituisce gli identificatori di funzione derivati dagli indirizzi con nomi deterministici basati sull'ordine di analisi.--normalize-map [CSV]: Durante l'uso di --normalize, scrive un file CSV che mappa ogni nome di funzione originale al suo nome normalizzato. Quando il percorso viene omesso, View8 deriva <output>.name_map.csv da --out, oppure da --inp quando non è impostato alcun percorso di output.--tree, -t: Divide l'output in una struttura ad albero invece di memorizzare tutte le funzioni in un unico file. Specifica la funzione che verrà utilizzata come radice dell'albero. Per iniziare dalla funzione principale predefinita, usa start.--split_mode: Modalità di divisione dell'albero. Le opzioni sono declarers, calls e references. Predefinito: declarers.--inline_depth, -d: Nelle modalità calls e references, include nel file principale dell'albero le funzioni raggiungibili dalla radice selezionata fino alla profondità N. La profondità 0 significa solo la radice selezionata; la profondità 1 include i chiamati/riferimenti diretti; la profondità 2 include i loro figli. Non utilizzato con declarers.--inline_branch_limit, -l: In modalità albero, incorpora nel file principale dell'albero i rami figlio completi con al massimo N funzioni quando i rami figlio sono inclusi da --inline_depth. I rami più grandi vengono salvati separatamente.--split_depth: Nelle modalità calls e references, limita la profondità di attraversamento dei rami di utilizzo esportati. Predefinito: 4.--include, -n: File contenente le funzioni da includere nell'output.--exclude, -x: File contenente le funzioni da escludere dall'output.--func: Visualizza una funzione selezionata.--show_all: Nella modalità di visualizzazione delle funzioni, mostra anche le righe contrassegnate come nascoste.--verbosity, -v: Livello di dettaglio. Intervallo accettato: 0 a 3.Per decompilare un file bytecode V8 ed esportare il codice decompilato:
python view8.py -i input_file -o output_filePer impostazione predefinita, View8 rileva la versione del bytecode V8 del file di input utilizzando VersionDetector.exe e cerca automaticamente un binario del disassembler compatibile nella cartella Bin. Questo può essere modificato specificando un binario del disassembler diverso con l'opzione --path o -p:
python view8.py -i input_file -o output_file --path /percorso/del/disassemblerPer saltare il processo di disassemblaggio e fornire un file già disassemblato come input, usa l'opzione --input_format disassembled o -f disassembled:
python view8.py -i input_file -o output_file -f disassembledUsa --normalize per sostituire i nomi di funzione derivati dagli indirizzi con identificatori deterministici. Per preservare la relazione tra i nomi originali e quelli normalizzati, aggiungi --normalize-map:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map \
--out decompiled/sample.dec.txt \
--export_format decompiled serializedQuesto scrive decompiled/sample.dec.name_map.csv con le seguenti colonne:
original_name,normalized_name
func_start_0x268514e9dcd9,func_start_0x100000000
func_rne_0x268514eb0779,func_rne_0x100000001È possibile fornire anche un percorso CSV esplicito:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map mappings/sample.names.csv \
--out decompiled/sample.dec.txtA volte è utile decompilare il file in un formato serializzato che preservi gli oggetti e le strutture analizzati. Questo tipo di output può essere più facile da post-elaborare rispetto a un formato testuale, ad esempio durante un'ulteriore deoffuscazione. Per creare un output serializzato, usa il formato di esportazione serialized:
python view8.py -i input_file -o output_file -e serializedAvviso di sicurezza: l'attuale formato serializzato è un file Python pickle (.pkl). L'unpickling di dati provenienti da fonti non attendibili può eseguire codice arbitrario. Carica solo file serializzati che hai generato tu stesso.
Per ricaricare un output serializzato ed esportarlo in un altro formato, usa --input_format serialized o -f serialized:
python view8.py -i input_file -o output_file -f serializedSpecifica i formati di esportazione usando l'opzione --export_format o -e. Puoi combinare più formati:
v8_opcodetranslateddecompiledserializedAd esempio, per esportare sia gli opcode V8 che il codice decompilato affiancati:
python view8.py -i input_file -o output_file -e v8_opcode decompiledPer impostazione predefinita, il formato utilizzato è decompiled.
Per payload bundle di grandi dimensioni, scrivere tutte le funzioni decompilate in un unico file può essere difficile da analizzare. L'output ad albero divide la radice selezionata e le funzioni correlate in una struttura di directory.
Usa --tree per selezionare la radice dell'albero:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_out/ \
--tree startIl valore speciale start indica la funzione principale predefinita recuperata da View8.
L'albero può essere diviso utilizzando una delle tre modalità:
declarers: segue le relazioni di dichiarazione lessicale, ovvero quali funzioni sono dichiarate all'interno o sotto un'altra funzione. Questo è utile per comprendere la struttura bundle o simile a moduli, ma non rappresenta il flusso di esecuzione.calls: segue le chiamate di funzione dirette, come func_x(...). Questo è utile per recuperare lo scheletro di esecuzione da una radice selezionata.references: segue tutti i riferimenti di funzione visibili, non solo le chiamate dirette. Questo include callback, handler esportati, route handler, proprietà di oggetti e altre funzioni assegnate. Questa modalità è utile per scoprire le superfici di capacità, ma può produrre alberi molto più grandi rispetto a calls.Esempio:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode callsIl file principale dell'albero contiene sempre la funzione radice selezionata.
Nelle modalità calls e references, --inline_depth controlla quanti livelli del grafo sono inclusi nel file principale:
--inline_depth 0 -> solo radice
--inline_depth 1 -> radice + chiamati/riferimenti diretti
--inline_depth 2 -> radice + chiamati/riferimenti diretti + i loro figliAd esempio, il seguente comando crea una panoramica di esecuzione compatta contenente la radice e i suoi chiamati diretti:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1Per includere un ulteriore livello di chiamate:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2--inline_depth è supportato solo con le modalità calls e references. Non viene utilizzato con declarers.
I rami figlio di grandi dimensioni vengono salvati in file separati. Nelle modalità calls e references, --split_depth controlla la profondità di attraversamento dei rami di utilizzo esportati:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5L'opzione --inline_branch_limit controlla se i piccoli rami figlio completi vengono inclusi anche nel file principale:
--inline_branch_limit 3Ciò significa che i rami figlio completi contenenti al massimo 3 funzioni possono essere incorporati nel file principale. I rami più grandi vengono salvati separatamente.
Nelle modalità calls e references, i rami figlio vengono incorporati solo quando il --inline_depth selezionato include già le funzioni figlio. Ad esempio, --inline_depth 0 significa solo radice, quindi i rami figlio non vengono incorporati anche se sono piccoli.
Per una panoramica di esecuzione compatta:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5Per una panoramica architetturale più ampia:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2 \
--split_depth 5Per callback esportati, handler, route e superfici di capacità:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_refs/ \
--tree start \
--split_mode references \
--inline_depth 1 \
--split_depth 3Per struttura lessicale o simile a moduli:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_declarers/ \
--tree start \
--split_mode declarers \
--split_depth 3Per visualizzare una funzione selezionata, usa --func:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func nome_funzionePer mostrare anche le righe contrassegnate come nascoste:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func nome_funzione \
--show_allLa versione del bytecode V8 è memorizzata come hash all'inizio del file. Di seguito sono riportate le opzioni disponibili per VersionDetector.exe:
-h: Recupera una versione e restituisce il suo hash.-d: Recupera un hash in formato little-endian e restituisce la versione corrispondente tramite forza bruta.-f: Recupera un file e restituisce la sua versione.