Zum Inhalt springen
>_marc-oltersdorf.de

Canyon ECLIPS: App zeigt „Firmware aktuell“, obwohl 0.1.2 da ist – Ursache und Workaround

Marc Oltersdorf 3 Min. Lesezeit

Mein ECLIPS lief mit Firmware 0.1.0, die Canyon-App meldete „aktuell“, obwohl der Hersteller längst 0.1.2 bereitstellt. Im Update-Check der App steckt ein regulärer Ausdruck, der ein Versions-Suffix verlangt, das der echte Dateiname nicht hat. Mit einem kleinen Frida-Hook auf einem gerooteten Testhandy lief danach das normale Update der App durch.

$ git log --graph Der Weg zur Lösung

  1. Problem App meldet „aktuell“

    Das Gerät meldet FW 0.1.0, im Update-Speicher des Herstellers liegt 0.1.2.

  2. Versuch App dekompiliert

    Der Update-Check liest einen Pfad aus der Remote-Konfiguration und prüft den Dateinamen per regulärem Ausdruck.

  3. Erkenntnis Fehlendes Fragezeichen

    Die letzte Gruppe (?:\+[0-9]) ist Pflicht, der echte Dateiname hat aber kein „+Ziffer“.

  4. Irrweg Selbst flashen verworfen

    Ein Update per nRF Connect Device Manager wäre möglich gewesen, ich wollte aber den regulären Weg der App nutzen.

  5. Lösung Frida-Hook auf Pattern.compile

    Das Suffix wird optional, die App bietet das Update an, das Gerät meldet danach 0.1.2.

Am Gravelbike sitzt Canyons ECLIPS (Dynamo, Pufferakku, Licht vorne und hinten, USB). Das Gerät meldet über Bluetooth FW: 0.1.0, die Canyon-App (Android 8.5.0) zeigt „Firmware aktuell“ – dabei liegt im Update-Speicher des Herstellers längst 0.1.2. Wie die Bluetooth-Steuerung des ECLIPS grundsätzlich funktioniert, steht in meinem Beitrag zur Lichtsteuerung per Web Bluetooth. Mir ging es um das Laden: Mein ECLIPS brach das Laden über den Dynamo während der Fahrt immer wieder ab, und 0.1.2 bringt laut den Texten in der Firmware eine neue Steuerung der Schutzschaltung am Dynamo.

Das Problem

Die App holt den Pfad der neuesten Firmware aus ihrer Remote-Konfiguration (Schlüssel ios_power_supply_firmware_version_path, den auch die Android-App nutzt). Der Dateiname lautet CPS_v1_EE0006-0.1.2.zip. Bevor sie Versionen vergleicht, prüft sie den Namen gegen dieses Muster aus der dekompilierten App:

^CPS_v[0-9]_EE[0-9]{4}-[0-9]\.[0-9]\.[0-9](?:-rc[0-9]+)?(?:\+[0-9])

Die letzte Gruppe (?:\+[0-9]) hat kein ?. Nach der Version muss also ein „+Ziffer“ folgen, etwa 0.1.2+1. Der echte Name hat das nicht. Die App protokolliert intern „Received incorrect firmware name while checking updates“ und meldet: kein Update. Für den Nutzer sieht das aus wie „alles aktuell“.

Die Lösung

Ein Frida-Skript macht genau dieses Suffix optional, indem es Pattern.compile für die zwei betroffenen Muster abfängt. Download, Signaturprüfung und Übertragung laufen danach unverändert über die App.

Java.perform(function () {
    var Pattern = Java.use("java.util.regex.Pattern");
    var FIX = {
        "^CPS_v[0-9]_EE[0-9]{4}-[0-9]\\.[0-9]\\.[0-9](?:-rc[0-9]+)?(?:\\+[0-9])":
            "^CPS_v[0-9]_EE[0-9]{4}-[0-9]\\.[0-9]\\.[0-9](?:-rc[0-9]+)?(?:\\+[0-9])?",
        "[0-9]\\.[0-9]\\.[0-9](?:-rc[0-9]+)?(?:\\+[0-9])":
            "[0-9]\\.[0-9]\\.[0-9](?:-rc[0-9]+)?(?:\\+[0-9])?"
    };
    function fix(p) {
        if (p !== null && FIX.hasOwnProperty(p)) {
            console.log("[fw-fix] Muster korrigiert: " + p);
            return FIX[p];
        }
        return p;
    }
    Pattern.compile.overload("java.lang.String").implementation = function (p) {
        return this.compile(fix(p));
    };
    Pattern.compile.overload("java.lang.String", "int").implementation = function (p, f) {
        return this.compile(fix(p), f);
    };
    console.log("[fw-fix] aktiv - jetzt in der App die Firmware-Seite des ECLIPS oeffnen");
});

Auf einem gerooteten Android-Handy mit laufendem frida-server und angemeldeter Canyon-App hängt man das Skript an die laufende App:

frida -D geraet -p pid -l fw_update_fix.js -q -t inf

Dann in der App mit dem ECLIPS verbinden und die Firmware-Seite öffnen. Die Ausgabe des Hooks:

[fw-fix] aktiv - jetzt in der App die Firmware-Seite des ECLIPS oeffnen
[fw-fix] Muster korrigiert: ^CPS_v[0-9]_EE[0-9]{4}-[0-9]\.[0-9]\.[0-9](?:-rc[0-9]+)?(?:\+[0-9])
[fw-fix] Muster korrigiert: [0-9]\.[0-9]\.[0-9](?:-rc[0-9]+)?(?:\+[0-9])

Danach ließ sich das Update in der App starten. Anschließend meldete der ECLIPS über Bluetooth FW: 0.1.2 und weiterhin EEPROM_Ver: 0006.

Warum das funktioniert

Nach der Namensprüfung vergleicht die App die EEPROM-Kennung aus dem Dateinamen (0006) und die Version (0.1.2) mit dem, was das Gerät meldet. Der eigentliche Download prüft den Namen nicht noch einmal. Der Hook ändert also nur die eine fehlerhafte Prüfung. Die Firmware selbst bleibt das signierte Paket des Herstellers, und der Bootloader im ECLIPS (MCUboot) ist vom Hook nicht betroffen.

Stolperfallen

  • Der Hook wirkt nur, solange die App läuft. Wischt man sie aus der Übersicht, ist er weg und muss neu angehängt werden.
  • Andere Apps, die den ECLIPS per Bluetooth verbinden, vorher trennen, damit die Canyon-App ihn für sich hat.
  • Rad still, Akku ordentlich geladen, Handy nah am Rad.
  • Das Muster verlangt das Suffix auch im zweiten Ausdruck, der die Version herauszieht. Beide müssen geändert werden, sonst bleibt die Versionsnummer leer.

Hat dir das geholfen?

RSS abonnieren

$ ls ../ Passt dazu