SteelSeries Rival 100 : reverse engineering d'un périphérique USB

La semaine dernière je me suis acheté (un peu sur un coup de tête, je l'avoue) une nouvelle souris : la SteelSeries Rival 100. Cette souris propose de nombreuses options de personnalisation, telles que la couleur du rétro-éclairage, ou la configuration de la sensibilité du capteur… le seul hic, c'est que ces options ne sont accessibles que via un petit logiciel, le SteelSeries Engine 3, qui est disponible uniquement sous Windows et Mac OS X.

Étant donné que mon PC tourne exclusivement sous Ubuntu et que je n'ai pas la moindre envie de démarrer une machine sous Windows à chaque fois que je souhaite configurer ma souris, je me suis lancé dans le reverse engineering de la souris afin de pouvoir développer un petit utilitaire pour la configurer directement sous Linux.

SteelSeries Rival 100 reverse engineering

Pour réaliser le reverse engineering de la souris, je vais avoir besoin :

  • d'une machine virtuelle sous Windows,
  • de Wireshark,
  • et d'une deuxième souris (ou de tout autre périphérique de pointage comme un touchpad ou un trackpoint).

Je vais me servir de la machine virtuelle afin d'y faire tourner l'utilitaire de configuration, et pendant que je jouerai avec les réglages, je capturerai le trafic sur le bus USB à l'aide de Wireshark afin, je l'espère, de pouvoir déterminer le protocole utilisé entre le SteelSeries Engine 3 et la souris.

Récupération d'informations sur le périphérique USB

La première étape est de recueillir quelques informations sur la souris. La commande lsusb devrait fournir un certain nombre de renseignements utiles :

$ lsusb
...
Bus 001 Device 022: ID 1038:1702 SteelSeries ApS

Ici, on apprend :

  • que la souris est connectée au bus USB numéro 1,
  • que le système lui a attribué le numéro de périhpérique 22,
  • que l'identifiant du fabriquant (vendor id) est 1038,
  • que l'identifiant de ce modèle de souris (product id) est 1702.

Ça fait déjà pas mal d'informations, mais on devrait pouvoir en dénicher quelques-unes supplémentaires dans les journaux du noyau Linux :

$ dmesg
...
[23803.145407] usb 1-1: new full-speed USB device number 22 using xhci_hcd
[23803.382869] usb 1-1: New USB device found, idVendor=1038, idProduct=1702
[23803.382871] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[23803.382873] usb 1-1: Product: SteelSeries Rival 100 Gaming Mouse
[23803.382874] usb 1-1: Manufacturer: SteelSeries
[23803.385604] hid-generic 0003:1038:1702.000D: hiddev0,hidraw6: USB HID v1.11 Device [SteelSeries SteelSeries Rival 100 Gaming Mouse] on usb-0000:00:14.0-1/input0
[23803.387969] input: SteelSeries SteelSeries Rival 100 Gaming Mouse as /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/0003:1038:1702.000E/input/input22
[23803.388128] hid-generic 0003:1038:1702.000E: input,hidraw7: USB HID v1.11 Mouse [SteelSeries SteelSeries Rival 100 Gaming Mouse] on usb-0000:00:14.0-1/input1

On retrouve ici la plupart des informations vues précédemment, mais les lignes "hid-generic" nous informent que la souris fournit deux interfaces implémentant la classe HID (Human Interface Device), ce qui est une très bonne nouvelle.

Les interfaces qui implémentent cette classe sont très simples à contrôler, car le noyau Linux nous fournit pour chacune d'entre elles un fichier dans /dev afin de communiquer avec ces interfaces sans avoir à se soucier des détails du protocole.

Ces deux lignes nous renseignent également sur le fait que ces deux interfaces sont liées aux fichiers hidraw6 et hidraw7 (dans /dev), ce que l'on peut facilement vérifier :

$ ls /dev/hidraw*
/dev/hidraw0  /dev/hidraw2  /dev/hidraw4  /dev/hidraw6
/dev/hidraw1  /dev/hidraw3  /dev/hidraw5  /dev/hidraw7

Préparation de la machine virtuelle sous Windows

Maintenant que nous sommes en possession de toutes les informations utiles pour identifier le périphérique, il est temps de passer à la préparation de la machine virtuelle. Dans mon cas je vais utiliser VirtualBox avec une machine virtuelle sous Windows 8.1 qui traîne sur mon disque.

NOTE : Pour ceux qui ne possèdent pas de machine virtuelle sous Window, il est possible de s'en procurer une très facilement. Dans le cadre du projet Modern IE, Microsoft fournit gratuitement des machines virtuelles sous Windows, prêtes à l'emploi.

La préparation de la VM est très simple, il suffit :

  • de télécharger / installer / démarrer la machine virtuelle,
  • de télécharger la version du logiciel SteelSeries Engine 3 adaptée à la version de Windows installée sur la machine virtuelle,
  • et de l'installer sur le Windows virtualisé.

Une fois ceci fait, il ne reste plus qu'à vérifier que tout fonctionne :

  • on connecte la souris à la VM en utilisant l'icône en bas à droite de la fenêtre,
  • on lance SteelSeries Engine 3
  • et on s'assure qu'il reconnait bien la souris.

On peut même tester quelques réglages, normalement tout devrait être fonctionnel.

NOTE : c'est à ce moment-là que la seconde souris devient nécessaire. La Rival 100 n'est en effet plus utilisable dès lors qu'elle se retrouve connectée à la machine virtuelle.
Connexion de la souris à la VM Windows

Monitoring du bus USB à l'aide de Wireshark

Pour commencer il faut s'assurer que le module usbmon du noyau est bien chargé, c'est lui qui va permettre à Wireshark de récupérer les trames qui passent sur le bus USB. On peut vérifier que le module est chargé avec la commande suivante :

$ lsmod | grep usbmon

Si une ligne similaire à celle-ci apparait, c'est que le module est chargé :

usbmon                 28672  0

sinon, il faut le charger en utilisant la commande modprobe (en root) :

# modprobe usbmon

Une fois que le module usbmon est chargé, il ne reste plus qu'à lancer Wireshark (en root) :

# wireshark
NOTE : Il n'est pas recommandé de lancer Wireshark en root. Vous vous ferez d'ailleurs enguirlander lors du démarrage de celui-ci s'il détecte qu'il a été lancé en root… Ceci dit, je n'ai pas envie de régler ses permissions correctement pour l'instant, donc je vais le faire quand même.

Maintenant que Wireshark est lancé, on va pouvoir commencer à travailler.

Pour commencer, il faut sélectionner l'interface à espionner. Dans le cas présent il s'agit de "usbmonX", où "X" est le numéro du bus USB sur lequel la souris est connectée. Dans mon cas elle est connectée au bus numéro 1, je vais donc sélectionner l'interface "usbmon1".

Il ne reste plus qu'à lancer la capture en cliquant sur le bouton "Start" (oui celui avec une icône verte en forme d'aileron de requin…).

