
Décompile le bytecode V8 sérialisé (fichiers JSC) en code lisible de haut niveau ressemblant à du JavaScript, avec prise en charge de plusieurs versions de V8, sortie arborescente et nommage déterministe des fonctions pour l'analyse de logiciels malveillants.
View8 est un outil d'analyse statique conçu pour décompiler des objets bytecode V8 sérialisés (fichiers JSC) en code lisible de haut niveau. Pour analyser et désassembler ces objets sérialisés, View8 utilise un binaire V8 compilé patché. En conséquence, View8 produit une sortie textuelle similaire à JavaScript.
9.4.146.24 (Utilisée dans Node V16.x)10.2.154.26 (Utilisée dans Node V18.x)11.3.244.8 (Utilisée dans Node V20.x)Pour les versions compilées, consultez la page des versions.
--inp, : Le nom du fichier d'entrée.-i--out, -o : Chemin vers la sortie. Selon les options sélectionnées, la sortie peut être un fichier unique ou une arborescence de répertoires.--input_format, -f : Spécifie le format d'entrée. Les options sont :
raw : l'entrée est un fichier JSC brut.disassembled : le fichier d'entrée est déjà désassemblé.serialized : l'entrée est déjà décompilée et stockée dans un format sérialisé. Le format sérialisé actuel est le pickle Python ; utilisez uniquement des entrées de confiance.--export_format, -e : Spécifie le ou les formats d'exportation. Les options sont v8_opcode, translated, decompiled et serialized. Plusieurs options peuvent être combinées. Défaut : decompiled.--path, -p : Chemin vers le binaire de désassemblage. Requis si l'entrée est au format brut et que View8 ne peut pas localiser automatiquement le désassembleur correspondant.--scope : Propager les arguments de portée. Défaut : 1.--normalize : Remplacer les identifiants de fonctions dérivés des adresses par des noms déterministes basés sur l'ordre d'analyse.--normalize-map [CSV] : Lors de l'utilisation de --normalize, écrire un fichier CSV mappant chaque nom de fonction original à son nom normalisé. Lorsque le chemin est omis, View8 dérive <output>.name_map.csv de --out, ou de --inp lorsqu'aucun chemin de sortie n'est défini.--tree, -t : Diviser la sortie en une structure arborescente plutôt que de stocker toutes les fonctions dans un seul fichier. Spécifiez la fonction qui sera utilisée comme racine de l'arbre. Pour démarrer depuis la fonction principale par défaut, utilisez start.--split_mode : Mode de division de l'arbre. Les options sont declarers, calls et references. Défaut : declarers.--inline_depth, -d : Dans les modes calls et references, inclure les fonctions accessibles depuis la racine de l'arbre sélectionnée jusqu'à la profondeur N dans le fichier principal de l'arbre. La profondeur 0 signifie uniquement la racine sélectionnée ; la profondeur 1 inclut les appelés/références directs ; la profondeur 2 inclut leurs enfants. Non utilisé avec declarers.--inline_branch_limit, -l : En mode arbre, intégrer les branches enfants complètes contenant au plus N fonctions dans le fichier principal de l'arbre lorsque les branches enfants sont incluses par --inline_depth. Les branches plus grandes sont enregistrées séparément.--split_depth : Dans les modes calls et references, limiter la profondeur de parcours des branches d'utilisation exportées. Défaut : 4.--include, -n : Fichier contenant les fonctions à inclure dans la sortie.--exclude, -x : Fichier contenant les fonctions à exclure de la sortie.--func : Afficher une fonction sélectionnée.--show_all : En mode d'affichage de fonction, afficher également les lignes marquées comme cachées.--verbosity, -v : Niveau de verbosité. Plage acceptée : 0 à 3.Pour décompiler un fichier bytecode V8 et exporter le code décompilé :
python view8.py -i input_file -o output_filePar défaut, View8 détecte la version du bytecode V8 du fichier d'entrée à l'aide de VersionDetector.exe et recherche automatiquement un binaire de désassemblage compatible dans le dossier Bin. Cela peut être modifié en spécifiant un binaire de désassemblage différent avec l'option --path ou -p :
python view8.py -i input_file -o output_file --path /path/to/disassemblerPour ignorer le processus de désassemblage et fournir un fichier déjà désassemblé comme entrée, utilisez l'option --input_format disassembled ou -f disassembled :
python view8.py -i input_file -o output_file -f disassembledUtilisez --normalize pour remplacer les noms de fonctions dérivés des adresses par des identifiants déterministes. Pour préserver la relation entre les noms originaux et normalisés, ajoutez --normalize-map :
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map \
--out decompiled/sample.dec.txt \
--export_format decompiled serializedCela écrit decompiled/sample.dec.name_map.csv avec les colonnes suivantes :
original_name,normalized_name
func_start_0x268514e9dcd9,func_start_0x100000000
func_rne_0x268514eb0779,func_rne_0x100000001Un chemin CSV explicite peut également être fourni :
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map mappings/sample.names.csv \
--out decompiled/sample.dec.txtIl est parfois utile de décompiler le fichier dans un format sérialisé qui préserve les objets et structures analysés. Ce type de sortie peut être plus facile à post-traiter qu'un format texte, par exemple lors d'une désobfuscation ultérieure. Pour créer une sortie sérialisée, utilisez le format d'exportation serialized :
python view8.py -i input_file -o output_file -e serializedAvertissement de sécurité : le format sérialisé actuel est un fichier pickle Python (.pkl). Désérialiser des données provenant de sources non fiables peut exécuter du code arbitraire. Ne chargez que des fichiers sérialisés que vous avez générés vous-même.
Pour recharger une sortie sérialisée et l'exporter dans un autre format, utilisez --input_format serialized ou -f serialized :
python view8.py -i input_file -o output_file -f serializedSpécifiez le ou les formats d'exportation à l'aide de l'option --export_format ou -e. Vous pouvez combiner plusieurs formats :
v8_opcodetranslateddecompiledserializedPar exemple, pour exporter à la fois les opcodes V8 et le code décompilé côte à côte :
python view8.py -i input_file -o output_file -e v8_opcode decompiledPar défaut, le format utilisé est decompiled.
Pour les charges utiles groupées volumineuses, écrire toutes les fonctions décompilées dans un seul fichier peut être difficile à analyser. La sortie arborescente divise la racine sélectionnée et les fonctions associées en une structure de répertoires.
Utilisez --tree pour sélectionner la racine de l'arbre :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_out/ \
--tree startLa valeur spéciale start désigne la fonction principale par défaut récupérée par View8.
L'arbre peut être divisé selon l'un des trois modes suivants :
declarers : suit les relations de déclaration lexicale, c'est-à-dire quelles fonctions sont déclarées à l'intérieur ou sous une autre fonction. Ceci est utile pour comprendre la structure groupée ou modulaire, mais ne représente pas le flux d'exécution.calls : suit les appels de fonctions directs, tels que func_x(...). Ceci est utile pour reconstituer le squelette d'exécution à partir d'une racine sélectionnée.references : suit toutes les références de fonctions visibles, pas seulement les appels directs. Cela inclut les callbacks, les gestionnaires exportés, les gestionnaires de routes, les propriétés d'objets et autres fonctions assignées. Ce mode est utile pour découvrir les surfaces de capacités, mais il peut produire des arbres beaucoup plus grands que calls.Exemple :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode callsLe fichier principal de l'arbre contient toujours la fonction racine sélectionnée.
Dans les modes calls et references, --inline_depth contrôle le nombre de niveaux du graphe inclus dans le fichier principal :
--inline_depth 0 -> racine uniquement
--inline_depth 1 -> racine + appelés/références directs
--inline_depth 2 -> racine + appelés/références directs + leurs enfantsPar exemple, la commande suivante crée un aperçu d'exécution compact contenant la racine et ses appelés directs :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1Pour inclure une couche d'appels supplémentaire :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2--inline_depth n'est pris en charge qu'avec les modes calls et references. Il n'est pas utilisé avec declarers.
Les grandes branches enfants sont enregistrées dans des fichiers séparés. Dans les modes calls et references, --split_depth contrôle la profondeur de parcours des branches d'utilisation exportées :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5L'option --inline_branch_limit contrôle si les petites branches enfants complètes sont également incluses dans le fichier principal :
--inline_branch_limit 3Cela signifie que les branches enfants complètes contenant au plus 3 fonctions peuvent être intégrées dans le fichier principal. Les branches plus grandes sont enregistrées séparément.
Dans les modes calls et references, les branches enfants ne sont intégrées que lorsque le --inline_depth sélectionné inclut déjà les fonctions enfants. Par exemple, --inline_depth 0 signifie racine uniquement, donc les branches enfants ne sont pas intégrées même si elles sont petites.
Pour un aperçu d'exécution compact :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5Pour un aperçu architectural plus large :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2 \
--split_depth 5Pour les callbacks exportés, gestionnaires, routes et surfaces de capacités :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_refs/ \
--tree start \
--split_mode references \
--inline_depth 1 \
--split_depth 3Pour la structure lexicale ou modulaire :
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_declarers/ \
--tree start \
--split_mode declarers \
--split_depth 3Pour afficher une fonction sélectionnée, utilisez --func :
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_namePour afficher également les lignes marquées comme cachées :
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_name \
--show_allLa version du bytecode V8 est stockée sous forme de hachage au début du fichier. Voici les options disponibles pour VersionDetector.exe :
-h : Récupère une version et renvoie son hachage.-d : Récupère un hachage au format little-endian et renvoie sa version correspondante par force brute.-f : Récupère un fichier et renvoie sa version.