Écrire un pilote d'ordinateur portable pour le plaisir et le profit, ou comment s'engager dans le noyau même si vous n'êtes pas si intelligent

Où tout a commencé


Commençons par notre énoncé du problème. Nous avons 1 (un) ordinateur portable. Un nouvel ordinateur portable pour les joueurs. Avec un rétro-éclairage RVB sur son clavier. Cela ressemble à ceci:

image
Photo prise sur lenovo.com

Il y a aussi un programme installé sur cet ordinateur portable. C'est la chose qui contrôle notre rétro-éclairage.

Un problème - le programme fonctionne sous Windows, et nous voulons que tout fonctionne sur notre Linux préféré. Vous voulez que les LED clignotent et que ces jolies couleurs clignotent et ainsi de suite. Une question naturelle se pose, pouvons-nous faire tout cela sans rétro-ingénierie et sans écrire nos propres pilotes?

Une réponse naturelle se pose, non. Ouvrons IDA et commençons à craquer.



Étape 1 - Creuser dans le code


Il y a trois déclencheurs qui font briller le rétroéclairage. Par ordre croissant de difficulté:

1. Grand programme de joueur de buff appelé Lenovo Nerve Center, qui a un menu de configuration de gros buff juste pour ce rétro-éclairage.

2. Combinaison de touches de raccourci Fn + Espace - éventuellement gérée par le programme susmentionné également.

3. BIOS. Pendant le chargement de l'ordinateur portable, le rétroéclairage clignote en rouge pendant un bref instant. Peut-être que nous pouvons utiliser cela?

J'ai dû essayer tous les 3. Mais il n'y a eu de succès que le premier, alors parlons du premier ici.

Nous ouvrons le dossier avec notre programme:

dossier

Remarquez ici! Il existe une DLL avec un nom très intéressant - LedSettingsPlugin.dll. Serait-ce possible ...? Ouvrons-le dans IDA Pro et voyons par nous-mêmes.

moitié droite

Regardez la moitié droite de l'écran ici - donc, tant d'informations de débogage! Utilisons-le. Certaines chaînes ressemblent ici à des noms de fonction. Je me demande pourquoi ...

nom-de-fonction

Oh, ce sont des noms de fonction. Pratique! Appelons certaines choses par leurs propres noms, puis regardons à nouveau la liste des fonctions. Pour nommer des choses dans IDA, vous pouvez utiliser le raccourci clavier N, ou simplement cliquer avec le bouton droit sur la chose que vous voulez nommer.

setledstatusex

En regardant les fonctions, nous voyons quelque chose appelé Y720LedSetHelper :: SetLEDStatusEx. On dirait ce dont nous avons besoin! Il forme une sorte de chaîne, puis la donne à quelque chose appelé CHidDevHelper :: HidRequestsByPath. La partie intéressante que nous devrions examiner est var_38, mise en évidence avec amour par l'IDA.

Var_34 est également intéressant. Il va juste après var_38 - dans les traditions des assembleurs, les variables sont stockées dans l'ordre inverse sous le RSP. Var_34 dans IDA n'est que le nom de la constante - -34h.

À partir de var_38, notre programme met un zéro, puis le style, la couleur, le numéro trois et le bloc - une partie du clavier qui gagnera cette couleur. (Plus tard, les expériences montreront que le numéro trois est en fait la luminosité. L'ajout d'un contrôle qui rendra notre pilote encore meilleur que le pilote officiel!)

Maintenant, plongeons-nous dans HidRequestsByPath et essayons de comprendre ce que nous devons envoyer et où.

devhelper

Regardez, deux fonctions. HidD_GetFeature et HidD_SetFeature.

Ils ne sont pas suivis dans le fichier ... mais sont très bien suivis dans la documentation officielle de Microsoft - ici et ici .

Eh bien, nous pouvons nous tapoter le dos maintenant. C'est le fond du rocher, il n'y a nulle part où creuser plus profondément. Linux a ces deux fonctions - nous revenons simplement en arrière et les appelons avec les mêmes arguments. ... non?

Étape 2 - lancement du prototype ...?



Pas exactement. Revenons à Linux et ouvrons lsusb. Nous y trouverons notre clavier et nous lui enverrons quelque chose.

lsusb

Integrated Technology Express, Inc. est le plus intéressant ici. Utilisons l'outil populaire / dev / hidraw. Nous pouvons trouver le bon juste en regardant le / sys / class / hidraw / hidraw * / device / uevent correspondant.

cacher

Celui-là. Tous les chiffres correspondent - cela signifie que / dev / hidraw0 est notre appareil. Mais essayons d'envoyer quelque chose ... et rien ne se passe! Pourquoi. À ce stade, l'épuisement professionnel entre en jeu. Peut-être que ce n'est pas pour les simples mortels, ce truc d'ingénierie inverse?

Mais continuons d'essayer. Si l'auteur avait été plus doué dans ce domaine, il aurait inversé le centre nerveux un peu plus et aurait trouvé une solution ... Mais nous n'avons pas de cerveau. Redémarrons sous Windows, il y a une idée.

Il y a cette chose dans Windows - Gestionnaire de périphériques, ils l'appellent. A beaucoup de fonctions - mais nous sommes intéressés par un. Il vous permet de désactiver les appareils. Simple et autoritaire.

Désactivons donc tous les appareils jusqu'à ce que notre LED cesse de clignoter!

désactiver l'appareil

Ça l'a fait. Si nous jetons un œil à son ID matériel - nous verrons ce que c'est, ce mystérieux appareil LED.

