Transformer son Rapsberry Pi Zero W en souris programmable
Je dispose d’un Rapsberry Pi Zero W v1.1 qui s’ennuie, et qui a l’intérêt de pouvoir se transformer en HID, c’est à dire de se faire passer pour un clavier ou une souris. Et justement, j’ai besoin d’automatiser des clics (pour un jeu qui demande beaucoup trop de tâches répétitives à mon goût). Seulement voilà, les devs du jeu sont malins et empêchent les clics simulés depuis l’OS. Donc on va simuler des clics de souris via le Pi comme si c’était une vraie souris !
OS
Niveau OS, j’ai d’abord testé P4wnP1 A.L.O.A.. C’est un distribution qui permet entre autre de faire ça, mais bien plus encore. Malheureusement elle n’a pas été mise à jour depuis plusieurs années, et les clics ne sont pas détectés correctement par les nouvelles versions de MacOS. Et je n’ai pas trouvé comment passer outre ce problème.
On va donc repartir de la base, via l’installation d’une Raspberry Pi OS Lite (32-bit), facilitée par Raspberry Pi Imager qui fait tout pour nous. Bien penser à configurer le wifi et un utilisateur avant de flasher la carte SD, de cette manière l’image flashée est directement connectée à votre réseau et accessible une fois bootée au bout de quelques minutes en SSH. De mon côté la version de l’OS est Raspbian GNU/Linux 12 (bookworm)
Avant le premier boot, monter la carte SD et modifier les fichiers suivants de la partition de boot :
Modifier la fin du fichier config.txt pour qu’il ne contienne plus que ceci :
[cm5]
[all]
dtoverlay=dwc2
Ajouter au fichier cmdline.txt, sur la ligne existante, après ‘‘rootwait’’ le paramètre suivant :
modules-load=dwc2,libcomposite
Démarrage
Il faut relier le pi à votre ordinateur, en prenant soin :
- De brancher votre cable micro-usb sur le port USB. C’est important, c’est le seul à pouvoir emmettre les signaux pour le HID. L’autre port, PWR, ne fonctionnera pas.
- Il ne faut pas utiliser un cable OTG. Un cable classique, par exemple micro-usb vers USB-A fera l’affaire. Sur le mac il faut éventuellement un adapteur USB-A vers ESB-C, n’importe quel hub fera l’affaire.
- Le cable micro-usb doit supporter la data. Beaucoup de ces cables sont livrés pour recharger des appareils, et du coup ne transmettre que de l’électricité, pas les données, il nous faut transporter les deux !
Et zou, en branchant avec ce seul cable le Pi on peut démarrer et passer à la suite de la config.
Configuration
Connectez-vous en SSH sur le Pi, puis réaliser les opérations suivantes.
Créer ensuite un script pour démarrer la souris virtuelle. Chez moi ce sera dans mon dossier /home/olivier, à adapter :
hid_mouse.sh
1#!/bin/bash
2
3# Nettoyage si un gadget est déjà actif
4if [ -d /sys/kernel/config/usb_gadget/g1 ]; then
5 echo "" > /sys/kernel/config/usb_gadget/g1/UDC || true
6 rm -f /sys/kernel/config/usb_gadget/g1/configs/c.1/hid.usb0
7 rmdir /sys/kernel/config/usb_gadget/g1/functions/hid.usb0
8 rmdir /sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409
9 rmdir /sys/kernel/config/usb_gadget/g1/configs/c.1
10 rmdir /sys/kernel/config/usb_gadget/g1/strings/0x409
11 rmdir /sys/kernel/config/usb_gadget/g1
12fi
13
14# Création du gadget
15cd /sys/kernel/config/usb_gadget/
16mkdir -p g1
17cd g1
18
19# Identifiants USB génériques
20echo 0x1d6b > idVendor # Linux Foundation
21echo 0x0104 > idProduct # Multifunction Composite Gadget
22echo 0x0100 > bcdDevice
23echo 0x0200 > bcdUSB
24
25# Chaînes d'identification
26mkdir -p strings/0x409
27echo "fedcba9876543210" > strings/0x409/serialnumber
28echo "PiZero" > strings/0x409/manufacturer
29echo "USB Mouse" > strings/0x409/product
30
31# Configuration unique
32mkdir -p configs/c.1
33mkdir -p configs/c.1/strings/0x409
34echo "HID Mouse Config" > configs/c.1/strings/0x409/configuration
35echo 250 > configs/c.1/MaxPower
36
37# Création de la fonction HID souris
38mkdir -p functions/hid.usb0
39echo 1 > functions/hid.usb0/protocol # 1 = souris
40echo 1 > functions/hid.usb0/subclass # 2 = boot interface
41echo 8 > functions/hid.usb0/report_length
42
43# Descripteur HID souris 3 boutons, X/Y, Défilement vertical (molette)
44echo -ne '\x05\x01\x09\x02\xa1\x01\x09\x01\xa1\x00\x05\x09\x19\x01\x29\x03\x15\x00\x25\x01\x95\x03\x75\x01\x81\x02\x95\x01\x75\x05\x81\x03\x05\x01\x09\x30\x09\x31\x09\x38\x15\x81\x25\x7f\x75\x08\x95\x03\x81\x06\xc0\xc0' > functions/hid.usb0/report_desc
45
46# Lier la fonction à la config
47ln -s functions/hid.usb0 configs/c.1/
48
49# Activer le périphérique (récupérer nom UDC dispo)
50UDC_NAME=$(ls /sys/class/udc | head -n 1)
51echo "$UDC_NAME" > UDC
52
53# On active le fait que n'importe quel user puisse écrire sur le device, ça évitera d'executer les scripts en root
54chmod 666 /dev/hidg0
Le rendre executable
1chmod +x /home/olivier/hid_mouse.sh
Créer un fichier de démarrage pour systemd :
1sudo nano /etc/systemd/system/hidmouse.service
Et y coller les éléments suivants :
1[Unit]
2Description=USB HID Mouse Gadget Setup
3After=network.target sysinit.target
4DefaultDependencies=no
5
6[Service]
7Type=oneshot
8ExecStart=/home/olivier/hid_mouse.sh
9RemainAfterExit=true
10
11[Install]
12WantedBy=multi-user.target
Activer le service :
1sudo systemctl daemon-reexec
2sudo systemctl daemon-reload
3sudo systemctl enable hidmouse.service
Démarrer et vérifier que tout fonctionne correctement :
1sudo systemctl start hidmouse.service
2systemctl status hidmouse.service
Contrôlons la souris
Normalement à cette étape, ou après un redémarrage, votre Mac a du vous demander d’accepter votre nouvelle souris. Vérifions son fonctionnement en envoyant un clic souris depuis le pi.
1sleep 5
2echo -ne '\x01\x00\x00\x00\x00\x00\x00\x00' > /dev/hidg0
3sleep 0.1
4echo -ne '\x00\x00\x00\x00\x00\x00\x00\x00' > /dev/hidg0
Créons un web service pour cliquer à distance
Nous avons d’abord besoin de la librairie flask :
1sudo apt update
2sudo apt upgrade -y
3sudo apt install python3-flask -y
Puis créer un petit script python pour exposer le web service. Deux méthodes disponibles, l’une pour envoyer des scrolls, l’autre pour cliquer. Dans le fichier hid_ws.py :
1import os
2import time
3from flask import Flask, request, jsonify
4app = Flask(__name__)
5
6# A get method that takes as parameters the number of clicks
7@app.route('/click', methods=['GET'])
8def get_clicks():
9 num_clicks = request.args.get('n', default=1, type=int)
10 click_mouse(num_clicks)
11 return jsonify({'status': 'success', 'clicks': num_clicks})
12
13def click_mouse(num_clicks):
14 for _ in range(num_clicks):
15 # Write the click command to /dev/hidg0
16 with open('/dev/hidg0', 'wb') as f:
17 f.write(b'\x01\x00\x00\x00\x00\x00\x00\x00')
18 time.sleep(0.1)
19 f.write(b'\x00\x00\x00\x00\x00\x00\x00\x00')
20 time.sleep(0.3)
21
22def scroll_mouse(steps):
23 # Write the scroll command to /dev/hidg0
24 with open('/dev/hidg0', 'wb') as f:
25 # Turn steps into a byte representation
26 step_bytes = steps.to_bytes(1, byteorder='little', signed=True)
27 # Write the scroll command with direction
28 f.write(b'\x00\x00\x00' + step_bytes + b'\x00\x00\x00\x00')
29 time.sleep(0.1)
30 f.write(b'\x00\x00\x00\x00\x00\x00\x00\x00')
31 time.sleep(0.3)
32
33@app.route('/scroll', methods=['GET'])
34def get_scroll():
35 steps = request.args.get('steps', default=1, type=int)
36 scroll_mouse(steps)
37 return jsonify({'status': 'success', 'steps': steps})
38
39@app.route('/')
40def index():
41 return "Mouse control service is running. Use /click or /scroll endpoints."
42
43if __name__ == '__main__':
44 if not os.path.exists('/dev/hidg0'):
45 print("Error: /dev/hidg0 does not exist. Make sure the hidg kernel module is loaded.")
46 exit(1)
47 app.run(host='0.0.0.0', port=5000)
Là aussi créer un fichier systemd pour que le webservice se lance au démarrage du pi :
1sudo nano /etc/systemd/system/hidclick.service
Avec comme contenu :
1[Unit]
2Description=Python Web Service to simulate a click
3After=network.target hidmouse.service
4Requires=hidmouse.service
5
6[Service]
7ExecStart=/usr/bin/python3 /home/olivier/hid_ws.py
8Restart=always
9User=olivier
10WorkingDirectory=/home/olivier
11
12[Install]
13WantedBy=multi-user.target
Activer le service :
1sudo systemctl daemon-reexec
2sudo systemctl daemon-reload
3sudo systemctl enable hidclick.service
Verifier que ça fonctionne
Pour tester tout ceci, vous pouvez par exemple utiliser un terminal depuis votre mac qui attend 5 secondes puis clique, ce qui vous laisse le temps de positionner votre souris sur un élément à cliquer pour tester.
1sleep 5 && curl "http://192.168.0.13:5000/click"
Evidemment par la suite vous pouvez piloter votre souris depuis vos scripts pour tout automatiser sans que le programme cible puisse savoir si c’est une vraie ou fausse souris !