Zum Hauptinhalt springen

LED Brille - App-Analyse

APK Extraction

# Package finden
adb shell pm list packages | grep -i glass
# package:com.xl.ledglasses

# APK-Pfad ermitteln
adb shell pm path com.xl.ledglasses
# /data/app/~~abc123==/com.xl.ledglasses-xyz==/base.apk

# APK herunterladen
adb pull /data/app/.../base.apk ledglasses.apk

APK-Struktur

ledglasses.apk
├── AndroidManifest.xml
├── classes.dex
├── lib/
│ ├── arm64-v8a/
│ │ └── libAES.so ← Native Library!
│ └── armeabi-v7a/
│ └── libAES.so
├── res/
└── resources.arsc

Wichtiger Fund: Native Library libAES.so deutet auf AES-Verschlüsselung hin!

JADX-Analyse

BLE-Service Klasse

// BluetoothLEService.java
public class BluetoothLEService extends Service {

// Service UUID
public static final UUID SERVICE_UUID =
UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");

// Characteristics
public static final UUID CHAR_CONTROL =
UUID.fromString("d44bc439-abfd-45a2-b575-925416129600");
public static final UUID CHAR_DATA =
UUID.fromString("d44bc439-abfd-45a2-b575-92541612960a");
public static final UUID CHAR_NOTIFY =
UUID.fromString("d44bc439-abfd-45a2-b575-925416129601");

public void sendCommand(byte[] data) {
// Verschlüsselung vor dem Senden!
byte[] encrypted = AESCipher.encrypt(data);

BluetoothGattCharacteristic char =
mGatt.getService(SERVICE_UUID)
.getCharacteristic(CHAR_CONTROL);
char.setValue(encrypted);
mGatt.writeCharacteristic(char);
}
}

AES Cipher Wrapper

// AESCipher.java
public class AESCipher {

static {
System.loadLibrary("AES"); // Lädt libAES.so
}

// Native Methode - Implementation in libAES.so!
public static native byte[] encrypt(byte[] data);
public static native byte[] decrypt(byte[] data);
}

Problem: Der eigentliche Verschlüsselungscode und der Key sind in der nativen Library versteckt!

Command-Struktur

Aus der App-Analyse konnten wir die Command-Struktur rekonstruieren:

// CommandBuilder.java
public class CommandBuilder {

// ATATS - Text senden initiieren
public byte[] buildATATS(int textLength) {
byte[] cmd = new byte[16];
cmd[0] = 7; // Length
cmd[1] = 'A'; // 0x41
cmd[2] = 'T'; // 0x54
cmd[3] = 'A'; // 0x41
cmd[4] = 'T'; // 0x54
cmd[5] = 'S'; // 0x53
cmd[6] = 1; // Parameter
cmd[7] = (byte)(textLength >> 8); // High byte
cmd[8] = (byte)(textLength & 0xFF); // Low byte
return AESCipher.encrypt(cmd);
}

// ATCP - Übertragung abschließen
public byte[] buildATCP() {
byte[] cmd = new byte[16];
cmd[0] = 5; // Length
cmd[1] = 'A'; // 0x41
cmd[2] = 'T'; // 0x54
cmd[3] = 'C'; // 0x43
cmd[4] = 'P'; // 0x50
return AESCipher.encrypt(cmd);
}

// MODE - Display-Modus setzen
public byte[] buildMODE(int mode, int direction) {
byte[] cmd = new byte[16];
cmd[0] = 6; // Length
cmd[1] = 'M'; // 0x4D
cmd[2] = 'O'; // 0x4F
cmd[3] = 'D'; // 0x44
cmd[4] = 'E'; // 0x45
cmd[5] = (byte)mode;
cmd[6] = (byte)direction;
return AESCipher.encrypt(cmd);
}
}

Identifizierte Commands

CommandBytesBeschreibung
ATATS07 41 54 41 54 53 01 HH LLStart Text-Übertragung, HH/LL = Länge
ATCP05 41 54 43 50Complete - Übertragung abschließen
MODE06 4D 4F 44 45 MM DDDisplay-Modus setzen
DATALL [data...]Datenpaket, LL = Länge im Paket

Display-Modi

// Aus strings.xml und Code-Analyse
MODE_STATIC = 1; // Statische Anzeige
MODE_SCROLL = 2; // Scroll-Modus
DIRECTION_LEFT = 0;
DIRECTION_RIGHT = 1;
DIRECTION_DOWN = 2; // Vertikal (falls unterstützt)

Font-Daten

Die App enthält eine Bitmap-Font für die LED-Matrix:

// FontData.java (rekonstruiert)
public class FontData {
// Jeder Buchstabe als Bitmap für 8x? LED Matrix
// Format: Spaltenweise, LSB = oberste LED

public static final byte[] CHAR_H = {
0x7F, // ███████
0x08, // ___█___
0x08, // ___█___
0x08, // ___█___
0x7F, // ███████
0x00 // Spacing
};

public static final byte[] CHAR_E = {
0x7F, // ███████
0x49, // █__█__█
0x49, // █__█__█
0x41, // █_____█
0x00 // Spacing
};
// ... weitere Zeichen
}

Zusammenfassung App-Analyse

FundDetailsNächster Schritt
UUIDsService 0xFFF0, 3 Characteristics✓ Dokumentiert
VerschlüsselungAES (native)→ Ghidra-Analyse
Command-FormatATATS, ATCP, MODE, DATA✓ Dokumentiert
Font-DatenBitmap pro Zeichen✓ Extrahiert
KeyIn libAES.so versteckt→ Ghidra-Analyse

Nächster Schritt

Weiter zur Crypto-Analyse um den AES-Key aus der nativen Library zu extrahieren.