Frida
Android-Apps zur Laufzeit instrumentieren und BLE-Calls abfangen
Lernziel
Nach dieser Seite verstehst du, was dynamische Instrumentierung bedeutet und warum sie statische Analyse (JADX, Ghidra) ergänzt statt ersetzt. Du kannst Frida und frida-server einrichten, einen JavaScript-Hook in eine laufende Android-App injizieren und Laufzeitwerte — etwa Schlüssel, Argumente und Rückgabewerte — gezielt abfangen. Als konkretes Beispiel hookst du Cipher.getInstance in der LED-Brille-App, um den verwendeten Kryptoalgorithmus zur Laufzeit zu beobachten.
Was ist Frida?
Frida ist ein dynamisches Instrumentierungs-Toolkit. Es erlaubt dir, in laufende Prozesse auf Android, iOS, Windows, macOS und Linux einzugreifen und Funktionen durch JavaScript-Hooks zu überwachen oder zu modifizieren — ohne die Original-App neu zu kompilieren oder zu patchen.
Wo statische Analyse (JADX, Ghidra) den Quellcode liest, beobachtet Frida den Code beim Ausführen. Das ist besonders dann wertvoll, wenn:
- Schlüssel erst zur Laufzeit berechnet werden (z. B. aus Geräte-ID und Seriennummer abgeleitet)
- Code durch Obfuskation oder Packer schwer lesbar ist
- du prüfen möchtest, ob ein bestimmter Code-Pfad tatsächlich durchlaufen wird
- eine Funktion Argumente oder Rückgabewerte hat, die du im statischen Code nicht sehen kannst
Frida ist kein Ersatz für statische Analyse
Dynamische und statische Analyse ergänzen sich. JADX zeigt die Struktur — Frida zeigt die tatsächlichen Werte zur Laufzeit. In der Praxis stellt statische Analyse die Hypothese auf ("hier wird AES verwendet"), und Frida verifiziert sie ("der Schlüssel ist 0x41424344...").
Installation
frida-tools auf dem Rechner
# Virtuelle Umgebung (empfohlen)
python3 -m venv frida-env
source frida-env/bin/activate
# Frida installieren
pip install frida-tools
# Version prüfen
frida --version
# Ausgabe: 16.x.xfrida-server auf Android
frida-server ist ein kleiner Daemon, der auf dem Android-Gerät läuft und mit dem frida-Client auf deinem Rechner kommuniziert. Er muss zur Frida-Version auf dem Rechner passen.
# Aktuelle Release-Version herausfinden
frida --version # z. B. 16.3.3
# frida-server herunterladen (ARM64 für die meisten Android-Geräte)
# https://github.com/frida/frida/releases
# Datei: frida-server-16.3.3-android-arm64.xz
xz -d frida-server-16.3.3-android-arm64.xz
# Auf das Gerät übertragen und ausführbar machen
adb push frida-server-16.3.3-android-arm64 /data/local/tmp/frida-server
adb shell chmod 755 /data/local/tmp/frida-server
# frida-server starten (Root erforderlich)
adb shell su -c "/data/local/tmp/frida-server &"
# Verbindung prüfen
frida-ps -U
# Listet alle laufenden Prozesse auf dem GerätRoot-Zugriff erforderlich
frida-server auf Android benötigt Root-Rechte. Das bedeutet entweder ein gerootetes Gerät (z. B. mit Magisk) oder ein Android-Emulator mit Root (z. B. Genymotion, Android Studio AVD mit userdebug-Image). Für echte Geräte ohne Root gibt es den frida-gadget-Ansatz, der eine modifizierte APK erfordert.
BLE-Beispiel: Cipher.getInstance in der LED-Brille hooken
Die LED-Brille-App ruft Cipher.getInstance("AES/ECB/NoPadding") auf — das hat die statische Analyse in JADX gezeigt. Mit Frida kannst du zur Laufzeit prüfen: Welcher Algorithmus wird tatsächlich angefordert? Und welche Schlüssel werden übergeben?
Schritt 1: App identifizieren
# Paketname der laufenden App finden
frida-ps -Ua
# Beispiel-Ausgabe:
# PID Name Identifier
# 4521 LED Brille com.pinkysinyeeho.funkyglassesplusSchritt 2: Hook-Skript schreiben
Erstelle eine Datei hook_cipher.js:
Java.perform(function () {
// javax.crypto.Cipher hooken
var Cipher = Java.use("javax.crypto.Cipher");
// getInstance interceptieren
Cipher.getInstance.overload("java.lang.String").implementation = function (transformation) {
console.log("[*] Cipher.getInstance aufgerufen");
console.log(" Transformation: " + transformation);
console.log(" Stack:\n" + Java.use("android.util.Log")
.getStackTraceString(Java.use("java.lang.Exception").$new()));
// Original-Funktion aufrufen (App weiterhin funktionsfähig)
return this.getInstance(transformation);
};
// SecretKeySpec hooken — zeigt den tatsächlichen Schlüssel
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
SecretKeySpec.$init.overload("[B", "java.lang.String").implementation = function (keyBytes, algorithm) {
var hex = Array.from(keyBytes)
.map(function (b) { return ("0" + (b & 0xff).toString(16)).slice(-2); })
.join("");
console.log("[*] SecretKeySpec erstellt");
console.log(" Algorithmus: " + algorithm);
console.log(" Schlüssel: " + hex);
return this.$init(keyBytes, algorithm);
};
});Schritt 3: Hook injizieren
# An laufende App anhängen (attach)
frida -U -n "LED Brille" -l hook_cipher.js
# Oder App beim Start hooken (spawn) — empfohlen, um frühe Initialisierung zu erfassen
frida -U -f com.pinkysinyeeho.funkyglassesplus -l hook_cipher.js --no-pauseJetzt die App normal benutzen — die Konsole zeigt jeden Cipher.getInstance-Aufruf mit Algorithmusname und, sobald eine Verbindung hergestellt wird, den tatsächlichen Schlüssel.
Spawn vs. Attach
| Modus | Befehl | Wann verwenden |
|---|---|---|
| Attach | frida -U -n "App Name" -l script.js | App läuft bereits, spätere Funktionsaufrufe interessieren |
| Spawn | frida -U -f com.example.app -l script.js --no-pause | App vom Start hooken, frühe Initialisierung beobachten |
Für BLE-Analysen ist Spawn in der Regel besser: Verbindungsaufbau und Schlüsselaustausch geschehen häufig beim App-Start, bevor du attach hättest.
Erweiterte Techniken
SSL Pinning umgehen
Apps mit SSL Pinning verwerfen TLS-Verbindungen, wenn das Zertifikat nicht exakt dem erwarteten entspricht. Frida kann die Prüfung zur Laufzeit deaktivieren.
Java.perform(function () {
// TrustManager mit leerem Validator ersetzen
var TrustManager = Java.registerClass({
name: "com.frida.TrustManager",
implements: [Java.use("javax.net.ssl.X509TrustManager")],
methods: {
checkClientTrusted: function (chain, authType) {},
checkServerTrusted: function (chain, authType) {},
getAcceptedIssuers: function () { return []; },
},
});
var SSLContext = Java.use("javax.net.ssl.SSLContext");
var ctx = SSLContext.getInstance("TLS");
ctx.init(null, [TrustManager.$new()], null);
SSLContext.getDefault.implementation = function () { return ctx; };
console.log("[+] SSL Pinning deaktiviert");
});objection: Frida-basiertes Pentest-Framework
objection baut auf Frida auf und bietet vorgefertigte Hooks für häufige Aufgaben: SSL-Pinning-Bypass, Root-Detection-Bypass, Dateisystem-Exploration. Statt Skripte von Hand zu schreiben, verwendest du objection -g com.example.app explore. Installieren mit: pip install objection.
Native JNI-Funktionen hooken
Wenn kritische Logik in einer .so-Bibliothek liegt (wie libAES.so der LED-Brille), hookst du auf Native-Ebene statt auf Java-Ebene.
// Native Funktion nach Export-Name hooken
var keyExpansion = Module.findExportByName(
"libAES.so",
"Java_csh_tiro_cc_aes_keyExpansionDefault"
);
if (keyExpansion !== null) {
Interceptor.attach(keyExpansion, {
onEnter: function (args) {
console.log("[*] keyExpansionDefault aufgerufen");
// args[0] = JNIEnv*, args[1] = jclass, args[2] = jbyteArray (Schlüssel)
var jenv = Java.vm.tryGetEnv();
if (jenv !== null) {
var keyArray = jenv.getByteArrayElements(args[2], null);
console.log(" Schlüssel-Bytes: " + keyArray);
}
},
onLeave: function (retval) {
console.log("[<] keyExpansionDefault Rückgabewert: " + retval);
},
});
}BLE GATT-Aufrufe tracen
Android verwendet BluetoothGatt.writeCharacteristic für ausgehende BLE-Schreibvorgänge und BluetoothGattCallback.onCharacteristicChanged für eingehende Notifications. Beide lassen sich mit Frida hooken.
Java.perform(function () {
var BluetoothGatt = Java.use("android.bluetooth.BluetoothGatt");
BluetoothGatt.writeCharacteristic.overload(
"android.bluetooth.BluetoothGattCharacteristic"
).implementation = function (characteristic) {
var value = characteristic.getValue();
var hex = Array.from(value)
.map(function (b) { return ("0" + (b & 0xff).toString(16)).slice(-2); })
.join(" ");
console.log("[>] writeCharacteristic");
console.log(" UUID: " + characteristic.getUuid().toString());
console.log(" Payload: " + hex);
return this.writeCharacteristic(characteristic);
};
// Notifications kommen über BluetoothGattCallback, nicht BluetoothGatt
var GattCallback = Java.use("android.bluetooth.BluetoothGattCallback");
GattCallback.onCharacteristicChanged.overload(
"android.bluetooth.BluetoothGatt",
"android.bluetooth.BluetoothGattCharacteristic"
).implementation = function (gatt, characteristic) {
var value = characteristic.getValue();
var hex = Array.from(value)
.map(function (b) { return ("0" + (b & 0xff).toString(16)).slice(-2); })
.join(" ");
console.log("[<] onCharacteristicChanged (Notification)");
console.log(" UUID: " + characteristic.getUuid().toString());
console.log(" Payload: " + hex);
return this.onCharacteristicChanged(gatt, characteristic);
};
});Damit siehst du jeden BLE-Write und jede Notification auf Android-API-Ebene. Wenn die App selbst verschlüsselt (wie die LED-Brille mit AES-ECB), hookst du zusätzlich die Cipher-Aufrufe weiter oben im Stack, um die Klartextdaten vor der Verschlüsselung abzufangen. Die Kombination beider Hooks zeigt Klartext und Chiffrat nebeneinander.
Bekannte Probleme
| Problem | Ursache | Lösung |
|---|---|---|
Unable to attach to remote frida-server | frida-server läuft nicht oder Version stimmt nicht überein | Version prüfen: adb shell /data/local/tmp/frida-server --version |
Failed to spawn: unable to find process | App-Paketname falsch oder App nicht installiert | Mit frida-ps -Uai alle installierten Apps auflisten |
Architecture mismatch | frida-server für falsche CPU-Architektur heruntergeladen | adb shell getprop ro.product.cpu.abi prüfen, passendes Binary wählen |
| App stürzt beim Hook ab | Hook-Skript hat Fehler oder Methoden-Signatur ist falsch | Methoden-Overloads mit Cipher.getInstance.overloads auflisten |
| SELinux blockiert frida-server | Enforcing-Modus auf ungepatchten Geräten | adb shell setenforce 0 (temporär, nur für Tests) |
Zusammenfassung
- Frida instrumentiert laufende Prozesse ohne APK-Modifikation — es zeigt Laufzeitwerte, die statische Analyse nicht sehen kann.
- Einrichtung:
pip install frida-toolsauf dem Rechner + passendes frida-server-Binary auf dem Gerät. - Der Kernbefehl für Analysen:
frida -U -f <paketname> -l script.js --no-pause(Spawn-Modus). - Für BLE:
BluetoothGatt.writeCharacteristicundBluetoothGattCallback.onCharacteristicChangedhooken, um gesendete und empfangene Payloads mitzulesen. - objection bietet vorgefertigte Hooks für SSL-Pinning-Bypass und weitere häufige Pentest-Aufgaben.