IT security, FreeBSD, Linux, mail server hardening, post-quantum crypto, DNS, retro computing & hands-on hardware hacks. Privater Tech-Blog seit 2003.

Schlagwort: Reverse Engineering

Voltcraft CM 2016: Endlich eine Linux-GUI für das Ladegerät

Seit bestimmt zehn Jahren steht hier ein Voltcraft Charge Manager CM 2016 auf dem Schreibtisch. Irgendwann bei Conrad gekauft, als die tatsächlich noch Geschäfte in der Innenstadt hatten. Damals mit zwei kleinen Kindern war der Akkuverbrauch enorm. Spielzeugautos, Taschenlampen, Fernbedienungen, irgendwas war immer leer. Einwegbatterien waren teurer als heute (zumindest in meiner Erinnerung), und so wurde das Ladegerät schnell zum wichtigsten Gerät im Haushalt.

Das CM 2016 hat sechs unabhängige Ladeschächte (vier für AA/AAA, zwei für 9V-Blöcke) und kann deutlich mehr als nur Laden. Es misst Kapazitäten, erkennt defekte Akkus, kann Lade-/Entladezyklen fahren und hält Akkus per Trickle-Charge am Leben. Wer seine Akkus ernsthaft pflegen will, braucht so ein Gerät. Damit halten die Zellen länger, man kann sie wiederaufbereiten und erkennt rechtzeitig, wann einer reif für die Tonne ist.

Voltcraft Charge Manager CM 2016 Frontansicht mit Display und eingelegtem Akku
Der Voltcraft Charge Manager CM 2016 mit eingelegtem Akku

In den letzten Jahren wurde es zugegebenermaßen weniger eingesetzt. Trotzdem laufen noch immer diverse Smarthome-Geräte und Notfall-Taschenlampen mit Akkus, und da ist ein ordentliches Ladegerät einfach Pflicht.

Das Problem: kein Linux, kein gar nichts

Das CM 2016 kommt mit einer Windows-Software. CM2016 Logger V2.10, eine .NET-Anwendung von 2013. Unter Linux funktioniert die selbstverständlich nicht. Es gibt ein paar Projekte, die das Gerät auf der Kommandozeile auslesen können, ein Java-Tool hier, ein Python-Script dort. Eine echte GUI für Linux oder FreeBSD? Fehlanzeige. Selbst als ich vor ein paar Tagen noch einmal gesucht habe: nichts. Eine kommerzielle Java-GUI existiert zwar, aber nur als 30-Tage-Trial.

Also habe ich mich selbst daran gesetzt.

Das Ergebnis: eine native GTK4-Anwendung

Das Ergebnis ist eine vollständige Desktop-Anwendung in Python mit GTK4 und libadwaita. Quelloffen, MIT-lizenziert, auf GitHub:

https://github.com/Kernel-Error/voltcraft-cm2016

Voltcraft CM 2016 GUI: Hauptfenster mit Datentabelle und Live-Aufzeichnung
Hauptfenster mit Datentabelle während einer Live-Aufzeichnung

Die App erkennt das CM 2016 automatisch per USB (Silicon Labs CP210x Chip) und zeigt alle sechs Schächte in Echtzeit an. Alle zwei Sekunden kommen neue Messdaten rein: Spannung, Strom, Kapazität, Laufzeit, Programmstatus.

Was die App kann

  • Echtzeit-Überwachung aller 6 Ladeschächte mit Autoscroll und Slot-Filterung
  • Spannungs- und Strom-Diagramme als Linien- oder Balkendiagramm mit Zeitfenster-Steuerung
  • Chart-Zoom per Maus-Drag, Scrollrad oder Tastatur, dazu Daten-Tooltips
  • Export als CSV oder Spreadsheet (.xlsx mit eingebetteten Diagrammen)
  • Druckfunktion für Messprotokolle im DIN A4 Querformat
  • Speichern und Laden von Aufzeichnungen (.cm2016 Dateien), inklusive Crash-Recovery über Temp-Dateien
  • Sleep-Inhibit: das System schläft nicht ein, solange aufgezeichnet wird
  • 7 Sprachen: Deutsch, Englisch, Französisch, Niederländisch, Italienisch, Spanisch, Polnisch
