JADX-GUI

Android-APKs dekompilieren und BLE-Logik aufdecken

Was ist JADX-GUI und wofür brauchst du es?

JADX-GUI wandelt kompilierten Android-Bytecode (.dex-Dateien aus einer APK) zurück in lesbares Java. Das Ergebnis ist kein perfekter Quellcode — Variablennamen gehen verloren, manche Konstrukte werden anders dargestellt — aber es reicht vollständig aus, um die Programmlogik zu verstehen. Für BLE-Sicherheitsanalysen ist JADX-GUI das erste Werkzeug, das du anfasst: Es deckt auf, welche UUIDs eine App verwendet, ob und wie sie verschlüsselt, wo sie writeCharacteristic() aufruft, und ob kryptografische Schlüssel direkt im Java-Code stehen oder in eine native Bibliothek ausgelagert wurden. Oft lässt sich in JADX innerhalb von Minuten feststellen, ob ein Gerät hardcodierte Schlüssel hat oder ob sich die Analyse in Richtung Ghidra bewegen muss.


Installation

Linux

Die meisten Distributionen haben JADX nicht in ihren offiziellen Paketquellen, oder die Version ist veraltet. Empfohlen: direkt von GitHub herunterladen.

# Direkter Download (Version 1.5.3)
wget https://github.com/skylot/jadx/releases/download/v1.5.3/jadx-1.5.3.zip
unzip jadx-1.5.3.zip -d ~/.local/jadx
export PATH="$PATH:$HOME/.local/jadx/bin"

# Danach dauerhaft in Shell-Profil eintragen:
echo 'export PATH="$PATH:$HOME/.local/jadx/bin"' >> ~/.bashrc

# Starten
jadx-gui

Auf Debian/Ubuntu-Systemen gibt es auch ein Community-Paket, aber prüfe die Version:

sudo apt install jadx
jadx --version  # Sollte 1.5.x oder neuer sein

macOS

brew install jadx
jadx-gui

Windows

  1. jadx-1.5.3.zip von github.com/skylot/jadx/releases herunterladen
  2. In ein Verzeichnis deiner Wahl entpacken, z. B. C:/Tools/jadx/
  3. bin/jadx-gui.bat ausführen — kein Installer nötig

Java-Version

JADX benötigt Java 11 oder neuer. Prüfe mit java -version. Falls nicht vorhanden: OpenJDK von adoptium.net herunterladen. Auf macOS erledigt brew install jadx die Java-Abhängigkeit automatisch.


Erste Schritte — BLE-Beispiel: LED-Brille

Als konkretes Beispiel dient die App der LED-Brille: com.pinkysinyeeho.funkyglassesplus. Die APK kannst du mit ADB vom Testgerät extrahieren oder über einen APK-Mirror-Dienst herunterladen.

APK öffnen

jadx-gui com.pinkysinyeeho.funkyglassesplus.apk

JADX lädt und dekompiliert die APK. Bei mittelgroßen Apps dauert das 20–60 Sekunden. Danach siehst du im linken Panel einen Paketbaum mit drei Hauptbereichen:

  • Source code — dekompilierter Java-Code, gegliedert nach Paketnamen
  • Resources — XML-Ressourcen, AndroidManifest.xml, strings.xml
  • lib/ — native .so-Bibliotheken (relevant für Phase 3)

Krypto-Code finden: die Agreement-Klasse

Öffne die globale Textsuche mit Strg+Shift+F. Suche nach Cipher.getInstance. JADX hebt alle Treffer hervor und zeigt sie in einer Liste. In der LED-Brille-App führt einer der Treffer zur Klasse:

com.pinkysinyeeho.funkyglassesplus.model.data.Agreement

Dort findest du:

public static byte[] getEncryptData(byte[] data) {
    aes.cipher(data, data);
    return data;
}

