Programming lesson
Dynamische Kontrollabhängigkeit für Malware-Analyse: Pintool-Tutorial basierend auf Cs6747 Lab #6
Lerne, wie du mit einem Pintool dynamische Kontrollabhängigkeitsgraphen (CDG) erstellst, um Malware wie den Greencat-Trojaner zu analysieren – inklusive Schritt-für-Schritt-Anleitung und Beispielen.
Einführung in die dynamische Kontrollabhängigkeit
In der Malware-Analyse ist es entscheidend zu verstehen, welche Bedingungen (Prädikate) die Ausführung bestimmter Schadcode-Teile steuern. Die statische Analyse stößt hier oft an Grenzen, besonders bei selbstmodifizierendem oder gepacktem Code. Hier kommt die dynamische Kontrollabhängigkeit (Dynamic Control Dependence) ins Spiel: Sie basiert auf tatsächlichen Ausführungsspuren (Traces) und ermöglicht es, den Einfluss von Eingaben wie C&C-Befehlen präzise nachzuvollziehen. Dieses Tutorial führt dich durch die Entwicklung eines Pintools zur Erstellung eines dynamischen Kontrollabhängigkeitsgraphen (CDG) – genau wie in der Cs6747 Lab #6 Aufgabe.
Hintergrund: Der Greencat-Malware-Fall
Stell dir vor, du analysierst die berüchtigte Greencat-Malware, die 2014 ein E-Commerce-Startup in Monroe, CT, befallen hat. Die Malware kommuniziert über HTTP mit einem C&C-Server. Du hast Netzwerkpaketmitschnitte der Buchhaltungsrechner, aber wie findest du heraus, welcher C&C-Befehl welche Payload auslöst? Die Lösung: Ein dynamischer Kontrollabhängigkeitsgraph. Mit einem Pintool instrumentierst du die Malware, sendest verschiedene C&C-Befehle und zeichnest die Ausführungspfade auf. Aus diesen Daten berechnest du die Kontrollabhängigkeiten – also welche Instruktionen von welchen Prädikaten gesteuert werden.
Was ist ein dynamischer Kontrollabhängigkeitsgraph (CDG)?
Ein CDG ist ein gerichteter Graph, dessen Knoten die ausgeführten Instruktionen (Adressen) sind. Eine Kante von Instruktion A zu Instruktion B bedeutet, dass B von A kontrollabhängig ist: Die Ausführung von B wird durch das Ergebnis von A (z.B. eine Verzweigung) bestimmt. Die Berechnung basiert auf dem Konzept des unmittelbaren Postdominators (Immediate Post-Dominator, IPD). Eine Instruktion Y ist der IPD von X, wenn Y auf jedem Pfad ab X erreicht wird und es keinen anderen Knoten gibt, der diese Eigenschaft hat und zwischen X und Y liegt.
Schritt 1: Pintool für dynamische Kontrollabhängigkeit
Dein Pintool muss die dynamische Instruktionsspur (Trace) aus Lab #5 nutzen. Du benötigst für jede ausgeführte Instruktion die Adresse und den Kontrollfluss (z.B. Sprungziele). Optional kannst du einen Startknoten einführen, um die Abhängigkeiten des Programmstarts zu modellieren. Die Berechnung des CDG erfolgt in drei Schritten:
- Trace erfassen: Zeichne für jede Instruktion die Adresse und den nächsten Ausführungsschritt auf. Nutze Pin’s
INS_AddInstrumentFunctionundTRACE_AddInstrumentFunction. - Postdominator-Baum berechnen: Aus dem linearen Trace leitest du den Kontrollflussgraphen (CFG) ab. Berechne dann die IPDs – entweder im Pintool (z.B. mit einem Algorithmus von Lengauer-Tarjan) oder in einem Post-Processing-Skript mit NetworkX.
- CDG erstellen: Für jede Instruktion X: Finde den IPD Y. Wenn X nicht von Y dominiert wird, dann ist X von Y kontrollabhängig. Füge eine Kante von Y zu X hinzu. Wiederhole für alle Instruktionen.
Dein Pintool sollte am Ende eine DOT-Datei ausgeben. Beispiel: 0x634 -> 0x638; bedeutet, dass Instruktion 0x638 von 0x634 kontrollabhängig ist.
Schritt 2: CDG-Berechnung mit Python und NetworkX
Da die IPD-Berechnung komplex ist, empfiehlt sich ein Post-Processing-Skript. Hier ein minimales Beispiel:
import networkx as nx
# Angenommen, du hast eine Liste von (src, dst) Kanten aus dem Trace
trace_edges = [(0x634, 0x638), (0x634, 0x63C), ...]
cfg = nx.DiGraph()
cfg.add_edges_from(trace_edges)
# Berechne Postdominator-Baum (vereinfacht: nutze nx.immediate_dominators mit reverse Graph)
rev_cfg = cfg.reverse()
# Für jeden Knoten: IPD = nächster Postdominator im rev_cfg
# ... (hier müsste der Lengauer-Tarjan Algorithmus implementiert werden)
# Für dieses Tutorial nehmen wir an, du hast eine Funktion get_ipd(node)
cdg = nx.DiGraph()
for node in cfg.nodes():
ipd = get_ipd(node)
if ipd and not nx.has_path(cfg, node, ipd): # Vereinfachte Bedingung
cdg.add_edge(ipd, node)
nx.drawing.nx_pydot.write_dot(cdg, "cdg.dot")Beachte: Die tatsächliche IPD-Berechnung erfordert den Lengauer-Tarjan-Algorithmus. NetworkX bietet immediate_dominators, aber nur für Vorwärts-Dominatoren. Für Postdominatoren musst du den Graphen umkehren.
Schritt 3: Ausführung mit Greencat
Starte Greencat in einer kontrollierten Umgebung (z.B. VM). Sende nacheinander die bekannten C&C-Befehle (z.B. cmd1, cmd2, …). Dein Pintool zeichnet für jeden Befehl einen eigenen Trace auf. Generiere für jeden Befehl eine separate DOT-Datei. So siehst du, welcher Befehl welche Code-Pfade aktiviert.
Trend-Beispiel: Wie ein KI-Chatbot Befehle verarbeitet
Stell dir vor, ein KI-Chatbot wie ChatGPT verarbeitet verschiedene Benutzereingaben. Jede Eingabe (z.B. „Erzähle einen Witz“ vs. „Wie ist das Wetter?“) führt zu unterschiedlichen Code-Pfaden. Ein dynamischer CDG würde zeigen, dass die Antwortfunktion von der Eingabeverarbeitung kontrollabhängig ist. Genauso zeigt der CDG von Greencat, welcher C&C-Befehl die Schadfunktion aktiviert.
Praktische Tipps und häufige Fehler
- Speicher: Der Trace kann riesig werden. Begrenze die Anzahl der Instruktionen oder verwende Sampling.
- Mehrere Durchläufe: Es ist okay, das Pintool mehrmals auszuführen, um verschiedene Pfade abzudecken.
- Startknoten: Füge einen künstlichen START-Knoten hinzu, von dem alle ersten Instruktionen kontrollabhängig sind. Das vereinfacht die Berechnung.
- Vergiss nicht: Die Kanten im DOT zeigen vom Prädikat zur kontrollabhängigen Instruktion – also umgekehrt zum CFG.
Zusammenfassung
Mit diesem Tutorial hast du gelernt, wie du ein Pintool zur dynamischen Kontrollabhängigkeit erstellst. Die Methode ist essenziell für die Malware-Analyse, aber auch für Program Slicing und Debugging. Wende sie auf Greencat an und finde heraus, welche C&C-Befehle die schädlichen Payloads steuern. Viel Erfolg bei Cs6747 Lab #6!