Ghidra & FindCrypt

Native Bibliotheken reverse-engineeren und Kryptoschlüssel finden

Was ist Ghidra?

Ghidra ist ein Open-Source-Reverse-Engineering-Framework, das die NSA (National Security Agency) entwickelt hat und 2019 der Öffentlichkeit zugänglich gemacht hat. Es disassembliert und dekompiliert native Binärdateien — darunter ARM64-.so-Bibliotheken aus Android-APKs — und übersetzt Maschinencode in lesbaren C-ähnlichen Pseudocode.

In einer BLE-Sicherheitsanalyse setzt du Ghidra in Phase 3 ein: nachdem JADX gezeigt hat, dass die App sicherheitskritischen Code in eine native Bibliothek ausgelagert hat, öffnet Ghidra genau diese Bibliothek und legt offen, was darin steckt — insbesondere hardcodierte Kryptoschlüssel.

Das FindCrypt-Plugin erweitert Ghidra um automatische Erkennung von kryptographischen Konstanten. Statt manuell nach bekannten AES-S-Box-Bytes oder SHA-Rundenkonst­anten zu suchen, scannt FindCrypt die gesamte Binärdatei und markiert alle Treffer in Sekunden.

Ghidra vs. IDA Pro

IDA Pro ist der kommerzielle Industriestandard für Reverse Engineering (Lizenz: mehrere Tausend Euro). Ghidra ist kostenlos, kommt dem Funktionsumfang für ARM64-ELF-Analyse sehr nahe und ist für BLE-Sicherheitsanalysen vollständig ausreichend. Der Decompiler-Output ist stellenweise weniger sauber als bei IDA, aber alle relevanten Informationen sind vorhanden.

Installation

Systemvoraussetzungen

  • Java 21 oder neuer (JDK, nicht nur JRE — Ghidra startet nicht ohne das vollständige JDK)
  • 8 GB RAM empfohlen; für große Bibliotheken mit vielen Funktionen mehr
  • Betriebssystem: Linux, macOS oder Windows

Ghidra installieren

# Java-Version prüfen
java -version
# Muss: openjdk 21 oder höher ausgeben

# JDK installieren, falls nötig (Debian/Ubuntu)
sudo apt install openjdk-21-jdk

# Ghidra herunterladen: https://ghidra-sre.org/
# Wähle die aktuellste stabile Version (11.4.3+)
# Kein Installer — ZIP entpacken, fertig

cd ~/tools/ghidra_11.4.3_PUBLIC
./ghidraRun

Beim ersten Start legt Ghidra ein Projektverzeichnis an. Wähle einen festen Ort (z. B. ~/ghidra-projects/), damit du Projekte später wiederfindest.

FindCrypt-Plugin installieren

FindCrypt ist ein Community-Script, das als Python- oder Java-Datei in den Ghidra Script Manager geladen wird.

# FindCrypt herunterladen
# Quelle: https://github.com/d3v1l401/FindCrypt-Ghidra

# Script-Verzeichnis anlegen, falls noch nicht vorhanden
mkdir -p ~/ghidra_scripts

# FindCrypt.py dort ablegen
cp FindCrypt.py ~/ghidra_scripts/

In Ghidra: Window → Script Manager → Verzeichnis hinzufügen (~/ghidra_scripts/) → FindCrypt erscheint in der Liste.

Java-Version prüfen — häufigste Fehlerquelle

Ghidra 11.3+ verlangt JDK 21 (nicht nur JRE). Ein häufiger Fehler: das System hat java installiert (JRE), aber javac fehlt (JDK). Prüfe mit javac --version — gibt es keine Ausgabe oder eine Version unter 21, fehlt das richtige JDK.

Erste Schritte: libAES.so der LED-Brille analysieren

Dieses Beispiel folgt der realen Analyse der Sonhomay LED-Brille aus der Thesis. Ziel: den hardcodierten AES-128-Schlüssel aus libAES.so extrahieren.

Schritt 1: Bibliothek importieren

Du hast libAES.so bereits in Phase 1 aus der APK extrahiert. Sie liegt im Archiv unter lib/arm64-v8a/libAES.so.

