Schreiben eines Laptop-Treibers für Spaß und Gewinn oder Festschreiben des Kernels, auch wenn Sie nicht so schlau sind

Wo alles begann


Beginnen wir mit unserer Problemstellung. Wir haben 1 (einen) Laptop. Ein neuer Gaming-Laptop. Mit etwas RGB-Hintergrundbeleuchtung auf der Tastatur. Es sieht so aus:

Bild
Foto von lenovo.com gemacht

Auf diesem Laptop ist auch ein Programm installiert. Das ist die Sache, die unsere Hintergrundbeleuchtung steuert.

Ein Problem - das Programm läuft unter Windows und wir möchten, dass alles auf unserem Lieblings-Linux funktioniert. Willst du, dass LEDs blinken und diese hübschen Farben blinken und so. Es stellt sich natürlich die Frage, ob wir das alles ohne Reverse Engineering und Schreiben unserer eigenen Treiber tun können.

Eine natürliche Antwort ergibt sich, nein. Lassen Sie uns IDA öffnen und loslegen.



Schritt 1 - Code einlesen


Es gibt drei Auslöser, die die Hintergrundbeleuchtung zum Leuchten bringen. In aufsteigender Reihenfolge:

1. Das große Buff-Gamer-Programm Lenovo Nerve Center bietet ein umfangreiches Buff-Setup-Menü nur für diese Hintergrundbeleuchtung.

2. Tastenkombination Fn + Leertaste - möglicherweise auch vom oben genannten Programm verwaltet.

3. BIOS. Während der Laptop geladen wird, blinkt die Hintergrundbeleuchtung für einen kurzen Moment rot. Vielleicht können wir das nutzen?

Ich musste alle 3 ausprobieren. Aber es gab nur einige Erfolge entlang der ersten, also lasst uns hier über die erste sprechen.

Wir öffnen den Ordner mit unserem Programm:

Ordner

Beachten Sie hier! Es gibt eine DLL mit einem sehr interessanten Namen - LedSettingsPlugin.dll. Könnte es sein ...? Öffnen wir es in IDA Pro und überzeugen uns selbst.

rechte hälfte

Schauen Sie sich hier die rechte Bildschirmhälfte an - so viele Debug-Informationen! Lass es uns benutzen. Einige Zeichenfolgen sehen hier wie Funktionsnamen aus. Ich frage mich, warum ...

name-of-func

Oh, das sind Funktionsnamen. Bequem! Nennen wir einige Dinge mit ihren eigenen Namen und schauen wir uns die Funktionsliste noch einmal an. Um Dinge in IDA zu benennen, können Sie den Hotkey N verwenden oder einfach mit der rechten Maustaste auf das Ding klicken, das Sie benennen möchten.

setledstatusex

Wenn wir uns Funktionen ansehen, sehen wir etwas mit der Bezeichnung Y720LedSetHelper :: SetLEDStatusEx. Sieht aus wie was wir brauchen! Es bildet eine Art Zeichenfolge und gibt sie dann an CHidDevHelper :: HidRequestsByPath weiter. Der interessante Teil, den wir betrachten sollten, ist var_38, der von IDA liebevoll hervorgehoben wird.

Auch Var_34 ist interessant. Es geht direkt nach var_38 - in Assemblertraditionen werden Variablen unter dem RSP in umgekehrter Reihenfolge gespeichert. Var_34 in IDA ist nur der Name der Konstante - -34h.

Ab var_38 setzt unser Programm eine Null, dann Stil, Farbe, die Nummer drei und den Block - Teil der Tastatur, der diese Farbe erhält. (Später werden die Experimente zeigen, dass die Nummer drei tatsächlich die Helligkeit ist. Wenn Sie eine Kontrolle hinzufügen, wird unser Fahrer noch besser als die offizielle sein!)

Lassen Sie uns nun einen tiefen Einblick in HidRequestsByPath nehmen und herausfinden, was und wohin gesendet werden muss.

Entwicklungshelfer

Schau, zwei Funktionen. HidD_GetFeature und HidD_SetFeature.

Sie sind nicht in der Datei erfasst ... aber in der offiziellen Microsoft-Dokumentation sehr gut erfasst - hier und hier .

