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-Rundenkonstanten 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
./ghidraRunBeim 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:
| Aktion | Kürzel |
|---|---|
| Funktion umbenennen | L |
| Variable umbenennen | L (bei ausgewählter Variable) |
| Datentyp ändern | Ctrl+L |
| XREF anzeigen | Ctrl+Shift+F |
| Zur Definition springen | Ctrl+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.:
| Algorithmus | Erkannte Konstante |
|---|---|
| AES | S-Box (forward/inverse), Rundenkonstanten |
| DES / 3DES | S-Box-Permutationen |
| RC4 | KSA-Initialisierungsmuster |
| SHA-256 | Initialisierungsvektoren (0x6a09e667, ...) |
| MD5 | Initialisierungswerte |
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 F8Fü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:
- FindCrypt zuerst — Kryptoalgorithmen sind erkennbar, auch ohne Symbole
- String-Suche —
Search → For Stringsfindet Klartext-Fehlermeldungen und Protokollnamen, die Rückschlüsse auf den Kontext erlauben - JNI-Konvention nutzen —
Java_ist eine hardcodierte Zeichenkette im ELF-Symbol-Table, also auch in stripped Binaries exportiert. Nur interne Hilfsfunktionen verlieren ihre Namen. - 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-InstruktionDas 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