1. Ghidra öffnen → Neues Projekt: File → New Project → Next → Projektname eingeben
2. File → Import File → libAES.so auswählen
3. Ghidra erkennt automatisch: ELF, ARM Cortex, 64-bit, little-endian
4. OK → im folgenden Dialog "Analyze" bestätigen
5. Analyse läuft 10–30 Sekunden (Fortschrittsbalken unten rechts)

Nach Abschluss öffnet sich der Code Browser — das Hauptarbeitsfenster.

Schritt 2: JNI-Exportfunktion finden

Im Symbol Tree links unter Exports findest du alle öffentlichen Funktionen der Bibliothek. JNI-Exporte beginnen mit Java_. Aus der JADX-Analyse in Phase 1 kennst du bereits den Paketnamen — die gesuchte Funktion heißt:

Symbol Tree → Exports → Java_csh_tiro_cc_aes_keyExpansionDefault
Adresse: 0x00101278

Doppelklick öffnet Disassembler (links) und Decompiler (rechts) gleichzeitig. Der Decompiler-Output ist der wichtigste Blickwinkel für die Analyse.

Schritt 3: FindCrypt ausführen

Bevor du den Decompiler-Code liest, lass FindCrypt die gesamte Bibliothek scannen:

Window → Script Manager → "FindCrypt" eingeben → Run (grüner Pfeil)

FindCrypt zeigt eine Tabelle mit Treffern im Console-Fenster:

Adresse       Algorithmus   Beschreibung
0x00102000    AES           AES S-Box (Forward)
0x00102100    AES           AES S-Box (Inverse)
0x00102200    AES           Round Constants

Diese Adressen zeigen, wo die AES-Implementierung ihre Lookup-Tabellen gespeichert hat. Doppelklick auf einen Eintrag springt direkt dorthin.

Schritt 4: Schlüssel in der .rodata-Section lesen

Zurück zur JNI-Funktion bei 0x00101278: Im Decompiler siehst du, dass der Schlüssel über einen GOT-Eintrag (PTR_key_00112ff0) referenziert wird, der auf die .rodata-Section zeigt. Navigiere direkt zur Schlüsseladresse:

Navigation → Go To Address → 0x00113020 → Enter

Im Listing-Fenster erscheinen die rohen Bytes des AES-Schlüssels:

Adresse: 0x00113020  (.rodata-Section)

34 52 2A 5B  7A 6E 49 2C  08 09 0A 9D  8D 2A 23 F8

Das sind genau 16 Bytes (128 Bit) — der AES-128-Schlüssel. Er ist in jeder weltweit verkauften Einheit der LED-Brille identisch.

Schlüssel sofort verifizieren

Verifiziere den extrahierten Schlüssel mit einem Klartext-/Chiffretext-Paar aus dem PCAP-Capture von Phase 2: from Crypto.Cipher import AES; key = bytes([0x34,0x52,0x2A,0x5B,0x7A,0x6E,0x49,0x2C,0x08,0x09,0x0A,0x9D,0x8D,0x2A,0x23,0xF8]); AES.new(key, AES.MODE_ECB).encrypt(plaintext).hex(). Stimmt der Output mit dem erfassten Paket überein, ist der Schlüssel korrekt.

Wichtigste Features

Symbol Tree und Exports

Der Symbol Tree (links) gliedert die Bibliothek in:

  • Exports — öffentlich aufrufbare Funktionen (JNI-Einstiegspunkte)
  • Functions — alle erkannten Funktionen inkl. interne Hilfsfunktionen
  • Labels — benannte Datenpunkte (z. B. globale Variablen, Konstanten)
  • Namespaces — Gruppierung nach Klassen (wenn Debugsymbole vorhanden)

Für JNI-Analyse: immer bei Exports beginnen. Alle mit Java_ beginnenden Einträge sind JNI-Brücken aus dem Java-Code.

Decompiler-Fenster

Der Decompiler übersetzt ARM64-Maschinencode in C-ähnlichen Pseudocode. Er ist nicht perfekt — manche Konstrukte sehen ungewöhnlich aus — aber er zeigt die Logik korrekt. Wichtige Tastenkürzel:

