Legacy Support Team
Technisch

USB-Tastaturzustände auslesen: Ein Low-Level Linux-Abenteuer

7. Jan. 202512 Min. Lesezeit
USB-Tastatur und Linux-Terminal

Den Zustand aller Tasten auf einer USB-Tastatur auslesen: Ein Low-Level-Linux-Abenteuer

Haben Sie sich jemals gefragt, was unter der Haube passiert, wenn Sie eine Taste auf Ihrer USB-Tastatur drücken? Was wäre, wenn Sie einen Blick auf die Rohdaten werfen könnten, die von der Tastatur an Ihren Computer gesendet werden? In diesem Blogbeitrag tauchen wir tief in die Welt der USB-HID-Protokolle (Human Interface Device) ein und schreiben ein Low-Level-Linux-Programm, um den aktuellen Zustand aller Tasten auf einer USB-Tastatur auszulesen. Keine High-Level-Abstraktionen – nur direkter, ungefilterter Zugriff auf die Hardware.


Das Problem: Den Tastaturzustand ohne Ereignisse auslesen

Wenn Sie eine Taste auf Ihrer Tastatur drücken, verarbeitet das Betriebssystem dies als Ereignis. Diese Ereignisse sind für die meisten Anwendungen praktisch, aber was, wenn Sie den aktuellen Zustand aller Tasten wissen möchten – nicht nur derjenigen, die Ereignisse ausgelöst haben? Zum Beispiel:

  • Welche Tasten sind gerade gedrückt?
  • Was ist der Zustand der Modifikatortasten (Shift, Strg, Alt)?
  • Wie sieht es mit den LED-Zuständen aus (Feststelltaste, Num-Taste, Rollen-Taste)?

Dies ist eine häufige Herausforderung für:

  • Sicherheitsforscher, die Tastatureingaben analysieren.
  • Embedded-Entwickler, die USB-Geräte debuggen.
  • Neugierige Hacker, die verstehen möchten, wie USB-Tastaturen funktionieren.

Aber es gibt einen Haken: Wenn eine Taste gedrückt wurde, während das System ausgeschaltet war, generiert der Linux-Kernel kein Ereignis, es sei denn, eine andere Taste wird gedrückt. Das bedeutet, dass Sie sich nicht auf das Eingabesubsystem des Kernels verlassen können, um Tasten zu erkennen, die vor dem Systemstart gedrückt wurden. Um dies zu lösen, müssen wir das High-Level-Eingabesubsystem umgehen und direkt mit der USB-Tastatur auf der niedrigsten möglichen Ebene interagieren.


Die Lösung: Verwendung von `libusb` zur Abfrage der Tastatur

Wir verwenden die libusb-Bibliothek, um direkt mit der USB-Tastatur zu interagieren. Hier ist, was wir tun werden:

  1. Alle USB-Geräte auflisten, um Tastaturen zu finden.
  2. HID-Schnittstellen auf diesen Geräten identifizieren.
  3. Eine HID GET_REPORT-Anfrage senden, um den aktuellen Eingabebericht (Tastenzustände) abzurufen.
  4. Den Eingabebericht decodieren, um festzustellen, welche Tasten gedrückt sind.

Der Code: Den Tastenzustand auslesen

Unten finden Sie das C-Programm, das die ganze Arbeit erledigt. Es verwendet libusb, um mit USB-Geräten zu interagieren und den Eingabebericht für alle angeschlossenen Tastaturen abzurufen.

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

// HID GET_REPORT-Anfrage
#define HID_GET_REPORT 0x01
#define HID_REPORT_TYPE_INPUT 0x01

// Funktion, um zu überprüfen, ob ein Gerät eine HID-Tastatur ist
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, "Fehler beim Abrufen des Geräte-Deskriptors\n");
        return 0;
    }

    // Überprüfen, ob das Gerät ein HID-Gerät ist
    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, "Fehler beim Abrufen des Konfigurations-Deskriptors\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; // Dies ist ein HID-Gerät
                }
            }
        }
    
        libusb_free_config_descriptor(config);
    }
    
    return 0; // Kein HID-Gerät
}

