
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.txt;brotli 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.