AktionKürzel
Funktion umbenennenL
Variable umbenennenL (bei ausgewählter Variable)
Datentyp ändernCtrl+L
XREF anzeigenCtrl+Shift+F
Zur Definition springenCtrl+Klick

Cross-References (XREF)

XREFs zeigen, von wo aus eine Funktion oder Adresse referenziert wird. Klicke im Listing oder Decompiler auf eine Adresse und drücke Ctrl+Shift+F (oder wähle References → Find References):

XREF auf 0x00113020:
  0x00101290  LOAD   Java_csh_tiro_cc_aes_keyExpansionDefault

Das zeigt: Adresse 0x00113020 wird genau einmal geladen — aus der JNI-Funktion heraus. Nützlich, um zu prüfen, ob ein Schlüssel an mehreren Stellen verwendet wird.

Data Type Editor

Für strukturierte Daten (z. B. ein C-Struct, das einen Key zusammen mit Metadaten speichert) kannst du unter File → Open Data Type Manager → New Structure eigene Typen anlegen. Weise sie im Listing per Rechtsklick → Data → Reinterpret zu. Ghidra zeigt die Bytes dann als strukturierten Typ.

FindCrypt: Erkannte Algorithmen

FindCrypt erkennt u. a.:

AlgorithmusErkannte Konstante
AESS-Box (forward/inverse), Rundenkonst­anten
DES / 3DESS-Box-Permutationen
RC4KSA-Initialisierungs­muster
SHA-256Initialisierungs­vektoren (0x6a09e667, ...)
MD5Initialisierungs­werte

Kein Treffer bedeutet nicht zwingend, dass keine Kryptographie vorliegt — manche Implementierungen berechnen Konstanten zur Laufzeit, statt sie zu speichern. In dem Fall hilft dynamische Analyse mit Frida.

Fortgeschrittene Techniken

Ghidra-Scripting: Schlüsselextraktion automatisieren

Wenn du viele Bibliotheken analysierst oder denselben Schlüssel über mehrere Versionen hinweg verfolgen willst, lohnt sich ein Ghidra-Script. Ab Ghidra 11.3 stehen zwei Python-Varianten zur Verfügung: Jython (Python 2.7, intern mitgeliefert) und PyGhidra (CPython 3, für Scripts außerhalb des Ghidra-Installationsverzeichnisses). Java-Scripts werden ebenfalls unterstützt.

# Beispiel: Ghidra Python-Script (ablegen unter ~/ghidra_scripts/extract_rodata_key.py)
# Startet im Script Manager mit Run
# Kompatibel mit Jython und PyGhidra

from ghidra.program.model.mem import MemoryAccessException

def extract_key(program, address_str, length=16):
    addr = program.getAddressFactory().getAddress(address_str)
    memory = program.getMemory()
    key_bytes = []
    for i in range(length):
        key_bytes.append(memory.getByte(addr.add(i)) & 0xFF)
    return key_bytes

key = extract_key(currentProgram, "0x00113020")
print("AES Key: " + " ".join("{:02X}".format(b) for b in key))
# Ausgabe: AES Key: 34 52 2A 5B 7A 6E 49 2C 08 09 0A 9D 8D 2A 23 F8

Für Java-Scripting (schneller, direkter API-Zugriff) nutze den eingebauten Eclipse-basierten Script Editor unter Window → Script Manager → Edit Script.

Stripped Binaries analysieren

Ohne Symbole (stripped binary) fehlen alle Funktionsnamen — Ghidra benennt alles FUN_00101234. In diesem Fall:

  1. FindCrypt zuerst — Kryptoalgorithmen sind erkennbar, auch ohne Symbole
  2. String-SucheSearch → For Strings findet Klartext-Fehlermeldungen und Protokollnamen, die Rückschlüsse auf den Kontext erlauben
  3. JNI-Konvention nutzenJava_ ist eine hardcodierte Zeichenkette im ELF-Symbol-Table, also auch in stripped Binaries exportiert. Nur interne Hilfsfunktionen verlieren ihre Namen.
  4. Funktionssignaturen erkennen — AES-Initialisierungsfunktionen haben charakteristische Muster: Lookup-Table-Zugriffe in Schleifen, XOR-Operationen mit Byte-Masken

