Καταγραφή της Κατάστασης Πλήκτρων USB Πληκτρολογίου: Μια Περιπέτεια σε Χαμηλό Επίπεδο Linux

Αποθήκευση της Κατάστασης Όλων των Πλήκτρων σε ένα USB Πληκτρολόγιο: Μια Περιπέτεια σε Χαμηλό Επίπεδο στο Linux
Έχετε αναρωτηθεί ποτέ τι συμβαίνει κάτω από το καπό όταν πατάτε ένα πλήκτρο στο USB πληκτρολόγιό σας; Τι θα γινόταν αν μπορούσατε να ρίξετε μια ματιά στα ακατέργαστα δεδομένα που στέλνονται από το πληκτρολόγιο στον υπολογιστή σας; Σε αυτήν την ανάρτηση ιστολογίου, θα εμβαθύνουμε στον κόσμο των πρωτοκόλλων USB HID (Human Interface Device) και θα γράψουμε ένα πρόγραμμα σε χαμηλό επίπεδο στο Linux για να αποθηκεύσουμε την τρέχουσα κατάσταση όλων των πλήκτρων σε ένα USB πληκτρολόγιο. Χωρίς υψηλού επιπέδου αφαιρέσεις—μόνο ακατέργαστη, αφιλτράριστη πρόσβαση στο υλικό.
Το Πρόβλημα: Ανάγνωση της Κατάστασης του Πληκτρολογίου Χωρίς Συμβάντα
Όταν πατάτε ένα πλήκτρο στο πληκτρολόγιό σας, το λειτουργικό σύστημα το επεξεργάζεται ως ένα συμβάν. Αυτά τα συμβάντα είναι βολικά για τις περισσότερες εφαρμογές, αλλά τι γίνεται αν θέλετε να μάθετε την τρέχουσα κατάσταση όλων των πλήκτρων—όχι μόνο αυτών που προκάλεσαν συμβάντα; Για παράδειγμα:
- Ποια πλήκτρα είναι πατημένα αυτή τη στιγμή;
- Ποια είναι η κατάσταση των πλήκτρων τροποποιητών (Shift, Ctrl, Alt);
- Τι γίνεται με τις καταστάσεις των LED (Caps Lock, Num Lock, Scroll Lock);
Αυτή είναι μια κοινή πρόκληση για:
- Ερευνητές ασφαλείας που αναλύουν την είσοδο από το πληκτρολόγιο.
- Προγραμματιστές ενσωματωμένων συστημάτων που αποσφαλματώνουν συσκευές USB.
- Περίεργους χάκερς που θέλουν να καταλάβουν πώς λειτουργούν τα USB πληκτρολόγια.
Αλλά υπάρχει μια παγίδα: Αν ένα πλήκτρο ήταν πατημένο ενώ το σύστημα ήταν απενεργοποιημένο, ο πυρήνας του Linux δεν θα δημιουργήσει κανένα συμβάν εκτός αν πατηθεί ένα άλλο πλήκτρο. Αυτό σημαίνει ότι δεν μπορείτε να βασιστείτε στο υποσύστημα εισόδου του πυρήνα για να ανιχνεύσετε πλήκτρα που ήταν πατημένα πριν από την εκκίνηση του συστήματος. Για να λύσουμε αυτό το πρόβλημα, πρέπει να παρακάμψουμε το υποσύστημα εισόδου υψηλού επιπέδου και να αλληλεπιδράσουμε απευθείας με το USB πληκτρολόγιο στο χαμηλότερο δυνατό επίπεδο.
Η Λύση: Χρήση της libusb για την Ερώτηση του Πληκτρολογίου
Θα χρησιμοποιήσουμε τη βιβλιοθήκη libusb για να αλληλεπιδράσουμε απευθείας με το USB πληκτρολόγιο. Αυτό που θα κάνουμε:
- Απαρίθμηση όλων των συσκευών USB για να βρούμε πληκτρολόγια.
- Αναγνώριση διεπαφών HID σε αυτές τις συσκευές.
- Αποστολή μιας αίτησης
HID GET_REPORTγια να ανακτήσουμε την τρέχουσα αναφορά εισόδου (κατάσταση πλήκτρων). - Αποκωδικοποίηση της αναφοράς εισόδου για να προσδιορίσουμε ποια πλήκτρα είναι πατημένα.
Ο Κώδικας: Αποθήκευση των Καταστάσεων των Πλήκτρων
Παρακάτω είναι το πρόγραμμα σε C που κάνει όλη τη δουλειά. Χρησιμοποιεί τη libusb για να αλληλεπιδράσει με τις συσκευές USB και να ανακτήσει την αναφορά εισόδου για όλα τα συνδεδεμένα πληκτρολόγια.
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
// HID GET_REPORT request
#define HID_GET_REPORT 0x01
#define HID_REPORT_TYPE_INPUT 0x01
// Συνάρτηση για να ελέγξουμε αν μια συσκευή είναι ένα 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, "Failed to get device descriptor\n");
return 0;
}
// Έλεγχος αν η συσκευή είναι μια συσκευή 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, "Failed to get config descriptor\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; // Αυτή είναι μια συσκευή HID
}
}
}
libusb_free_config_descriptor(config);
}
return 0; // Δεν είναι μια συσκευή HID
}
// Συνάρτηση για να πάρουμε την αναφορά εισόδου από ένα HID πληκτρολόγιο
void get_input_report(libusb_device_handle *handle) {
unsigned char input_report[8]; // Τα περισσότερα πληκτρολόγια χρησιμοποιούν αναφορές εισόδου 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, // Τύπος Αναφοράς (Είσοδος) και Αναγνωριστικό Αναφοράς (0)
0, // Διεπαφή
input_report,
sizeof(input_report),
1000 // Χρονικό όριο σε χιλιοστά του δευτερολέπτου
);
if (ret < 0) {
fprintf(stderr, "Failed to get input report: %s\n", libusb_error_name(ret));
} else {
printf("Αναφορά Εισόδου:\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
ret = libusb_init(NULL);
if (ret < 0) {
fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_error_name(ret));
return 1;
}
// Λήψη της λίστας των συσκευών USB
count = libusb_get_device_list(NULL, &devices);
if (count < 0) {
fprintf(stderr, "Failed to get device list: %s\n", libusb_error_name((int)count));
libusb_exit(NULL);
return 1;
}
// Επανάληψη μέσα από όλες τις συσκευές
for (ssize_t i = 0; i < count; i++) {
libusb_device *device = devices[i];
// Έλεγχος αν η συσκευή είναι ένα HID πληκτρολόγιο
if (is_hid_keyboard(device)) {
struct libusb_device_descriptor desc;
ret = libusb_get_device_descriptor(device, &desc);
if (ret < 0) {
fprintf(stderr, "Failed to get device descriptor\n");
continue;
}
printf("Βρέθηκε HID πληκτρολόγιο: %04x:%04x\n", desc.idVendor, desc.idProduct);
// Άνοιγμα της συσκευής
libusb_device_handle *handle;
ret = libusb_open(device, &handle);
if (ret < 0) {
fprintf(stderr, "Failed to open device: %s\n", libusb_error_name(ret));
continue;
}
// Αποσύνδεση του οδηγού πυρήνα (αν είναι συνδεδεμένος)
if (libusb_kernel_driver_active(handle, 0) == 1) {
ret = libusb_detach_kernel_driver(handle, 0);
if (ret < 0) {
fprintf(stderr, "Failed to detach kernel driver: %s\n", libusb_error_name(ret));
libusb_close(handle);
continue;
}
}
// Δήλωση της διεπαφής
ret = libusb_claim_interface(handle, 0);
if (ret < 0) {
fprintf(stderr, "Failed to claim interface: %s\n", libusb_error_name(ret));
libusb_close(handle);
continue;
}
// Λήψη της αναφοράς εισόδου
get_input_report(handle);
// Απελευθέρωση της διεπαφής
libusb_release_interface(handle, 0);
// Επανασύνδεση του οδηγού πυρήνα (αν αποσυνδέθηκε)
libusb_attach_kernel_driver(handle, 0);
// Κλείσιμο της συσκευής
libusb_close(handle);
}
}
// Απελευθέρωση της λίστας συσκευών
libusb_free_device_list(devices, 1);
// Καθαρισμός της libusb
libusb_exit(NULL);
return 0;
}
Πώς Λειτουργεί
-
Απαρίθμηση Συσκευών:
- Το πρόγραμμα απαριθμεί όλες τις συσκευές USB και αναγνωρίζει τα HID πληκτρολόγια ελέγχοντας την κλάση διεπαφής τους.
-
Άνοιγμα της Συσκευής:
- Για κάθε HID πληκτρολόγιο, το πρόγραμμα ανοίγει τη συσκευή και αποσυνδέει τον οδηγό πυρήνα (αν είναι απαραίτητο).
-
Δήλωση της Διεπαφής:
- Το πρόγραμμα δηλώνει τη διεπαφή HID για να επικοινωνήσει απευθείας με τη συσκευή.
-
Αποστολή
HID GET_REPORT:- Το πρόγραμμα στέλνει μια αίτηση
GET_REPORTγια να ανακτήσει την αναφορά εισόδου, η οποία περιέχει την τρέχουσα κατάσταση όλων των πλήκτρων.
- Το πρόγραμμα στέλνει μια αίτηση
-
Αποκωδικοποίηση της Αναφοράς Εισόδου:
- Η αναφορά εισόδου εκτυπώνεται σε δεκαεξαδική μορφή. Κάθε byte αντιστοιχεί σε ένα συγκεκριμένο πλήκτρο ή τροποποιητή.
Εκτέλεση του Προγράμματος
- Εγκατάσταση της
libusb:
sudo apt install libusb-1.0-0-dev
- Μεταγλώττιση του προγράμματος:
gcc -o dump_keys dump_keys.c -lusb-1.0
- Εκτέλεση του προγράμματος με δικαιώματα root:
sudo ./dump_keys
Παράδειγμα Εξόδου
Για ένα πληκτρολόγιο με το πλήκτρο F9 πατημένο, η έξοδος μπορεί να μοιάζει με αυτό:
Βρέθηκε HID πληκτρολόγιο: 046d:c31c
Αναφορά Εισόδου:
00 00 42 00 00 00 00 00
Αυτό σημαίνει:
- Κανένα πλήκτρο τροποποιητή δεν είναι πατημένο (`00`).
- Το πλήκτρο F9 είναι πατημένο (`42`).
- Κανένα άλλο πλήκτρο δεν είναι πατημένο (`00 00 00 00 00`).
Γιατί Αυτό Είναι Σημαντικό
Αυτή η προσέγγιση σε χαμηλό επίπεδο σας δίνει πλήρη έλεγχο πάνω στο USB πληκτρολόγιο, επιτρέποντάς σας να:
- Αποσφαλματώνετε συσκευές USB.
- Αναλύετε την είσοδο από το πληκτρολόγιο για έρευνα ασφαλείας.
- Δημιουργήσετε προσαρμοσμένο υλικολογισμικό ή οδηγούς για πληκτρολόγια.
Επόμενα Βήματα
- Πειραματιστείτε με διαφορετικά πληκτρολόγια και παρατηρήστε τις αναφορές εισόδου τους.
- Επεκτείνετε το πρόγραμμα για να αποκωδικοποιήσετε τις καταστάσεις των LED ή να χειριστείτε πολλαπλά πληκτρολόγια ταυτόχρονα.
- Εμβαθύνετε στην προδιαγραφή USB HID για να καταλάβετε πιο σύνθετες συσκευές.
Καλή διασκέδαση! Ενημερώστε μας αν έχετε οποιεσδήποτε ερωτήσεις ή χρειάζεστε περαιτέρω βοήθεια. 🚀