
Ein LLVM-basiertes Instrumentierungswerkzeug für universelles Taint-Tracking, Datenflussanalyse und Tracing.
PolyTracker ist ein Werkzeug, das ursprünglich für die Automatische Lexikalische Annotation und Navigation von Parsern entwickelt wurde – ein retronymisches Kunstwort, das einzig zu dem Zweck erfunden wurde, es als The ALAN Parsers Project bezeichnen zu können. Inzwischen hat es sich jedoch zu einem allgemeinen Werkzeug für die effiziente Durchführung von Datenfluss- und Kontrollflussanalysen von Programmen entwickelt. PolyTracker ist ein LLVM-Pass, der Programme instrumentiert, um zu verfolgen, welche Bytes einer Eingabedatei von welchen Funktionen verarbeitet werden. Es gibt eine Datenbank mit den Datenflussinformationen sowie einen Laufzeit-Trace aus. PolyTracker bietet außerdem eine Python-Bibliothek für die Interaktion mit und die Analyse seiner Ausgaben sowie eine interaktive Python-REPL.
PolyTracker kann zusammen mit PolyFile verwendet werden, um automatisch den semantischen Zweck der Funktionen eines Parsers zu bestimmen. Es verfügt außerdem über eine experimentelle Funktion, die eine kontextfreie Grammatik erzeugen kann, welche die von einem Parser akzeptierte Sprache repräsentiert.
Im Gegensatz zu dynamischen Instrumentierungsalternativen wie Taintgrind verursacht PolyTracker bei fast allen Eingaben einen vernachlässigbaren Leistungsaufwand und ist in der Lage, jedes Byte der Eingabe gleichzeitig zu verfolgen. PolyTracker entstand als Fork des LLVM DataFlowSanitizer und lässt sich stark vom Angora Fuzzer inspirieren. Anders als das Angora-System ist PolyTracker jedoch in der Lage, die gesamte Herkunft (Provenance) einer Taint-Information zu verfolgen. Im Februar 2021 wurde dem LLVM DataFlowSanitizer eine neue Funktion zur Verfolgung der Taint-Herkunft hinzugefügt, die sogenannte Origin-Tracking. Diese kann jedoch höchstens 16 Taints gleichzeitig verfolgen, während PolyTracker bis zu 231-1 verfolgen kann.
Diese README dient als allgemeiner Leitfaden für die Installation von PolyTracker und das Kompilieren/Instrumentieren von Binärdateien. Für die programmatische Interaktion mit oder Erweiterung von PolyTracker über seine Python-API sowie für die Interaktion mit Laufzeit-Traces aus instrumentiertem Code konsultieren Sie die Python-Dokumentation.
PolyTracker wird über ein Python-Skript namens polytracker gesteuert. Sie können es installieren, indem Sie
pip3 install polytracker
ausführen.
PolyTracker benötigt eine sehr spezielle Systemumgebung, um zu laufen, daher werden fast alle Benutzer es wahrscheinlich in einer Container-Umgebung ausführen. Glücklicherweise macht polytracker dies einfach. Sie müssen lediglich docker installiert haben und dann ausführen:
polytracker docker pull
und
polytracker docker run
Der letztgenannte Befehl mountet das aktuelle Arbeitsverzeichnis in den PolyTracker-Docker-Container und ermöglicht es Ihnen, instrumentierte Programme zu bauen und auszuführen.
Das polytracker-Steuerskript – das Sie entweder von Ihrem Hostsystem oder von innerhalb des Docker-Containers aus ausführen können – verfügt über eine Vielzahl von Befehlen, sowohl zum Instrumentieren von Programmen als auch zur Analyse der resultierenden Artefakte. Sie können beispielsweise die Datenflüsse in der Ausführung untersuchen, den Kontrollflussgraphen des instrumentierten Programms rekonstruieren und sogar eine kontextfreie Grammatik extrahieren, die zu den vom Programm akzeptierten Eingaben passt. Sie können diese Befehle erkunden, indem Sie
polytracker --help
ausführen.
Das polytracker-Skript ist auch eine REPL, wenn es ohne Befehlszeilenargumente ausgeführt wird:
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> commands
PolyTracker wird auch mit einem build-Befehl geliefert. Dieser Befehl ermöglicht es dem Benutzer, beliebige Build-Befehle in einer mit Blight instrumentierten Umgebung auszuführen. Dies erzeugt eine Datei blight_journal.jsonl, die alle während des Builds ausgeführten Befehle aufzeichnet. Wenn Sie ein C/C++-Ziel haben, können Sie es instrumentieren, indem Sie polytracker build aufrufen und Ihren Build-Befehl übergeben:
polytracker build gcc -g -o my_binary my_source.c
Um ein Build-Ziel zu instrumentieren, verwenden Sie den Befehl instrument-targets. Standardmäßig verwendet der Befehl eine blight_journal.jsonl in Ihrem aktuellen Arbeitsverzeichnis, um eine instrumentierte Version Ihres Build-Ziels zu erstellen. Das instrumentierte Build-Ziel wird mit denselben Flags wie das ursprüngliche Build-Ziel erstellt.
polytracker instrument-targets my_binary
build unterstützt auch komplexere Programme, die ein Build-System wie Autotools oder CMake verwenden:
polytracker build cmake .. -DCMAKE_BUILD_TYPE=Release
polytracker build ninja
# oder
polytracker build ./configure
polytracker build make
Führen Sie dann instrument-targets für beliebige Ziele des Builds aus:
polytracker instrument-targets a.bin b.so
Anschließend sind a.instrumented.bin und b.instrumented.so die instrumentierten Versionen. Siehe die Dockerfiles im examples-Verzeichnis für Beispiele, wie reale Programme instrumentiert werden können.
Die instrumentierte Software schreibt ihre Ausgabe in den Pfad, der in POLYDB angegeben ist, oder, falls dieser weggelassen wird, in polytracker.tdag. Dies ist eine Binärdatei, die mit folgendem Befehl bearbeitet werden kann:
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}"
)
Sie können ein instrumentiertes Binary auch direkt aus der REPL ausführen:
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> trace = run_trace("path_to_binary", "path_to_input_file")
Dies führt das instrumentierte Binary bei Bedarf automatisch in einem Docker-Container aus.