Nun, wir können uns jetzt auf den Rücken klopfen. Dies ist der Tiefpunkt, es gibt keinen Ort, an dem man tiefer graben kann. Linux hat diese beiden Funktionen - wir gehen einfach zurück und rufen sie mit denselben Argumenten auf. ... richtig?

Schritt 2 - Starten des Prototyps ...?



Nicht genau. Kehren wir zu Linux zurück und öffnen lsusb. Dort finden wir unsere Tastatur und senden etwas an sie.

lsusb

Integrated Technology Express, Inc. ist das interessanteste hier. Verwenden wir das beliebte Tool / dev / hidraw. Wir können die richtige finden, indem wir uns die entsprechende Datei / sys / class / hidraw / hidraw * / device / uevent ansehen.

versteck dich

Dieser hier. Alle Ziffern stimmen überein - das heißt, / dev / hidraw0 ist unser Gerät. Aber lassen Sie uns versuchen, etwas zu senden ... und nichts passiert! Warum? An diesem Punkt setzt ein Burnout ein. Vielleicht ist es nichts für Sterbliche, diese Reverse Engineering-Sache?

Aber versuchen wir es weiter. Wäre der Autor besser darin, hätte er das Nervenzentrum noch mehr umgedreht und eine Lösung gefunden ... Aber wir haben kein Gehirn. Lassen Sie uns zu Windows neu starten, es gibt eine Idee.

Es gibt dieses Ding in Windows - Geräte-Manager, nennen sie es. Hat viele Funktionen - aber wir interessieren uns für eine. Hier können Sie Geräte deaktivieren. Einfach und verbindlich.

Also lassen Sie uns einfach alle Geräte deaktivieren, bis unsere LED nicht mehr blinkt!

Gerät deaktivieren

Das hat es geschafft. Wenn wir uns seine Hardware-ID ansehen, werden wir sehen, was es ist, dieses mysteriöse LED-Gerät.

Unser Gerät

Schau - er ist es.

dmesg

Das Gerät, das mein dmesg seit mehreren Jahren belästigt.

[Warum wurde es nicht in lsusb gezeigt? Es ist natürlich überhaupt kein USB! Die Hintergrundbeleuchtung verwendet ein Protokoll namens I2C HID - es ermöglicht dem Hersteller, das Gerät vor den Händen bösartiger Heimwerker zu verbergen und HID- Geräte auf dem Systembus des Computers selbst zu installieren.]

Sidestep 2.5 - Machen wir ein Commit


Diese Suche nach dem richtigen Gerät wurde stark verkürzt. Im ursprünglichen Bericht musste ich mein Setup fast bis zum Kernel öffnen, bevor mir klar wurde, warum nichts funktionierte. Außerdem hat mir dieses Dmesg-Protokoll nicht gefallen, wenn es mir jedes Mal angezeigt wird, wenn ich meinen Computer entsperre. Seit wir hier sind - warum schreibst du nicht ein kurzes Commit?

Wir müssen zwei Dinge finden - den Ort, an dem I2C-HID-Geräte gehandhabt werden, und den Ort, an dem ihre Macken aufbewahrt werden. Hier. Überlegen wir nicht zu lange und schauen uns den Fehler an: unvollständiger Bericht. Lassen Sie es uns vervollständigen.

schlechte Eingabegröße

Fügen Sie einen neuen Quirk hinzu, nennen wir ihn I2C_HID_QUIRK_BAD_INPUT_SIZE oder so ähnlich. Um das Motto zu behalten.

schrullig

Fügen wir unser Gerät auch der Liste der skurrilen Geräte hinzu. Was wir bisher gemacht haben:

1. Sucht in einer Suchmaschine nach "i2c hid linux kernel". Klickte auf das 4. Ergebnis in DuckDuckGo.

2. Schrieb drei (!) Wörter in Englisch - BAD_INPUT_SIZE

3. BIT (4) um eins erweitert. Ergebnis - BIT (5).

4. HID-IDS.H wurde eine Nummer hinzugefügt - die ID unseres Geräts (nicht abgebildet, aber im Schwierigkeitsgrad ähnlich).