Wireshark : sélection de l'interface

Une fois la capture lancée, la fenêtre de Wireshark va très vite se remplir de plein de lignes : il s'agit de l'ensemble du trafic circulant sur le bus USB de la machine. Seule une infime partie de tout ce trafic nous intéresse, il va donc falloir filtrer un peu tout ça. Pour ce faire on va entrer le filtre suivant dans le champ "Filter" qui se trouve en haut de la fenêtre :

usb.device_address == XXX

"XXX" est le numéro qui a été attribué au périphérique (dans mon cas il s'agit du numéro 22).

Il devrait à présent y avoir beaucoup moins de lignes affichées (sauf si vous avez fait bouger la souris depuis le début de la capture, auquel cas vous aurez déjà plein de lignes).

Wireshark : filtré par usb.device_address

Analyse du protocole de la souris

Maintenant que tout est prêt, l'analyse va pouvoir débuter. Pour commencer on va jouer avec un réglage qui devrait être facile à interpréter : la couleur du rétro-éclairage de la souris.

Dans l'interface du SteelSeries Engine 3, sélectionnons une couleur dont le code hexadécimal soit facile à repérer, par exemple "#112233" ou "#AABBCC"… Dans mon cas je choisis "#FF4411" par ce que c'est un orange que j'aime bien (no comment)…

Une fois le réglage validé (en cliquant sur le bouton "Fermer" de la fenêtre), la souris change instantanément de couleur, et deux nouvelles lignes apparaissent dans Wireshask :

  • La première provient de notre machine ("host") et se dirige vers la première interface de la souris (22.0),
  • et la seconde provient de la souris et est destinée au PC.
NOTE : Normalement, dans la colonne Protocol du premier paquet, le protocole indiqué devrait être USBHID. Si ce n'est pas le cas, il faut déconnecter puis reconnecter la souris à la Machine virtuelle. Cela permettra à Wireshark de capturer les trames qui lui indiqueront qu'il a à faire à un périphérique de classe HID.

À priori celle qui devrait nous intéresser est la première, et en fouillant un peu dans les données de la trame, on finit par tomber sur la ligne suivante :

Data Fragment: 0500FF44110000000000000000000000...

On y retrouve très clairement la couleur que nous avions sélectionnée précédemment. Pour s'assurer qu'il ne s'agit pas là d'une coïncidence, il est possible de réessayer avec une autre couleur, par exemple "#00FFFF".

Cette fois encore, la souris change de couleur (elle vire au bleu) et deux nouvelles trames apparaissent dans Wireshark. Dans la première d'entre elles, on retrouve une fois de plus notre couleur :

Data Fragment: 050000FFFF0000000000000000000000...

À ce stade, on peut conclure qu'il existe une commande 0x0500 qui prend en paramètre les trois canaux (rouge, vert et bleu) de la couleur souhaitée.

À présent essayons de changer la couleur à la main. Vous vous souvenez des fichiers que nous avions trouvés dans /dev au tout début de nos investigations ? Et bah c'est maintenant qu'ils vont nous être utiles.

  • Pour commencer, il faut ouvrir un terminal root (si vous êtes sous Ubuntu, vous pouvez utiliser la commande "sudo su").
  • Ensuite il suffit d'écrire les octets souhaités directement dans le premier des deux fichiers trouvés précédemment (c'est à dire /dev/hidraw6 dans mon cas). Par exemple, essayons d'allumer la souris en rouge ("#FF0000") :
