
Un outil d'instrumentation basé sur LLVM pour le suivi universel de taint, l'analyse de flux de données et le traçage.
PolyTracker est un outil créé à l'origine pour l'annotation lexicale automatisée et la navigation des analyseurs, un rétro-acronyme conçu uniquement dans le but de le désigner comme The ALAN Parsers Project. Cependant, il a évolué pour devenir un outil polyvalent permettant d'effectuer efficacement des analyses de flux de données et de flux de contrôle des programmes. PolyTracker est une passe LLVM qui instrumente les programmes pour suivre quels octets d'un fichier d'entrée sont manipulés par quelles fonctions. Il produit une base de données contenant les informations de flux de données, ainsi qu'une trace d'exécution. PolyTracker fournit également une bibliothèque Python pour interagir avec et analyser sa sortie, ainsi qu'un REPL Python interactif.
PolyTracker peut être utilisé conjointement avec PolyFile pour déterminer automatiquement la finalité sémantique des fonctions d'un analyseur. Il dispose également d'une fonctionnalité expérimentale capable de générer une grammaire hors contexte représentant le langage accepté par un analyseur.
Contrairement aux alternatives d'instrumentation dynamique comme Taintgrind, PolyTracker impose une surcharge de performance négligeable pour presque toutes les entrées, et est capable de suivre chaque octet d'entrée à la fois. PolyTracker a commencé comme un fork du DataFlowSanitizer de LLVM et s'inspire fortement du Angora Fuzzer. Cependant, contrairement au système Angora, PolyTracker est capable de suivre la provenance complète d'un taint. En février 2021, le DataFlowSanitizer de LLVM a ajouté une nouvelle fonctionnalité pour le suivi de la provenance du taint appelée origin tracking. Cependant, il ne peut suivre que 16 taints au maximum à la fois, alors que PolyTracker peut en suivre jusqu'à 231-1.
Ce README sert de guide d'utilisation général pour installer PolyTracker et compiler/instrumenter des binaires. Pour interagir programmatiquement avec ou étendre PolyTracker via son API Python, ainsi que pour interagir avec les traces d'exécution produites par le code instrumenté, consultez la documentation Python.
PolyTracker est contrôlé via un script Python appelé polytracker. Vous pouvez
l'installer en exécutant
pip3 install polytracker
PolyTracker nécessite un environnement système très particulier pour fonctionner, donc presque
tous les utilisateurs sont susceptibles de l'exécuter dans un environnement conteneurisé.
Heureusement, polytracker facilite les choses. Tout ce que vous avez à faire est
d'avoir docker installé, puis d'exécuter :
polytracker docker pull
et
polytracker docker run
Cette dernière commande monte le répertoire de travail courant dans le conteneur Docker PolyTracker et vous permet de compiler et d'exécuter des programmes instrumentés.
Le script de contrôle polytracker—que vous pouvez exécuter depuis votre système
hôte ou depuis l'intérieur du conteneur Docker—propose une variété de commandes, à la fois
pour instrumenter des programmes et pour analyser les artefacts produits. Par
exemple, vous pouvez explorer les flux de données de l'exécution, reconstruire le
graphe de flux de contrôle du programme instrumenté, et même extraire une grammaire
hors contexte correspondant aux entrées acceptées par le programme. Vous pouvez explorer ces
commandes en exécutant
polytracker --help
Le script polytracker est également un REPL, s'il est exécuté sans argument en
ligne de commande :
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> commands
PolyTracker est également fourni avec une commande build. Cette commande permet
à l'utilisateur d'exécuter n'importe quelle commande de compilation dans un
environnement instrumenté Blight. Cela produira
un fichier blight_journal.jsonl qui enregistre toutes les commandes exécutées lors de la
compilation. Si vous avez une cible C/C++, vous pouvez l'instrumenter en invoquant
polytracker build et en passant votre commande de compilation :
polytracker build gcc -g -o my_binary my_source.c
Pour instrumenter une cible de compilation, utilisez la commande instrument-targets. Par
défaut, la commande utilisera un blight_journal.jsonl dans votre répertoire de travail
courant pour construire une version instrumentée de votre cible de compilation. La
cible de compilation instrumentée sera construite avec les mêmes options que la
cible d'origine.
polytracker instrument-targets my_binary
build prend également en charge des programmes plus complexes qui utilisent un système de
compilation comme autotools ou CMake :
polytracker build cmake .. -DCMAKE_BUILD_TYPE=Release
polytracker build ninja
# ou
polytracker build ./configure
polytracker build make
Exécutez ensuite instrument-targets sur toutes les cibles de la compilation :
polytracker instrument-targets a.bin b.so
Ensuite, a.instrumented.bin et b.instrumented.so seront les versions
instrumentées. Consultez les Dockerfiles du répertoire
examples
pour des exemples d'instrumentation de programmes réels.
Le logiciel instrumenté écrira sa sortie dans le chemin spécifié par
POLYDB, ou polytracker.tdag si omis. Il s'agit d'un fichier binaire sur
lequel on peut agir en exécutant :
from polytracker import PolyTrackerTrace, taint_dag
trace = PolyTrackerTrace.load("polytracker.tdag")
tdfile = trace.tdfile
first_node = list(tdfile.nodes)[0]
print(f"First node affects control flow: {first_node.affects_control_flow}")
# Operate on all Range nodes
for index, node in enumerate(tdfile.nodes):
if isinstance(node, taint_dag.TDRangeNode):
print(f"Node {index}: first {node.first}, last {node.last}")
# Access taint forest
tdforest = trace.taint_forest
n1 = tdforest.get_node(1)
print(
f"Forest node {n1.label}. Parent labels: {n1.parent_labels}, "
f"source: {n1.source.path if n1.source is not None else None}, "
f"affects control flow: {n1.affected_control_flow}"
)
Vous pouvez également exécuter un binaire instrumenté directement depuis le REPL :
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> trace = run_trace("path_to_binary", "path_to_input_file")
Cela exécutera automatiquement le binaire instrumenté dans un conteneur Docker, si nécessaire.