@Olivier's

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 :

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 !