echo -en "\x05\x00\xFF\x00\x00" > /dev/hidraw6"
NOTE : Il est possible que dans certains cas il faille répéter la commande précédente deux fois pour que cela fonctionne car pour une raison que j'ignore, les octets peuvent ne pas être directement écrits dans le fichier (ils restent dans le buffer d'écriture en attendant d'être effectivement écrits).

Et là, la souris devrait s'allumer en rouge. Si ce n'est pas le cas, essayez d'écrire dans un autre fichier (voire dans /dev/hidraw0 dans certains cas…).

Il ne reste plus qu'à répéter les opérations pour chacun des réglages présents dans l'utilitaire de configuration de SteelSeries pour trouver toutes les commandes existantes.

Reverse engineering de la souris SteelSeries Rival 100

Liste des commandes

Ci-dessous, un tableau récapitulatif des commandes que j'ai été en mesure de découvrir :

Nom Commande Paramètres Descriptions
set_sensitivity 0x03 <preset> <value> Définit la sensibilité du capteur
set_polling_rate 0x04 0x00 <rate> Définit la fréquence d'interrogation
set_color 0x05 0x00 <red> <green> <blue> Change la couleur de la souris
set_light_effect 0x07 0x00 <effect> Définit les effets lumineux (pulsation…)
save 0x09 - Sauvegarde les paramètres dans la souris
set_btn6_action 0x0B <action> Définit l'action du bouton sous la molette
??? 0x16 ??? Envoyée à la connexion de la souris, action indéterminée