Voltcraft CM 2016 GUI: Spannungs- und Strom-Diagramme
Liniendiagramme für Spannung und Strom über die Zeit
Voltcraft CM 2016 GUI: Balkendiagramm
Balkendiagramm-Ansicht
Voltcraft CM 2016 GUI: Port-Auswahl Dialog
Port-Auswahl mit automatischer Geräteerkennung

Das Protokoll: nicht ganz so dokumentiert wie gedacht

Das CM 2016 sendet alle zwei Sekunden einen 127-Byte-Frame über die serielle Schnittstelle (19200 Baud, 8N1). Die ersten sieben Bytes sind immer CM2016 , dann folgen zehn Bytes Header und je 18 Bytes pro Ladeschacht.

Klingt einfach. Ist es auch, bis man die existierende Dokumentation mit echten Messdaten vergleicht. Dabei sind mir ein paar Dinge aufgefallen, die so nirgendwo korrekt dokumentiert waren:

  • Kapazitätsfelder sind 32-Bit Little-Endian, nicht 24-Bit wie es in mehreren Quellen steht. Charge-Capacity und Discharge-Capacity belegen jeweils vier Bytes.
  • Der dokumentierte Byte-Swap für Discharge-Capacity war falsch. Mehrere Referenzprojekte haben die Bytes in der falschen Reihenfolge gelesen, was zu absurden Kapazitätswerten geführt hat.
  • Die Header-Bytes 7-16 waren bisher größtenteils undokumentiert. Tatsächlich stecken da die Firmware-Version, die eingestellte Akku-Chemie (NiMH/NiZn), Temperaturdaten und ein Action-Counter drin. Alles Big-Endian, im Gegensatz zu den Slot-Daten, die Little-Endian sind.
  • Die Kapazitätsskalierung hängt vom Slot-Typ ab: Slots 1-4 (AA/AAA) teilen durch 100, die beiden 9V-Slots teilen durch 1000. Gleiches gilt für den Strom: AA/AAA-Slots durch 1000, 9V-Slots durch 10000.

Das alles musste mit dem echten Gerät verifiziert werden. Akku rein, verschiedene Programme durchlaufen lassen, Rohwerte mit dem Display vergleichen. Klassisches Reverse Engineering, nur eben mit Ladeströmen statt mit Netzwerkpaketen.

Technik unter der Haube

Die Anwendung nutzt GTK4 mit libadwaita für eine zeitgemäße GNOME-Oberfläche. Dark Mode funktioniert automatisch. Die Diagramme werden mit Cairo gerendert, Benachrichtigungen laufen über den libadwaita ToastOverlay. Für den seriellen Zugriff kommt pyserial zum Einsatz, die CP210x-Erkennung läuft über die USB Vendor/Product ID von Silicon Labs.

Der Spreadsheet-Export erzeugt .xlsx-Dateien mit eingebetteten Diagrammen über openpyxl. Die Druckfunktion generiert DIN A4 Querformat mit allen Slots und Diagrammen. Sessions lassen sich als .cm2016-Dateien speichern und wieder laden, und falls die Anwendung während einer Aufzeichnung abstürzt, gibt es eine automatische Recovery über Temp-Dateien.

Insgesamt 135 Unit-Tests decken Parser, Protokoll und die Export-Funktionen ab.

Installation

Die App braucht GTK 4.14+ und libadwaita 1.5+. Unter Debian/Ubuntu/Mint:

sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-4.0 gir1.2-adw-1
git clone https://github.com/Kernel-Error/voltcraft-cm2016.git
cd voltcraft-cm2016
python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install -e .

Unter Fedora:

sudo dnf install python3-gobject gtk4 libadwaita

Unter Arch:

sudo pacman -S python-gobject gtk4 libadwaita

Dann einfach cm2016 starten, USB-Kabel anschließen, fertig.

Ein .deb-Paket gibt es auf der GitHub Releases Seite. Ein Flatpak-Manifest liegt im Repository, die Einreichung bei Flathub ist eingereicht.