Wir sind Programmierer, lasst uns jetzt etwas programmieren.

Siehe die Zeile:
ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;

Und dann beschwert es sich, dass ret_size nicht das ist, was es ist.

Wann immer unsere Eigenart aktiv ist, machen wir das doch rückwärts.

if-Bedingung

Senden Sie den Patch an die Mailingliste ... (vorher testen!). Um ehrlich zu sein, war der Einstieg in diese Mailingliste schwieriger als der eigentliche Patch. Es ist keineswegs einfach.

angewendet

Das war's

Schritt 3 - Fahrer!


Zu diesem Zeitpunkt erinnerte ich mich - ich habe versucht, einen Treiber zu schreiben. Beschlossen, dass ich das noch nicht dem Kernel übergebe - es tauchten Fragen auf, an diesem Punkt muss man sich wirklich einige überlegen. Wenn jemand, der dies liest, gut im Kernel ist und mich konsultieren kann, würde ich mich über ein Gespräch freuen.

Lassen Sie uns Python öffnen. (War früher heftig, aber du änderst deine Meinung sehr schnell mit dieser Art von Sprache). Verwenden wir das beliebte Tool / dev / hidraw.

/ dev / hidraw0, / dev / hidraw1 - wie funktionieren diese Dateien überhaupt? Für einfache E / A können Sie sie wie jede andere verwenden, und es scheint, dass dies nur funktionieren würde. Und GetFeature und SetFeature - das ist eine ganz andere Geschichte.

StackOverflow (oder jemand anderes, an den ich mich nicht erinnern kann) sagte mir, ich müsste etwas untersuchen, das als ioctl bezeichnet wird. Es ist eine besondere Art, mit ungewöhnlichen Dateien wie HID-Geräten, Terminals und ähnlichen abscheulichen Dingen zu arbeiten.

Auf den ersten Blick scheint es einfach zu sein - Sie geben ihm ein offenes Datei-Handle (nicht Python-Level, OS-Level! Lesen Sie hier mehr über OS-Handles , wenn Sie wissen möchten.), Eine Zahl und einen Puffer - und dann tut es das etwas mit diesem Puffer und gibt es Ihnen zurück. Ich versuche hier nicht witzig zu sein, es ist nur so, dass es fast vollständig von der Implementierung abhängt. Hier ist ein Beispiel: 0xC0114806. Was ist das denn Das ist SetFeature. Das ist auch so

(6 << 29) | (ord('H') << 8) | (0x06 << 0) | (0x11 << 16).

Die ersten 6 bedeuten, dass die Datei sowohl zum Lesen als auch zum Schreiben geöffnet ist. Wir schreiben nur, aber in den Dokumenten steht 6, was bedeutet, dass wir 6 setzen. Ord ('H') steht für HID. Manchmal steht es für andere Dinge, abhängig von der Datei. Aber jetzt ist es versteckt.

0x06 ist der Befehl selbst. Der sechste Befehl von HID ist SetFeature, den wir brauchen. Und der letzte Teil? Die Größe des Puffers.

Alles, was bleibt, ist, es zu einem ioctl-Aufruf zusammenzufügen, und Sie erhalten einen Treiber. Es funktioniert .

Nachwort des Autors


Hoffe, der Artikel war eine interessante Lektüre. Möglicherweise sogar eine nützliche Lektüre. Einige Teile wurden der Kürze halber weggelassen oder gekürzt - ein aufmerksamerer Leser könnte feststellen, dass der Treiber den aktuellen Status der Tastatur lesen kann, und die Funktion set_status verfügt über einen zweiten ioctl-Aufruf, der im Text nicht einmal erwähnt wurde. Hardwareentwicklung und der Linux-Kernel sind große Dinge, und eine Geschichte wird nicht alles abdecken. Und am Ende des Tages geht es in dem Artikel vielleicht nicht darum. Vielleicht geht es nur darum, wie es überhaupt nicht schwer ist, etwas Kleines und Gutes für Open Source oder für sich selbst zu tun. Sie müssen nur eine Woche lang freie Abende, etwas Tee und den Wunsch haben, sich im Code zurechtzufinden.

Source: https://habr.com/ru/post/de484752/


All Articles