notre-appareil

Regardez - c'est lui.

dmesg

L'appareil qui harcèle mon dmesg depuis plusieurs années.

[Pourquoi n'a-t-il pas été affiché dans lsusb? Ce n'est pas du tout USB, bien sûr! Le rétroéclairage utilise un protocole appelé I2C HID - il permet au fabricant de cacher l'appareil aux mains des bricoleurs maléfiques d' installer des gadgets HID sur le bus système de l'ordinateur lui-même.]

Sidestep 2.5 - faisons un commit


Cette recherche du bon appareil a été fortement abrégée. Dans le récit d'origine, j'ai dû ouvrir ma configuration presque jusqu'au noyau avant de comprendre pourquoi rien ne fonctionnait. De plus, je n'ai pas apprécié que ce journal dmesg se présente à moi chaque fois que je déverrouille mon ordinateur. Puisque nous sommes ici - pourquoi ne pas écrire un court commit?

Nous devons trouver deux choses - l'endroit où les périphériques I2C HID sont manipulés et l'endroit où leurs bizarreries sont stockées. Ici. Ne réfléchissons pas trop et regardons l'erreur: rapport incomplet. Faisons-le complet.

mauvaise taille d'entrée

Ajoutez une nouvelle bizarrerie, appelons-la I2C_HID_QUIRK_BAD_INPUT_SIZE ou quelque chose comme ça. Pour garder la thématique.

bizarrerie

Ajoutons également notre appareil à la liste des excentriques. Ce que nous avons fait jusqu'à présent:

1. Recherche "i2c hid linux kernel" dans un moteur de recherche. Cliqué sur le 4e résultat dans DuckDuckGo.

2. A écrit trois (!) Mots en anglais - BAD_INPUT_SIZE

3. Ajout d'un au BIT (4). Résultat - BIT (5).

4. Ajout d'un numéro à hid-ids.h - l'ID de notre appareil (non illustré, mais il est similaire en difficulté)

Nous sommes programmeurs, faisons de la programmation maintenant.

Voir la ligne:
ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;

Et puis, il se plaint que ret_size n'est pas ce qu'il est.

Chaque fois que notre bizarrerie est active, faisons-le mais à l'envers.

si-condition

Envoyez le patch à la liste de diffusion ... (testez-le au préalable!). Pour être honnête, entrer dans cette liste de diffusion était plus difficile que le correctif réel. Ce n'est pas simple du tout.

appliqué

Voilà!

Étape 3 - chauffeur!


À ce stade, je me suis souvenu - j'essayais d'écrire un pilote. J'ai décidé de ne pas encore commettre cela pour le noyau - des questions ont commencé à se poser, à ce stade, vous devez vraiment y penser. Si quelqu'un qui lit ceci est bon en noyau et peut me consulter, je serais heureux de parler.

Ouvrons python. (Autrefois bash, mais vous changez d'avis très rapidement avec ce genre de langage). Utilisons l'outil populaire / dev / hidraw.

/ dev / hidraw0, / dev / hidraw1 - comment fonctionnent ces fichiers? Pour les E / S simples, vous pouvez les utiliser comme n'importe quel autre, et il semble que cela fonctionnerait. Et GetFeature et SetFeature - eh bien, c'est une toute autre histoire.

StackOverflow (ou quelqu'un d'autre, je ne me souviens pas) m'a dit que je devrais examiner quelque chose appelé ioctl. C'est une façon spéciale de travailler avec des fichiers rares comme les appareils HID, les terminaux et autres choses dégoûtantes similaires.

Cela semble simple à première vue - vous lui donnez un descripteur de fichier ouvert (pas au niveau Python, au niveau du système d'exploitation! En savoir plus sur les poignées du système d'exploitation ici si vous voulez le savoir.), Un certain nombre et un tampon - et puis il le fait quelque chose avec ce tampon et vous le restitue. Je n'essaie pas d'être spirituel ici, c'est juste que cela dépend presque entièrement de l'implémentation. Voici un exemple: 0xC0114806. C'est quoi ça? C'est SetFeature. C'est aussi

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

Les 6 premiers signifient que le fichier sera ouvert à la fois en lecture et en écriture. Nous écrivons seulement, mais les docs ont dit 6 cela signifie que nous avons mis 6. Ord ('H') signifie HID. Parfois, cela représente d'autres choses, selon le fichier. Mais maintenant c'est HID.

0x06 est la commande elle-même. La sixième commande de HID est SetFeature, dont nous avons besoin. Et la dernière partie? La taille du tampon.

Il ne reste plus qu'à l'intégrer dans un appel ioctl et vous obtenez un pilote. Ça marche. .

Mot de l'auteur


J'espère que l'article a été une lecture intéressante. Même une lecture utile, peut-être. Certaines parties ont été omises ou raccourcies par souci de concision - un lecteur plus attentif pourrait remarquer que le pilote peut lire l'état actuel du clavier, et la fonction set_status a un deuxième appel ioctl qui n'était même pas mentionné dans le texte. Le développement matériel et le noyau Linux sont de grandes choses, et une histoire ne couvrira pas tout. Et à la fin de la journée, l'article ne traite peut-être pas de cela. Il s'agit peut-être simplement de savoir comment faire quelque chose de petit et de bon, pour l'open-source ou pour vous-même, n'est pas difficile du tout. Il vous suffit de trouver une semaine de soirées gratuites, du thé et un désir de fouiller dans le code.

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


All Articles