Falls jemand das gleiche Gerät hat und Fragen zur App oder zum Protokoll hat, einfach fragen.

Siehe auch: NB-2033-U: Reverse Engineering eines Fingerabdrucklesers für Linux

NEXT Biometrics NB-2033-U: Reverse Engineering eines Fingerabdrucklesers für Linux

Illustration eines USB-Fingerabdrucklesers mit Linux-Tux und USB-Protokollanalyse (Reverse Engineering NB-2033-U)

Im letzten Beitrag zum Thema hatte ich angekündigt, dass ich mir auch den NB-2033-U vornehmen will. Der steckt in einem zweiten Fujitsu Notebook hier, dem von meiner Tochter Maja. Gleicher Hersteller, gleiche Sensorfamilie, sollte ähnlich laufen wie beim NB-2020-U. Dachte ich.

Falsch gedacht.

Hersteller sagt: geht nicht

Ich hatte bei NEXT Biometrics nach Protokolldokumentation oder einem SDK für den NB-2033-U gefragt. Kevin Hung, Director FAE, antwortete freundlich aber eindeutig:

„Both 2020-U and 2033-U have different firmware and USB stack. The code flow (libusb) related to 2033-U and 2020-U is different. This could be the reason for 2033-U failure/unsupported in linux. As of now, it is not supported.“

Kein SDK, keine Doku, kein Support. Und 74 Einträge auf linux-hardware.org mit Status „failed“ für die USB ID 298d:2033. Weltweit kein Linux-Support für dieses Gerät.

Gut. Dann eben Reverse Engineering.

Erster Versuch: Windows-Treiber belauschen

Plan A war klassisch: Windows-Treiber in einer VM laufen lassen, USB-Traffic mitschneiden. VirtualBox installiert, USB-Passthrough konfiguriert, Windows gestartet. Der Fingerabdruckleser tauchte im Gerätemanager auf. Mit Code 31. Treiber konnte das Gerät nicht starten. Secure Boot hatte VirtualBox den Kernel-Treiber nicht signiert, und der USB-Passthrough war damit unbrauchbar.

Plan A verworfen.

Plan B: Das SDK direkt auf Linux

Das SDK von NEXT Biometrics (libNBBiometrics.so) unterstützt den NB-2033-U intern. Es kommuniziert direkt über libusb, ohne Kernel-Treiber. Das heißt: ich kann das SDK-Sample direkt auf dem Linux-Notebook laufen lassen und gleichzeitig den USB-Traffic mit usbmon mitschneiden.

Dafür musste Secure Boot deaktiviert werden. usbmon ist ein Kernel-Modul, und lockdown=integrity (von Secure Boot gesetzt) blockiert es auch für root. Secure Boot im BIOS aus, lockdown=none in GRUB, Neustart. Danach:

modprobe usbmon
cat /sys/kernel/debug/usb/usbmon/3u > /tmp/capture.txt &
./NBBSample

7654 Zeilen USB-Traffic. Das komplette Protokoll des NB-2033-U, aufgezeichnet während einer Enrollment-Session.

Was dabei rauskam

Das Protokoll ist komplett anders als beim NB-1010-U/NB-2020-U. Kevins Aussage stimmte. Hier die wesentlichen Unterschiede:

EigenschaftNB-1010-U / NB-2020-UNB-2033-U
Bulk IN EndpointEP 3 (0x83)EP 1 (0x81)
Kommandoformat[0x80][CMD][SEQ][0x00]...[CMD][0x00][LEN_LO][LEN_HI][PAYLOAD] (TLV)
Finger-ErkennungEinzelnes 0x38Zwei 0x0D Config + 0x38
Bildübertragung90 Chunks à 540 Bytes180 Chunks à 268 Bytes
InitEinmal 0x07Zweimal 0x07 nötig

Gleicher Sensor-Die (256×180 Pixel, 385 DPI, aktiv thermisch), aber ein komplett anderer USB-Stack. Der NB-2033-U nutzt ein TLV-Format (Type-Length-Value) statt des festen Kommandoschemas vom NB-1010-U. Jedes Kommando hat eine eigene Längenangabe, und die Antworten sind anders strukturiert.