// Funktion, um den Eingabebericht von einer HID-Tastatur abzurufen
void get_input_report(libusb_device_handle *handle) {
    unsigned char input_report[8]; // Die meisten Tastaturen verwenden 8-Byte-Eingabeberichte
    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, // Berichtstyp (Eingabe) und Berichts-ID (0)
        0, // Schnittstelle
        input_report,
        sizeof(input_report),
        1000 // Timeout in Millisekunden
    );

    if (ret < 0) {
        fprintf(stderr, "Fehler beim Abrufen des Eingabeberichts: %s\n", libusb_error_name(ret));
    } else {
        printf("Eingabebericht:\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;

    // libusb initialisieren
    ret = libusb_init(NULL);
    if (ret < 0) {
        fprintf(stderr, "Fehler beim Initialisieren von libusb: %s\n", libusb_error_name(ret));
        return 1;
    }
    
    // Liste der USB-Geräte abrufen
    count = libusb_get_device_list(NULL, &devices);
    if (count < 0) {
        fprintf(stderr, "Fehler beim Abrufen der Geräteliste: %s\n", libusb_error_name((int)count));
        libusb_exit(NULL);
        return 1;
    }
    
    // Alle Geräte durchlaufen
    for (ssize_t i = 0; i < count; i++) {
        libusb_device *device = devices[i];
    
        // Überprüfen, ob das Gerät eine HID-Tastatur ist
        if (is_hid_keyboard(device)) {
            struct libusb_device_descriptor desc;
            ret = libusb_get_device_descriptor(device, &desc);
            if (ret < 0) {
                fprintf(stderr, "Fehler beim Abrufen des Geräte-Deskriptors\n");
                continue;
            }
    
            printf("HID-Tastatur gefunden: %04x:%04x\n", desc.idVendor, desc.idProduct);
    
            // Gerät öffnen
            libusb_device_handle *handle;
            ret = libusb_open(device, &handle);
            if (ret < 0) {
                fprintf(stderr, "Fehler beim Öffnen des Geräts: %s\n", libusb_error_name(ret));
                continue;
            }
    
            // Kernel-Treiber abkoppeln (falls angeschlossen)
            if (libusb_kernel_driver_active(handle, 0) == 1) {
                ret = libusb_detach_kernel_driver(handle, 0);
                if (ret < 0) {
                    fprintf(stderr, "Fehler beim Abkoppeln des Kernel-Treibers: %s\n", libusb_error_name(ret));
                    libusb_close(handle);
                    continue;
                }
            }
    
            // Schnittstelle beanspruchen
            ret = libusb_claim_interface(handle, 0);
            if (ret < 0) {
                fprintf(stderr, "Fehler beim Beanspruchen der Schnittstelle: %s\n", libusb_error_name(ret));
                libusb_close(handle);
                continue;
            }
    
            // Eingabebericht abrufen
            get_input_report(handle);
    
            // Schnittstelle freigeben
            libusb_release_interface(handle, 0);
    
            // Kernel-Treiber wieder ankoppeln (falls abgekoppelt)
            libusb_attach_kernel_driver(handle, 0);
    
            // Gerät schließen
            libusb_close(handle);
        }
    }
    
    // Geräteliste freigeben
    libusb_free_device_list(devices, 1);
    
    // libusb aufräumen
    libusb_exit(NULL);
    
    return 0;
}

Wie es funktioniert

  1. Geräte-Enumeration:

    • Das Programm listet alle USB-Geräte auf und identifiziert HID-Tastaturen, indem es deren Schnittstellenklasse überprüft.
  2. Öffnen des Geräts:

    • Für jede HID-Tastatur öffnet das Programm das Gerät und koppelt den Kernel-Treiber ab (falls notwendig).
  3. Beanspruchen der Schnittstelle:

    • Das Programm beansprucht die HID-Schnittstelle, um direkt mit dem Gerät zu kommunizieren.
  4. Senden der `HID GET_REPORT`-Anfrage:

    • Das Programm sendet eine `GET_REPORT`-Anfrage, um den Eingabebericht abzurufen, der den aktuellen Zustand aller Tasten enthält.
  5. Decodieren des Eingabeberichts:

    • Der Eingabebericht wird im Hexadezimalformat ausgegeben. Jedes Byte entspricht einer bestimmten Taste oder einem Modifikator.

Das Programm ausführen

  1. Installieren Sie libusb:
sudo apt install libusb-1.0-0-dev
  1. Kompilieren Sie das Programm:
gcc -o dump_keys dump_keys.c -lusb-1.0
  1. Führen Sie das Programm mit Root-Rechten aus:
sudo ./dump_keys

Beispielausgabe

Für eine Tastatur, bei der die F9-Taste gedrückt ist, könnte die Ausgabe so aussehen:

HID-Tastatur gefunden: 046d:c31c
Eingabebericht:
00 00 42 00 00 00 00 00

Das bedeutet:

  • Keine Modifikatortasten sind gedrückt (`00`).
  • Die F9-Taste ist gedrückt (`42`).
  • Keine anderen Tasten sind gedrückt (`00 00 00 00 00`).

Warum das wichtig ist

Dieser Low-Level-Ansatz gibt Ihnen die vollständige Kontrolle über die USB-Tastatur und ermöglicht es Ihnen:

  • USB-Geräte zu debuggen.
  • Tastatureingaben für Sicherheitsforschung zu analysieren.
  • Benutzerdefinierte Tastatur-Firmware oder Treiber zu erstellen.

Nächste Schritte

  • Experimentieren Sie mit verschiedenen Tastaturen und beobachten Sie deren Eingabeberichte.
  • Erweitern Sie das Programm, um LED-Zustände zu decodieren oder mehrere Tastaturen gleichzeitig zu handhaben.
  • Tauchen Sie tiefer in die USB-HID-Spezifikation ein, um komplexere Geräte zu verstehen.

Viel Spaß beim Hacken! Lassen Sie uns wissen, wenn Sie Fragen haben oder weitere Hilfe benötigen. 🚀

Lassen Sie uns über Ihr Projekt sprechen

Egal, ob Sie Hilfe bei der Wartung, Updates oder der Planung für die Zukunft benötigen, wir sind hier, um zuzuhören und Ihnen zu helfen, wo immer wir können.