Das Objekt aes ist vom Typ einer JNI-Wrapper-Klasse, die über System.loadLibrary("AES") geladen wird. Der eigentliche Schlüssel steckt nicht im Java-Code — er ist in der nativen Bibliothek libAES.so vergraben. Das ist ein typisches Muster, das Phase 3 (Ghidra) erfordert.

UUIDs per Regex suchen

Öffne die globale Suche (Strg+Shift+F), aktiviere Regular expressions und verwende dieses Muster:

[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}

Für die LED-Brille liefert das drei herstellerspezifische UUIDs, die den Control-, Data- und Notification-Charakteristiken entsprechen. Diese UUIDs brauchst du später, um den Wireshark-Traffic den richtigen Funktionen zuzuordnen.

Kurzform für Standard-UUIDs

Viele BLE-Charakteristiken nutzen Bluetooth-SIG-UUIDs im Format 0000XXXX-0000-1000-8000-00805f9b34fb. Suche zusätzlich nach dem Kurzmuster 0000fff oder 0000180 — das findet Standarddienste wie Battery Service (0x180F) oder Device Information (0x180A) schneller.


Wichtigste Features

Globale Textsuche (Strg+Shift+F)

Die mächtigste Funktion in JADX. Die Suche durchläuft den gesamten dekompilierten Code und alle Ressourcen. Aktiviere Regular expressions für Mustersuche:

SucheRegex?Zweck
Cipher.getInstanceNeinAES/DES/RSA-Verwendung finden
SecretKeySpecNeinSchlüssel-Initialisierung
System.loadLibraryNeinNative Bibliotheken identifizieren
[0-9a-f]{8}-[0-9a-f]{4}JaUUID-Muster
writeCharacteristicNeinBLE-Sendepfade
0x[0-9a-fA-F]{2,4}JaHex-Literale (potenzielle Schlüsselbytes)

Deobfuskierung

JADX enthält einen eingebauten Deobfuskierer. Er benennt Ein-Zeichen-Klassen (a, b, c) in sprechendere Namen um und rekonstruiert, wo möglich, ursprüngliche Strukturen. Aktiviere ihn unter Tools → Deobfuscation oder starte JADX mit:

jadx-gui --deobf target.apk

Deobfuskierung verändert Klassennamen

Nach der Deobfuskierung stimmen die Klassennamen nicht mehr mit dem Stack Trace einer laufenden App überein. Wenn du Frida-Hooks schreibst oder Smali-Patches planst, arbeite ohne Deobfuskierung — sonst verlierst du die Zuordnung.

Resource Browser

Im linken Panel unter Resources findest du alle XML-Dateien der App. Besonders interessant:

  • AndroidManifest.xml — App-Berechtigungen (Bluetooth, Location), deklarierte Services, Broadcast Receiver
  • res/values/strings.xml — Hardcodierte Strings, manchmal inkl. Server-URLs, API-Schlüssel oder Versionsnummern
  • assets/ — Zertifikate, Konfigurationsdateien, manchmal eingebettete Binärdaten

Cross-Reference-Navigation (X-Refs)

Rechtsklick auf eine Klasse, Methode oder Feld → Find usages. JADX zeigt alle Codestellen, die diesen Bezeichner referenzieren. Das ist besonders hilfreich, um den Datenpfad rückwärts zu verfolgen: von writeCharacteristic() zur Methode, die den Payload aufbaut, von dort zur Verschlüsselungsroutine.

Export als Gradle-Projekt

File → Save as Gradle project exportiert den dekompilierten Code in eine IDE-kompatible Struktur. Du kannst das Projekt dann in Android Studio oder IntelliJ IDEA öffnen und die IDE-eigene Navigation nutzen — hilfreich bei großen Apps mit hunderten Klassen.


Fortgeschrittene Techniken

Obfuskierungsmuster erkennen

Professionell geschützte Apps verwenden ProGuard (eingebaut in Android Studio) oder das deutlich aggressivere DexGuard. Erkennungsmerkmale:

MerkmalProGuardDexGuard
Klassennamena, b, c (Einbuchstaben)Zufällige Unicode-Namen
StringsKlartextVerschlüsselt (Runtime-Entschlüsselung)
ReflectionSeltenHäufig für Methoden-Lookup
Zeitaufwand RE4–8 Stunden12–40 Stunden

Bei stark obfuskierten Apps hilft Frida (dynamische Analyse) mehr als statisches RE mit JADX. Erkenne es früh: Wenn du nach 30 Minuten keine sinnvollen Klassen- oder Methodennamen siehst und alle Strings als byte-Array-Initialisierungen erscheinen, ist DexGuard im Spiel.

Smali-Ansicht bei Dekompilierungsfehlern

Manchmal erzeugt JADX für einzelne Methoden keinen gültigen Java-Code (Kommentar /* JADX ERROR: ... */). In solchen Fällen wechsle in die Smali-Ansicht: Rechtsklick auf die Klasse → Show Smali. Smali ist der Assembler-Code von Dalvik/ART, lesbarer als Hex, aber näher an der Maschine als Java.

Wichtige Smali-Opcodes für BLE-Analyse:

invoke-virtual {v0, v1}, Ljavax/crypto/Cipher;->doFinal([B)[B
# entspricht: cipher.doFinal(data)

iget-object v2, p0, Ljava/util/UUID;->mostSigBits:J
# entspricht: uuid.mostSigBits

Batch-Verarbeitung per CLI

JADX läuft auch ohne GUI. Das ist nützlich, wenn du mehrere APKs vergleichen oder Ergebnisse per grep weiterverarbeiten willst:

# Alle Klassen einer APK in ein Verzeichnis dekompilieren
jadx -d ./output/ target.apk

# Anschließend mit grep nach Krypto-Mustern suchen
grep -r "Cipher.getInstance\|SecretKeySpec\|MessageDigest" ./output/

# Nur Ressourcen dekompilieren (kein Java-Code)
jadx --no-src -d ./resources/ target.apk

# Mehrere APKs auf einmal
for apk in *.apk; do
    jadx -d "./decoded_${apk%.apk}/" "$apk"
done

Krypto-Muster gezielt suchen

Eine strukturierte Suchstrategie deckt systematisch alle kryptografischen Konstrukte auf:

# Im CLI-Output suchen (nach jadx -d output/)
grep -rn "Cipher.getInstance" output/sources/
grep -rn "SecretKeySpec\|IvParameterSpec" output/sources/
grep -rn "MessageDigest.getInstance" output/sources/
grep -rn "Mac.getInstance" output/sources/  # HMAC
grep -rn "KeyAgreement.getInstance" output/sources/  # ECDH/DH

Datenpfade von BLE-Writes zur Verschlüsselung verfolgen

Der entscheidende analytische Schritt: den Weg eines BLE-Befehls vom UI-Event bis zum tatsächlichen writeCharacteristic()-Aufruf nachverfolgen. Starte bei writeCharacteristic und arbeite rückwärts:

  1. X-Ref auf writeCharacteristic -- zeigt, welche Methode den Schreibvorgang auslöst
  2. Parameter der Methode -- enthält den Payload, oft als byte[]
  3. X-Ref auf die Payload-Methode -- zeigt, ob der Payload vorher verschlüsselt oder transformiert wird
  4. Verschlüsselungsaufruf -- Cipher.doFinal() oder ein JNI-Call an eine native Methode

Bei der LED-Brille führt dieser Pfad von BluetoothGattCharacteristic.writeCharacteristic() über eine Wrapper-Klasse zur Agreement.getEncryptData(), die den JNI-Call an libAES.so enthält. Drei Hops von der BLE-Schnittstelle zum Schlüssel.

Native Library Handoff erkennen

Der Übergang von Java zu nativem Code ist das Signal, JADX beiseite zu legen und Ghidra zu starten. Suche nach:

System.loadLibrary("AES");      // lädt libAES.so
System.loadLibrary("crypto");   // lädt libcrypto.so
native byte[] cipher(byte[] data, byte[] out);  // JNI-Deklaration

Wenn du System.loadLibrary() und eine zugehörige native-Methoden-Deklaration findest, liegt der eigentliche Code in der .so-Datei unter lib/arm64-v8a/. Diese Datei extrahierst du aus der APK (sie ist ein ZIP-Archiv) und übergibst sie an Ghidra.


Häufige Probleme

OutOfMemoryError beim Öffnen großer APKs

JADX startet standardmäßig mit begrenztem Heap. Bei sehr großen Apps (Facebook, Google-Apps, einige Hersteller-Bloatware) reicht das nicht. Erhöhe den Heap über die JVM-Optionen:

# Über JAVA_OPTS (Linux/macOS)
export JAVA_OPTS="-Xmx8g"
jadx-gui target.apk

# Alternativ: Heap direkt in der Startdatei anpassen
# Öffne bin/jadx-gui und ändere die DEFAULT_JVM_OPTS-Zeile

Spezifische Klassen können nicht dekompiliert werden

Manche Klassen enthalten Bytecode-Konstrukte, die JADX nicht sauber zurücktransformieren kann. Der Fehler erscheint als Kommentar im Code:

/* JADX ERROR: Method load error
   jadx.core.utils.exceptions.DecodeException: ... */

Lösung: In die Smali-Ansicht wechseln (Rechtsklick → Show Smali) oder die betreffende Datei mit apktool d target.apk entpacken und den Smali-Code direkt lesen.

Fehlende Ressourcen bei Split-APKs

Moderne Android-Apps werden als App Bundle (AAB) ausgeliefert, das der Play Store in mehrere Split-APKs aufteilt. Die Hauptdatei base.apk enthält nicht alle nativen Bibliotheken und Ressourcen — architekturspezifische Dateien liegen z. B. in split_config.arm64_v8a.apk.

# Alle Split-APKs vom Gerät extrahieren
adb shell pm path com.example.bleapp
# Gibt mehrere Pfade aus — alle mit adb pull herunterladen
adb pull /data/app/.../base.apk
adb pull /data/app/.../split_config.arm64_v8a.apk

# JADX kann alle APKs gleichzeitig öffnen:
# File → Open files → alle APKs auswählen

Klassen fehlen nach der Dekompilierung

Wenn eine Klasse im Code referenziert wird, aber nicht im Paketbaum erscheint, ist sie wahrscheinlich Teil einer externen AAR-Bibliothek, die nicht mitgeliefert wurde, oder sie liegt in einem der Split-APKs. Prüfe mit:

# Dekompilieren und im Output nach fehlenden Klassen suchen
jadx -d ./output/ target.apk
grep -rn "kryptografie\|UUID\|ble" ./output/sources/

JADX vs. apktool — wann welches?

JADX ist für Java-Code-Analyse. apktool ist für Ressourcen, AndroidManifest.xml und wenn du Smali-Code direkt patchen willst. Bei einer BLE-Sicherheitsanalyse startest du mit JADX, wechselst aber zu apktool, wenn du native Bibliotheken aus einem Split-APK brauchst oder Manifest-Berechtigungen prüfen willst, die JADX manchmal unvollständig darstellt.


Zusammenfassung

  • JADX-GUI ist der Einstiegspunkt jeder APK-Analyse: Java-Bytecode zu lesbarem Quellcode
  • Globale Suche (Strg+Shift+F) mit Regex findet UUIDs, Krypto-Klassen und BLE-Schreibpfade in Sekunden
  • System.loadLibrary() + native-Methode = Handoff an Ghidra für Phase 3
  • Smali-Ansicht rettet dich bei Dekompilierungsfehlern
  • CLI-Modus (jadx -d output/ target.apk) erlaubt Batch-Verarbeitung und Grep-Integration
  • Split-APKs immer gemeinsam öffnen — sonst fehlen native Bibliotheken

Wissens-Check