Legacy Support Team
Tecnico

Analisi dello Stato dei Tasti di una Tastiera USB: Un'Avventura Linux a Basso Livello

7 Gen 202512 min di lettura
Tastiera USB e terminale Linux

Dumping dello Stato di Tutti i Tasti su una Tastiera USB: Un'Avventura a Basso Livello su Linux

Ti sei mai chiesto cosa succede sotto il cofano quando premi un tasto sulla tua tastiera USB? E se potessi dare un'occhiata ai dati grezzi inviati dalla tastiera al tuo computer? In questo post del blog, ci immergeremo nel mondo dei protocolli USB HID (Human Interface Device) e scriveremo un programma a basso livello su Linux per dumping dello stato corrente di tutti i tasti su una tastiera USB. Nessuna astrazione di alto livello—solo accesso diretto e non filtrato all'hardware.


Il Problema: Leggere lo Stato della Tastiera Senza Eventi

Quando premi un tasto sulla tastiera, il sistema operativo lo elabora come un evento. Questi eventi sono comodi per la maggior parte delle applicazioni, ma cosa succede se vuoi conoscere lo stato corrente di tutti i tasti—non solo quelli che hanno generato eventi? Ad esempio:

  • Quali tasti sono attualmente premuti?
  • Qual è lo stato dei tasti modificatori (Shift, Ctrl, Alt)?
  • E gli stati dei LED (Caps Lock, Num Lock, Scroll Lock)?

Questa è una sfida comune per:

  • Ricercatori di sicurezza che analizzano l'input della tastiera.
  • Sviluppatori di sistemi embedded che debuggano dispositivi USB.
  • Hacker curiosi che vogliono capire come funzionano le tastiere USB.

Ma c'è un problema: Se un tasto è stato premuto mentre il sistema era spento, il kernel Linux non genererà alcun evento a meno che non venga premuto un altro tasto. Questo significa che non puoi fare affidamento sul sottosistema di input del kernel per rilevare i tasti che sono stati premuti prima dell'avvio del sistema. Per risolvere questo problema, dobbiamo bypassare il sottosistema di input di alto livello e interagire direttamente con la tastiera USB al livello più basso possibile.


La Soluzione: Usare `libusb` per Interrogare la Tastiera

Useremo la libreria libusb per interagire direttamente con la tastiera USB. Ecco cosa faremo:

  1. Enumerare tutti i dispositivi USB per trovare le tastiere.
  2. Identificare le interfacce HID su quei dispositivi.
  3. Inviare una richiesta HID GET_REPORT per recuperare il report di input corrente (stato dei tasti).
  4. Decodificare il report di input per determinare quali tasti sono premuti.

Il Codice: Dumping dello Stato dei Tasti

Di seguito è riportato il programma in C che fa tutto il lavoro pesante. Utilizza libusb per interagire con i dispositivi USB e recupera il report di input per tutte le tastiere connesse.

#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>

// Richiesta HID GET_REPORT
#define HID_GET_REPORT 0x01
#define HID_REPORT_TYPE_INPUT 0x01

// Funzione per verificare se un dispositivo è una tastiera HID
int is_hid_keyboard(libusb_device *device) {
    struct libusb_device_descriptor desc;
    int ret = libusb_get_device_descriptor(device, &desc);
    if (ret < 0) {
        fprintf(stderr, "Errore nel recupero del descrittore del dispositivo\n");
        return 0;
    }

    // Verifica se il dispositivo è un dispositivo HID
    if (desc.bDeviceClass == LIBUSB_CLASS_PER_INTERFACE) {
        struct libusb_config_descriptor *config;
        ret = libusb_get_config_descriptor(device, 0, &config);
        if (ret < 0) {
            fprintf(stderr, "Errore nel recupero del descrittore di configurazione\n");
            return 0;
        }
    
        for (int i = 0; i < config->bNumInterfaces; i++) {
            const struct libusb_interface *interface = &config->interface[i];
            for (int j = 0; j < interface->num_altsetting; j++) {
                const struct libusb_interface_descriptor *iface_desc = &interface->altsetting[j];
                if (iface_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
                    libusb_free_config_descriptor(config);
                    return 1; // Questo è un dispositivo HID
                }
            }
        }
    
        libusb_free_config_descriptor(config);
    }
    
    return 0; // Non è un dispositivo HID
}

// Funzione per ottenere il report di input da una tastiera HID
void get_input_report(libusb_device_handle *handle) {
    unsigned char input_report[8]; // La maggior parte delle tastiere usa report di input di 8 byte
    int ret = libusb_control_transfer(
        handle,
        LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
        HID_GET_REPORT,
        (HID_REPORT_TYPE_INPUT << 8) | 0x00, // Tipo di Report (Input) e ID Report (0)
        0, // Interfaccia
        input_report,
        sizeof(input_report),
        1000 // Timeout in millisecondi
    );

    if (ret < 0) {
        fprintf(stderr, "Errore nel recupero del report di input: %s\n", libusb_error_name(ret));
    } else {
        printf("Report di Input:\n");
        for (int i = 0; i < ret; i++) {
            printf("%02x ", input_report[i]);
        }
        printf("\n");
    }
}