Die Kommandos im Detail

Aus dem USB-Capture konnte ich sechs Kommandos identifizieren:

  • 0x07 — Init/Wake. Muss zweimal gesendet werden, sonst reagiert der Sensor nicht.
  • 0x0D — Sensor-Konfiguration. Wird zweimal vor jeder Finger-Erkennung gebraucht, um den „Enhanced“ Modus zu aktivieren.
  • 0x38 — Finger-Erkennung. Byte 4 der Antwort ist der Detect-Level. Schwellwert 40.
  • 0x12 — Capture starten. Liefert 180 Zeilen à 256 Pixel, 8-Bit Graustufen.
  • 0x13 — Geräteinformationen (Hersteller, Modell, Seriennummer).
  • 0xF7 — Firmware-Version.

Thermischer Sensor: Eigenheiten

Der Sensor misst Temperaturänderungen, nicht statischen Kontakt. Das klingt nach einem Detail, ist aber für die Treiber-Implementierung entscheidend. Finger auflegen erzeugt einen kurzen Spike im Detect-Wert (10 bis 50+). Finger bleibt liegen, und der Wert fällt zurück auf Basisniveau. Der Treiber muss also den Spike erkennen, nicht einen dauerhaften Zustand.

Dazu kommt: Nach dem Init gibt es transiente Spikes, die ungefähr 1,5 Sekunden brauchen, bis sie abklingen. Ohne Settle-Pause nach dem Init erkennt der Treiber Phantom-Finger.

Der Treiber

Rausgekommen ist nb2033.c, ein eigenständiger libfprint-Treiber mit rund 350 Zeilen. Kein proprietärer Code, keine SDK-Abhängigkeit. Das SDK diente nur als Referenz für die Capture-Analyse, der Treiber ist sauber von Grund auf geschrieben. Lizenz: LGPL 2.1+ wie alle libfprint-Treiber.

Die State Machine:

  1. Init (0x07 × 2) mit 1,5 Sekunden Settle-Pause
  2. Finger-Detect-Polling (0x0D + 0x0D + 0x38, Schwellwert 40)
  3. Pre-Capture Config (0x0D)
  4. Capture (0x12) mit 150 ms Pause, dann 180 Zeilen lesen
  5. Bild an libfprint übergeben

Test

Getestet auf Majas Fujitsu Notebook mit Linux Mint 22.3:

$ fprintd-enroll
Using device /net/reactivated/Fprint/Device/0
Enrolling right-index-finger finger.
Enroll result: enroll-stage-passed
[... 5/5 Stages ...]
Enroll result: enroll-completed
$ fprintd-verify
Using device /net/reactivated/Fprint/Device/0
Listing enrolled fingers:
 - #0: right-index-finger
Verify result: verify-match (true)

Richtiger Finger: Match. Falscher Finger: No Match. Enrollment sauber, Verifikation zuverlässig.

Upstream

Der Merge Request ist eingereicht: MR !574 bei libfprint. Fünf Dateien: der neue Treiber, meson.build, autosuspend.hwdb und die Allowlist. CI läuft durch. Der verwandte MR !569 für den NB-2020-U ist noch in Review.

Für die Wiki-Aktualisierung (das Gerät von der „unsupported“ Liste nehmen) gibt es Issue #134.

Fazit

Der Hersteller sagt „not supported“, 74 Linux-User melden „failed“, und trotzdem war das an einem Nachmittag erledigt. SDK auf Linux ausführen, USB-Traffic mitschneiden, Protokoll rekonstruieren, Treiber schreiben, testen, upstream einreichen. Alles mit Open-Source-Tools: usbmon, libusb, libfprint.

Das Ergebnis: Majas Notebook hat jetzt einen funktionierenden Fingerabdruckleser unter Linux. Und sobald der Merge Request durch ist, haben ihn alle anderen auch.

Wie immer: Bei Fragen, fragen.

© 2026 -=Kernel-Error=-RSS

Theme von Anders NorénHoch ↑