
Toolkit di deobfuscazione statica per bytecode JavaScript V8 compilato, focalizzato sui payload JSCeal. Fornisce filtri basati su pattern, riappiattimento del flusso di controllo, ricostruzione delle stringhe e rinomina opzionale delle funzioni assistita da LLM per l'analisi.
Questo strumento è dedicato alla deoffuscazione statica del bytecode JavaScript V8 compilato che è stato protetto con javascript-obfuscator.
Opera su pseudocodice prodotto da View8, piuttosto che sul codice sorgente JavaScript originale. Il progetto è stato sviluppato e testato su payload JSCeal.
I filtri sono guidati da pattern e sono pensati principalmente come toolkit di ricerca e implementazione di riferimento. Lo strumento non è un deoffuscatore JavaScript generico, non ricostruisce il codice sorgente originale e non produce JavaScript eseguibile. Il suo output rimane pseudocodice View8 destinato a ispezione statica, ricerca, confronto ed esportazione dell'albero delle funzioni.
pickle di Python. Caricare un file .pkl dannoso o non attendibile può eseguire codice. Carica solo file serializzati che hai generato localmente con View8.requirements.txtbrotli per il flusso di lavoro batch di decompressione su Linux;Crea un ambiente Python isolato:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
Il backend OpenAI in deobf_ai.py richiede inoltre il pacchetto Python OpenAI:
python3 -m pip install openai
Il backend Anthropic usa l'API HTTP tramite requests. Il backend Ollama prevede un server Ollama raggiungibile.
Il payload JSCeal originale app.jsc è compresso con Brotli. Su Linux, può essere decompresso con l'utilità brotli:
brotli -d app.jsc -o app.decompressed.jsc
Il flusso di lavoro batch in scripts/ esegue questo passaggio con scripts/unpack_all.sh.
Su Windows, o quando l'utilità da riga di comando brotli non è disponibile, l'helper Node.js incluso può essere usato come fallback. Decomprime solo l'input e non lo esegue:
node Utils/decompress-jsc.js app.jsc
Scrive:
app.jsc.decompressed.jsc
La cache del codice V8 è specifica per versione. Usa un disassemblatore compilato per la stessa versione V8 del payload.
I campioni JSCeal usati durante lo sviluppo erano basati su V8 10.2.154.26. I disassemblatori predefiniti di una build V8 non correlata non funzioneranno correttamente.
L'albero del sorgente contiene il sorgente del disassemblatore e le patch V8 richieste in:
Utils/disasm/v8dasm.cpp
Utils/disasm/patches/
Un binario Linux precompilato è distribuito con la release del progetto, mentre l'albero del sorgente contiene il sorgente e le patch necessarie per ricompilarlo. Una descrizione dettagliata è disponibile nella Wiki del progetto. Dopo aver ottenuto o compilato il v8dasm corrispondente, esegui:
/path/to/v8dasm app.decompressed.jsc > app.jsc.disasm.txt
Fornisci il file disassemblato a view8.py e produci sia l'output serializzato per ulteriori elaborazioni sia lo pseudocodice leggibile dall'uomo:
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
Questo produce:
decompiled/app.dec.txt
decompiled/app.dec.pkl
L'opzione --normalize rende gli identificatori di funzione generati riproducibili tra esecuzioni ripetute di disassembly e decompilazione.
Esistono filtri separati per i singoli livelli di offuscamento. Possono essere applicati insieme all'output View8 serializzato con deobf_all.py:
mkdir -p deobfuscated
python3 deobf_all.py \
--inp decompiled/app.dec.pkl \
--out deobfuscated/app.deobf.txt \
--export_format decompiled serialized
Il filtro stringhe predefinito è la variante 2, usata dalla maggior parte dei payload JSCeal analizzati. Per selezionare esplicitamente lo schema stringhe più semplice, aggiungi:
--str_deobf 1
Gli output tipici sono:
deobfuscated/app.deobf.txt
deobfuscated/app.deobf.pkl
deobfuscated/app.deobf.txt.strings.txt
decompiled/app.dec.resolved_funcs.csv
Il CSV delle funzioni risolte è una cache specifica del campione. Quando è assente, la passata stringhe recupera la configurazione del decoder richiesta, scrive il CSV e continua a deoffuscare le stringhe nella stessa esecuzione. Le esecuzioni successive riusano la cache e sono normalmente più veloci.
Non riutilizzare un CSV di funzioni risolte con un payload decompilato diverso.
Dopo che tutti i filtri di deoffuscazione strutturale sono stati applicati, deobf_ai.py può proporre nomi che descrivono il comportamento delle funzioni. Supporta i backend Anthropic, OpenAI e Ollama.
Passa il modello esplicitamente affinché le esecuzioni rimangano riproducibili.
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
Nella modalità predefinita, il rinominatore costruisce un albero di chiamate dirette a partire dalla funzione di ingresso e rinomina solo le funzioni raggiunte tramite chiamate. Aggiungi --greedy per includere tutti i riferimenti di funzione visibili, inclusi callback e handler assegnati.
Il CSV a due colonne generato funge da cache e consente a un'esecuzione interrotta di continuare. Seleziona una cache esistente esplicitamente con --csv:
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
Nella modalità normale, il CSV è trattato come una cache potenzialmente parziale. I nomi in cache vengono applicati per primi, le funzioni già coperte dalla cache vengono rimosse dall'albero di chiamate o riferimenti selezionato e l'LLM viene invocato solo per le funzioni che rimangono non risolte. Se il CSV copre completamente quell'albero, non è richiesta alcuna chiave API o connessione LLM. Se copre solo parte dell'albero, il backend selezionato viene inizializzato e le nuove mappature generate vengono aggiunte allo stesso CSV.
Usa la stessa modalità ad albero usata quando il CSV è stato creato. Un CSV prodotto da un'esecuzione --greedy normalmente richiede di nuovo --greedy se l'obiettivo è continuare quell'esecuzione piuttosto che riutilizzare solo il sottoinsieme di chiamate dirette.
Usa --apply-csv-only quando il CSV contiene già le etichette che vuoi applicare, incluse mappature revisionate, modificate, importate o ribasate:
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
Questa modalità:
--csv esplicito;--func.Le righe il cui identificatore di funzione originale non esiste nell'input vengono ignorate. Il comando fallisce quando il CSV non contiene mappature applicabili al file caricato.
Usa --func con l'identificatore completo esatto della funzione per richiedere un'analisi semantica mirata di una funzione deoffuscata:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--func func_example_0x100001234 \
--llm_backend anthropic \
--model '<model-id>'
L'analisi include un nome proposto, un riepilogo del comportamento, input e valore di ritorno, effetti collaterali, logica passo-passo, pseudocodice ripulito, prove a supporto e incertezze non risolte. Fornire --csv aggiunge nomi semantici in cache come contesto per i riferimenti all'interno della funzione selezionata senza modificare il corpus caricato. Usa --analysis-out analysis/function.md per salvare il report come Markdown. Le corrispondenze fuzzy vengono stampate solo come suggerimenti; l'identificatore di funzione richiesto deve corrispondere esattamente.
Usa --help per le opzioni che controllano temperatura, batching, modalità di pensiero Anthropic, limiti di token e percorsi CSV personalizzati.
I nomi generati da LLM sono ausili di navigazione, non prove. Verificali sempre rispetto al corpo deoffuscato.
L'output JSCeal deoffuscato è solitamente molto grande. Carica l'output serializzato di nuovo in View8 e suddividilo in alberi di funzioni più piccoli.
In questa fase, aggiungi --scope 0. La propagazione dello scope è già stata eseguita dal deoffuscatore e ripeterla potrebbe propagare i valori in modo errato.
Un albero basato sulle relazioni di dichiarante:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/declarers \
--export_format decompiled \
--tree start \
--scope 0
Una panoramica compatta delle chiamate dirette:
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
Un albero di riferimenti più ampio, inclusi callback e handler assegnati:
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
File JSC diversi possono usare modalità diverse di offuscamento delle stringhe.
La modalità più semplice osservata usa lo spostamento degli indici ed è gestita da deobf_str1.py. La modalità JSCeal più comune usa Base64, RC4, stringhe a blocchi e indici trasformati; è gestita da deobf_str2.py.
La pipeline completa seleziona la variante 2 per impostazione predefinita. I filtri possono anche essere eseguiti indipendentemente per i test.
deobf_str2.pyUsa --help per visualizzare tutte le modalità e le opzioni disponibili:
python3 deobf_str2.py --help
Un'esecuzione diretta di deoffuscazione delle stringhe può essere avviata con:
python3 deobf_str2.py \
--inp decompiled/app.dec.pkl \
--out work/app.strings.txt \
--export_format decompiled serialized
Durante l'esecuzione, lo script identifica le funzioni di decodifica delle stringhe, carica le eventuali configurazioni in cache valide, risolve quelle mancanti, salva il CSV risultante e decodifica le stringhe. Una seconda esecuzione non è richiesta.
Quando deobf_str2.py viene usato direttamente, il suo nome CSV predefinito è resolved_funcs.csv. Seleziona un percorso specifico del campione con --csv o -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
Quando si concatenano filtri individuali, preserva l'output serializzato tra le fasi affinché le passate successive possano continuare a operare sugli oggetti View8.
deobf_all.py applica le seguenti fasi in ordine:
La rinomina delle funzioni assistita da LLM è opzionale e viene eseguita separatamente dopo la deoffuscazione strutturale.
Il repository include un flusso di lavoro helper completo in scripts/. Tutti gli script sono mantenuti in una directory e usano la stessa configurazione centralizzata.
scripts/config.sh percorsi condivisi di strumenti e workspace
scripts/copy_payloads.sh raccoglie e nomina con MD5 i file JSCeal app.jsc
scripts/unpack_all.sh decompressione Brotli
scripts/disasm_all.sh disassembly V8 batch
scripts/decompile_all.sh decompilazione View8 batch
scripts/deobfuscate_all.sh deoffuscazione batch con log combinato
scripts/run_unattended.sh deoffuscazione e validazione scollegate
scripts/collect_output.sh raccoglie cache di decoder ed elenchi di stringhe
Il scripts/config.sh fornito contiene percorsi da un ambiente di esempio:
JSC_DEOBF_ROOT="$HOME/jsc_deobfuscator"
V8DASM="$HOME/code/v8/v8dasm"
Modifica quel file una volta per configurare il percorso di installazione, il disassemblatore V8 corrispondente, le directory del workspace, i comandi esterni, i percorsi dei log e il layout di raccolta. Il workspace predefinito è la directory da cui viene lanciato lo script helper.
Ogni valore può anche essere sovrascritto tramite una variabile d'ambiente. JSC_HELPER_CONFIG può selezionare un file di configurazione diverso.
Un'esecuzione batch tipica è:
scripts/copy_payloads.sh
scripts/unpack_all.sh
scripts/disasm_all.sh
scripts/decompile_all.sh
scripts/deobfuscate_all.sh
scripts/collect_output.sh
Gli script preservano le convenzioni usate per il corpus JSCeal, inclusa la gestione dei file app.jsc scoperti come payload compressi con Brotli e la loro denominazione tramite MD5. Rivedi scripts/README.md prima di applicare il flusso di lavoro a campioni non correlati.
Per un batch lungo, scripts/run_unattended.sh avvia la deoffuscazione con nohup, scrive file di log con timestamp, PID e stato, e valida ogni output generato per riferimenti non risolti a funzioni di decodifica stringhe in cache:
scripts/run_unattended.sh
I campioni selezionati possono essere forniti esplicitamente:
scripts/run_unattended.sh \
decompiled/sample1.dec.pkl \
decompiled/sample2.dec.pkl
View8/ decompilatore View8 ed esportatore di alberi di funzioni
Utils/decompress-jsc.js fallback di decompressione Brotli per Windows
Utils/disasm/v8dasm.cpp sorgente del disassemblatore V8
Utils/disasm/patches/ patch V8 richieste dal disassemblatore
Utils/check_unresolved_decoder_references.py
helper di validazione dell'output
deobf_all.py pipeline di deoffuscazione predefinita completa
deobf_str1.py filtro semplice di spostamento degli indici delle stringhe
deobf_str2.py filtro stringhe RC4/Base64 con recupero degli indici
deobf_scope2.py propagazione di scope e dizionario
deobf_unflattener.py riappiattimento del flusso di controllo
deobf_replace_ops.py sostituzione di proxy e wrapper di operazioni
deobf_globals.py propagazione globale
deobf_inline_temporaries.py pulizia finale conservativa
deobf_ai.py rinomina opzionale delle funzioni assistita da LLM
scripts/ helper batch e di validazione configurabili
Ogni passata principale può essere eseguita separatamente per i test. Esegui lo script selezionato con --help per vedere la sua interfaccia completa:
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. Nuove varianti possono richiedere rilevatori o trasformazioni aggiuntivi.La pipeline è stata testata per regressione sul corpus JSCeal usato nella ricerca associata. I controlli di base della release includono:
python3 -m compileall -q .
python3 deobf_all.py --help
python3 deobf_str2.py --help
python3 deobf_ai.py --help
python3 View8/view8.py --help
Per ogni campione del corpus, verifica che l'esecuzione:
.pkl che .txt;Lo script helper non presidiato automatizza la validazione finale dei riferimenti ai decoder.
javascript-obfuscator.Il codice sorgente di JSC Deobfuscator scritto per questo progetto è concesso in licenza
sotto la GNU General Public License, versione 2 o (a tua scelta) qualsiasi versione successiva
(GPL-2.0-or-later). Vedi LICENSE per il testo completo della licenza.
Copyright (C) 2026 Aleksandra "Hasherezade" Doniec @ Check Point Research.
Il sottomodulo View8 è un progetto separato. Il materiale del disassemblatore derivato da terze parti
in Utils/disasm/ mantiene la sua provenienza esistente e non viene
rilicenziato dalla nota di copyright sopra.