int main() {
    libusb_device **devices;
    ssize_t count;
    int ret;

    // Inizializzazione di libusb
    ret = libusb_init(NULL);
    if (ret < 0) {
        fprintf(stderr, "Errore nell'inizializzazione di libusb: %s\n", libusb_error_name(ret));
        return 1;
    }
    
    // Ottieni la lista dei dispositivi USB
    count = libusb_get_device_list(NULL, &devices);
    if (count < 0) {
        fprintf(stderr, "Errore nel recupero della lista dei dispositivi: %s\n", libusb_error_name((int)count));
        libusb_exit(NULL);
        return 1;
    }
    
    // Itera attraverso tutti i dispositivi
    for (ssize_t i = 0; i < count; i++) {
        libusb_device *device = devices[i];
    
        // Verifica se il dispositivo è una tastiera HID
        if (is_hid_keyboard(device)) {
            struct libusb_device_descriptor desc;
            ret = libusb_get_device_descriptor(device, &desc);
            if (ret < 0) {
                fprintf(stderr, "Errore nel recupero del descrittore del dispositivo\n");
                continue;
            }
    
            printf("Trovata tastiera HID: %04x:%04x\n", desc.idVendor, desc.idProduct);
    
            // Apri il dispositivo
            libusb_device_handle *handle;
            ret = libusb_open(device, &handle);
            if (ret < 0) {
                fprintf(stderr, "Errore nell'apertura del dispositivo: %s\n", libusb_error_name(ret));
                continue;
            }
    
            // Scollega il driver del kernel (se attaccato)
            if (libusb_kernel_driver_active(handle, 0) == 1) {
                ret = libusb_detach_kernel_driver(handle, 0);
                if (ret < 0) {
                    fprintf(stderr, "Errore nello scollegamento del driver del kernel: %s\n", libusb_error_name(ret));
                    libusb_close(handle);
                    continue;
                }
            }
    
            // Richiedi l'interfaccia
            ret = libusb_claim_interface(handle, 0);
            if (ret < 0) {
                fprintf(stderr, "Errore nella richiesta dell'interfaccia: %s\n", libusb_error_name(ret));
                libusb_close(handle);
                continue;
            }
    
            // Ottieni il report di input
            get_input_report(handle);
    
            // Rilascia l'interfaccia
            libusb_release_interface(handle, 0);
    
            // Ricollega il driver del kernel (se scollegato)
            libusb_attach_kernel_driver(handle, 0);
    
            // Chiudi il dispositivo
            libusb_close(handle);
        }
    }
    
    // Libera la lista dei dispositivi
    libusb_free_device_list(devices, 1);
    
    // Pulizia di libusb
    libusb_exit(NULL);
    
    return 0;
}

Come Funziona

  1. Enumerazione dei Dispositivi:

    • Il programma elenca tutti i dispositivi USB e identifica le tastiere HID controllando la loro classe di interfaccia.
  2. Apertura del Dispositivo:

    • Per ogni tastiera HID, il programma apre il dispositivo e scollega il driver del kernel (se necessario).
  3. Richiesta dell'Interfaccia:

    • Il programma richiede l'interfaccia HID per comunicare direttamente con il dispositivo.
  4. Invio di `HID GET_REPORT`:

    • Il programma invia una richiesta `GET_REPORT` per recuperare il report di input, che contiene lo stato corrente di tutti i tasti.
  5. Decodifica del Report di Input:

    • Il report di input viene stampato in formato esadecimale. Ogni byte corrisponde a un tasto specifico o a un modificatore.

Esecuzione del Programma

  1. Installa libusb:
sudo apt install libusb-1.0-0-dev
  1. Compila il programma:
gcc -o dump_keys dump_keys.c -lusb-1.0
  1. Esegui il programma con privilegi di root:
sudo ./dump_keys

Esempio di Output

Per una tastiera con il tasto F9 premuto, l'output potrebbe essere simile a questo:

Trovata tastiera HID: 046d:c31c
Report di Input:
00 00 42 00 00 00 00 00

Questo significa:

  • Nessun tasto modificatore è premuto (`00`).
  • Il tasto F9 è premuto (`42`).
  • Nessun altro tasto è premuto (`00 00 00 00 00`).

Perché Questo è Importante

Questo approccio a basso livello ti dà il controllo completo sulla tastiera USB, permettendoti di:

  • Debuggare dispositivi USB.
  • Analizzare l'input della tastiera per la ricerca sulla sicurezza.
  • Costruire firmware o driver personalizzati per tastiere.

Prossimi Passi

  • Sperimenta con diverse tastiere e osserva i loro report di input.
  • Estendi il programma per decodificare gli stati dei LED o gestire più tastiere contemporaneamente.
  • Immergiti più a fondo nella specifica USB HID per comprendere dispositivi più complessi.

Buon hacking! Facciamoci sapere se hai domande o hai bisogno di ulteriori assistenza. 🚀

Parliamo del Tuo Progetto

Che tu abbia bisogno di aiuto con la manutenzione, gli aggiornamenti o la pianificazione per il futuro, siamo qui per ascoltarti e aiutarti in ogni modo possibile.