Decompiler-Artefakte verstehen

Ghidra's Decompiler produziert manchmal irreführende Ausgaben:

Zwei Ladeoperationen statt einer 128-Bit-Last:

// Ghidra zeigt:
___bss_start__  = *PTR_key_00112ff0;        // Bytes 0–7
_DAT_00113038   = *(PTR_key_00112ff0 + 8);  // Bytes 8–15

// Tatsächlicher ARM64-Assembler:
ldr  q0, [x1]   // Eine einzige 128-Bit-NEON-Instruktion

Das ist ein bekanntes Artefakt. Es ist ein einziger zusammenhängender 16-Byte-Schlüssel.

Unbenannte lokale Variablen: Variablen wie uVar1, lVar2 sind temporäre Bezeichnungen. Benenne sie sofort um (L), sobald du ihre Bedeutung erkennst — das macht den Code erheblich lesbarer.

Mehrere Binärversionen vergleichen (Version Tracking)

Ghidra enthält ein eingebautes Version Tracking-Modul (unter Tools → Version Tracking). Es vergleicht zwei Binärdateien und zeigt, welche Funktionen identisch geblieben sind, welche sich verändert haben. Nützlich, wenn du prüfen willst, ob ein Hersteller eine Lücke in einem Firmware-Update tatsächlich geschlossen hat.

Decompiler-Timeout erhöhen

Große, unrollte Verschlüsselungsfunktionen (>500 Zeilen Assembler) können den Decompiler überfordern. Standardmäßig bricht er nach 60 Sekunden ab:

Edit → Tool Options → Decompiler → Analysis → Timeout (seconds): 300

Setze den Wert auf 300 Sekunden, bevor du mit der Analyse einer unbekannten Bibliothek beginnst.

Häufige Probleme

Falsche Architektur ausgewählt

Symptom: Ghidra zeigt beim Import x86 oder armeabi-v7a an, obwohl es eine ARM64-Bibliothek ist.

Ursache: Manche APKs liefern mehrere .so-Versionen. Du hast versehentlich die falsche geladen.

Lösung: Prüfe den Datei-Pfad innerhalb des APK-Archivs: lib/arm64-v8a/libAES.so ist ARM64, lib/armeabi-v7a/libAES.so ist 32-Bit-ARM. Speicheradressen und Registernamen unterscheiden sich je nach Architektur — lade immer arm64-v8a für moderne Android-Geräte.

Decompiler-Timeout

Symptom: Das Decompiler-Fenster zeigt Decompiler timed out statt Pseudocode.

Lösung: Timeout auf 300 Sekunden erhöhen (siehe oben). Falls das nicht reicht: die Funktion ist vermutlich absichtlich obfuskiert oder sehr groß. Arbeite dann im Disassembler direkt mit dem Assembler-Code.

FindCrypt findet keine Treffer

Mögliche Ursachen:

  • Konstanten werden zur Laufzeit berechnet (AES-Key-Schedule on-the-fly) — dann hilft nur dynamische Analyse mit Frida
  • Proprietäre Kryptoalgorithmen ohne bekannte Signaturen
  • FindCrypt-Script nicht korrekt installiert (Verzeichnis nicht in Ghidra eingetragen)

Prüfen: Im Script Manager unter ~/ghidra_scripts/ muss FindCrypt.py sichtbar sein. Falls nicht: Script Manager → Script Directories → Plus-Icon → Verzeichnis hinzufügen.

Java-Versionsfehler beim Start

Symptom: Ghidra requires Java 21 or later oder Ghidra startet nicht.

Lösung:

# Installierte Java-Versionen anzeigen
update-java-alternatives --list

# Auf Java 21 wechseln
sudo update-java-alternatives --set java-21-openjdk-amd64

# Ghidra-eigene JDK-Variable setzen (optional, überschreibt System-JDK)
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
./ghidraRun

Wissens-Check