
Synthétiseur Greybox conçu pour la désobfuscation des instructions d'assembleur.
QSynthesis est une API Python3 permettant d'effectuer une synthèse de programme basée sur les entrées/sorties d'expressions de vecteurs de bits. Elle vise à faciliter la désobfuscation de code. L'algorithme est une approche greybox combinant à la fois une synthèse boîte noire basée sur les entrées/sorties et une recherche AST boîte blanche pour synthétiser des sous-expressions (si le nœud racine ne peut pas être synthétisé).
Cet algorithme a été décrit à l'origine lors de l'atelier académique BAR :
Le code a été publié dans le cadre de la conférence Black Hat suivante :
Avertissement : Ce framework est expérimental et ne doit être utilisé qu'à des fins d'expérimentation. Il vise principalement à stimuler la recherche dans ce domaine.
L'installation, les exemples et la documentation de l'API sont disponibles sur la documentation dédiée : Documentation
Le cœur de la synthèse est basé sur le moteur symbolique Triton sur lequel est construit l'ensemble du framework. Il fournit les fonctionnalités suivantes :
Pour fonctionner, Triton doit d'abord être installé : documentation d'installation. Triton ne s'installe pas automatiquement dans un virtualenv, copiez-le dans votre venv ou utilisez --system-site-packages lors de la configuration de votre venv.
Ensuite :
$ git clone https://github.com/quarkslab/qsynthesis.git
$ cd qsynthesis
$ pip3 install '.[all]'
Le [all] installera toutes les dépendances (voir la documentation pour une installation légère).
L'algorithme de synthèse nécessite de générer des tables d'oracles dérivées d'une grammaire (un
ensemble de variables et d'opérateurs). L'installation de Qsynthesis fournit l'utilitaire qsynthesis-table-manager
permettant de manipuler les tables. La commande suivante génère une table avec 3 variables de 64 bits,
5 opérateurs utilisant un vecteur de 16 entrées. Nous limitons la génération à 5 millions d'entrées.
$ qsynthesis-table-manager generate -bs 64 --var-num 3 --input-num 16 --random-level 5 --ops AND,NEG,MUL,XOR,NOT --watchdog 80 --limit 5000000 my_oracle_table
Generate Table
Watchdog value: 80.0
Depth 2 (size:3) (Time:0m0.23120s)
Depth 3 (size:21) (Time:0m0.23198s)
Depth 4 (size:574) (Time:0m0.26068s)
Depth 5 (size:400858) (Time:0m21.23231s)
Threshold reached, generation interrupted
Stop required
Depth 5 (size:5000002) (Time:4m52.56009s) [RAM:9.52Gb]
Note : Le processus de génération consomme de la RAM, le --watchdog permet de définir un
pourcentage de la RAM au-delà duquel la génération est interrompue.
Nous pouvons ensuite essayer de simplifier une expression apparemment obfusquée avec :
from qsynthesis import SimpleSymExec, TopDownSynthesizer, InputOutputOracleLevelDB
blob = b'UH\x89\xe5H\x89}\xf8H\x89u\xf0H\x89U\xe8H\x89M\xe0L\x89E\xd8H\x8bE' \
b'\xe0H\xf7\xd0H\x0bE\xf8H\x89\xc2H\x8bE\xe0H\x01\xd0H\x8dH\x01H\x8b' \
b'E\xf8H+E\xe8H\x8bU\xe8H\xf7\xd2H\x0bU\xf8H\x01\xd2H)\xd0H\x83\xe8' \
b'\x02H!\xc1H\x8bE\xe0H\xf7\xd0H\x0bE\xf8H\x89\xc2H\x8bE\xe0H\x01\xd0' \
b'H\x8dp\x01H\x8bE\xf8H+E\xe8H\x8bU\xe8H\xf7\xd2H\x0bU\xf8H\x01\xd2' \
b'H)\xd0H\x83\xe8\x02H\t\xf0H)\xc1H\x89\xc8H\x83\xe8\x01]\xc3'
# Perform symbolic execution of the instructions
symexec = SimpleSymExec("x86_64")
symexec.initialize_register('rip', 0x40B160) # arbitrary address
symexec.initialize_register('rsp', 0x800000) # arbitrary stack
symexec.execute_blob(blob, 0x40B160)
rax = symexec.get_register_ast("rax") # retrieve rax register expressions
# Load lookup tables
ltm = InputOutputOracleLevelDB.load("my_oracle_table")
# Perform Synthesis of the expression
synthesizer = TopDownSynthesizer(ltm)
synt_rax, simp = synthesizer.synthesize(rax)
print(f"expression: {rax.pp_str}")
print(f"synthesized expression: {synt_rax.pp_str} [{simp}]")
Un grand merci aux contributeurs de cette recherche :