set_sensitivity

La souris est capable de contenir deux pré-réglages de sensibilité que l'on peut alterner via le bouton situé sous la molette de la souris.

0x03 <preset> <value>

Paramètres :

  • preset : Numéro du pré-réglage à modifier
    • 0x01 : pré-réglage numéro 1
    • 0x02 : pré-réglage numéro 2
  • value : Sensibilité du capteur parmi les valeurs suivantes :
    • 0x08 : 250 DPI
    • 0x07 : 500 DPI
    • 0x06 : 1000 DPI (valeur par défaut du pré-réglage numéro 1)
    • 0x05 : 1250 DPI
    • 0x04 : 1500 DPI
    • 0x03 : 1750 DPI
    • 0x02 : 2000 DPI (valeur par défaut du pré-réglage numéro 2)
    • 0x01 : 4000 DPI

set_polling_rate

Définit la fréquence à laquelle la position de la souris sera lue par l'ordinateur. Plus cette valeur est élevée, plus la précision sera grande. Cependant, une valeur élevée signifie également plus de trafic sur le bus USB et une plus grande utilisation du CPU.

0x04 0x00 <rate>

Paramètres :

  • 0x00 : le premier paramètre n'est pas utilisé sur ce modèle et doit être défini à 0x00
  • rate : le taux de rafraichissement parmi les valeurs suivantes :
    • 0x04 : 125 Hz (8 ms)
    • 0x03 : 250 Hz (4 ms)
    • 0x02 : 500 Hz (2 ms)
    • 0x01 : 1000 Hz (1 ms, valeur par défaut)

set_color

Définit la couleur du rétro-éclairage de la souris.

0x05 0x00 <red> <green> <blue>

Paramètres :

  • 0x00 : le premier paramètre n'est pas utilisé sur ce modèle et doit être défini à 0x00
  • red: valeur du canal rouge de la couleur RGB (compris entre 0x00 et 0xFF)
  • green: valeur du canal vert de la couleur RGB (compris entre 0x00 et 0xFF)
  • blue: valeur du canal bleu de la couleur RGB (compris entre 0x00 et 0xFF)

set_light_effect

Configure un effet de lumière.

0x07 0x00 <effect>

Paramètres :

  • 0x00 : le premier paramètre n'est pas utilisé sur ce modèle et doit être défini à 0x00
  • effect : l'effet à appliquer :
    • 0x01 : statique
    • 0x02 : pulsation lente
    • 0x03 : pulsation moyenne (appelée « respiration » dans le SteelSeries Engine 3)
    • 0x04 : pulsation rapide

save

Sauvegarde les réglages actuels dans la mémoire interne de la souris. Si cette commande n'est pas appelée après avoir modifié les paramètres de la souris, celle-ci retournera à sa précédente configuration la prochaine fois qu'elle sera reconnectée à la machine.

0x09

set_btn6_action

Définit l'action que le bouton numéro 6 (numéroté comme tel dans SteelSeries Engine 3) doit effectuer.

0x0B <action>

Paramètres :

  • action : action du bouton :
    • 0x00 : permet le basculement entre les pré-réglages de sensibilité du capteur (valeur par défaut)
    • 0x01 : rend le bouton accessible par le système d'exploitation (reconnu en tant que "bouton 10" par xev)

Conclusion

Au final, le reverse engineering de cette souris s'est avéré beaucoup plus simple que je ne l'aurais pensé. Et maintenant que je suis en possession de toutes les informations nécessaires, je devrais être en mesure de développer assez rapidement un petit utilitaire en ligne de commande pour configurer ma souris (je le placerais sur Github lorsque je l'aurai terminé).

EDIT 16/04/2016: J'ai écrit le petit utilitaire en python, et comme promis il est disponible sur Github : github.com/flozz/rivalcfg.
Si vous souhaitez étudier le code du logiciel, je vous recommande de jeter un coup d'œil à la version 1.0.1 qui est la plus simple à comprendre.