Автор: Hcamael@knowchuangyu 404 лаборатория
Время: 27 февраля 2024 г.
В процессе использования Google для поиска соответствующих учебных материалов я нашел книгу - «Научимся играть по USB по кругу». Во время чтения я обнаружил, что мне необходимо приобрести соответствующее аппаратное оборудование.
Что касается разработки аппаратного обеспечения, мы рассмотрим возможность проведения обучения и исследований на более позднем этапе, а на раннем этапе мы рассмотрим возможность использования существующего оборудования для обучения и исследований. Первым решением было использование мобильного телефона в качестве USB-устройства для подключения к компьютеру. Однако после исследования выяснилось, что поддержка разработки USB-устройств в Android не очень удобна. Кроме того, разработка USB инкапсулирована на уровне Android. , что затруднит понимание основных деталей USB.
Впоследствии, учитывая, что у меня под рукой было устройство Raspberry Pi 4b, я решил попробовать использовать это устройство для разработки USB-устройств. Выполнив поиск в Google, я нашел проект под названием key-mime-pi, который послужил для меня отправной точкой.
1 Инструменты для разработки USB-устройств
Ссылки
После некоторых исследований, поскольку визуализация инструментов, связанных с USB, в Windows лучше, я наконец выбрал устройство Windows в качестве USB-хоста.
Первый инструмент — USB Tree View, который хорошо отображает статус главного-подчиненного дерева USB-устройств на хосте, информацию о дескрипторе USB и другие данные. Как показано на рисунке 1:
Рис. 1. Интерфейс USB Tree View
Как видно из дерева USB-устройств на рисунке выше, самым низким уровнем хоста является хост-контроллер USB, который напрямую подключен к ЦП. Затем верхний уровень подключается к корневому концентратору USB (HUB), который можно сравнить с док-станцией USB. Его функция заключается в расширении нескольких интерфейсов USB, но общая пропускная способность по-прежнему определяется хост-контроллером USB.
Например, на рисунке выше версия хост-контроллера USB — USB3.1, тогда корневой концентратор USB на верхнем уровне может расширять порты USB уровня USB3.1 и ниже, например порт USB2.0. Однако независимо от того, сколько портов USB будет расширено, общая пропускная способность будет зафиксирована на уровне 10 Гбит/с (поскольку пропускная способность USB3.1 составляет 10 Гбит/с). Кроме того, к концентратору можно подключить несколько концентраторов. Например, на рисунке выше показан универсальный USB-концентратор, который на самом деле представляет собой интерфейс USB, расширенный шасси на примере хоста.
Кроме того, нажмите на подключенное USB-устройство, чтобы просмотреть более подробную информацию о USB-устройстве, как показано на рисунке 2:
Рис. 2. Просмотр сведений об устройстве USB в дереве USB.
Если вы поняли информацию, содержащуюся в каждой части изображения выше, она будет объяснена позже. Однако этот инструмент также имеет некоторые ошибки. Инструмент не может нормально анализировать информацию HID, как показано на рисунке 3:
Рисунок 3: HID-дескриптор
После установки USBPcap на Wireshark в Windows он может захватывать USB-трафик на хост-контроллере. Например, на хосте есть три хост-контроллера, поэтому в Wireshark отображаются три USBPcap, как показано на рисунке 4.
Рисунок 4. Интерфейс Wireshark.
Если вы не знаете, какому хост-контроллеру принадлежит USB-порт, вставленный в настройки USB, вы можете использовать USBTree View для его проверки. Однако, поскольку Wireshark захватывает трафик на хост-контроллере, а хост-контроллер USB может подключаться к нескольким USB-устройствам, когда я хочу изучить определенное USB-устройство, мне нужно использовать выражение фильтра Wireshark для фильтрации трафика хост-контроллера. другие USB-устройства фильтруются.
Bus Hound
Можно захватить указанныйUSBТрафик устройства,Однако недостатком этого инструмента является то, что он стоит денег.,В противном случае размер и количество перехваченных пакетов ограничены.,Взломанная версия этого ПО пока не найдена. На рисунке 5 показан интерфейс программного обеспечения:
Рисунок 5: Интерфейс Bus Hound
2 Использование Raspberry Pi 4b в качестве USB-клавиатуры для ПК
Ссылки
Следующий,читаяkey-mime-pi
Исходный код проекта,Я обнаружил, что эмулировать USB-устройство с помощью устройства Raspberry Pi 4b очень легко. По коду проекта,Его можно разбить на следующие два этапа:
1.Нужно включитьdwc2водить машину:на малине пиconfig.txtДобавитьdtoverlay=dwc2
,После запуска устройства,Проверьте, включен ли автозапуск:
$ lsmod|grep dwc2
dwc2 196608 0
2.Запустите, как показано нижеbash
Скрипт:
#!/usr/bin/env bash
# Adapted from https://github.com/girst/hardpass-sendHID/blob/master/README.md
# Exit on first error.
set -e
# Treat undefined environment variables as errors.
set -u
modprobe libcomposite
cd /sys/kernel/config/usb_gadget/
mkdir -p g1
cd g1
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
STRINGS_DIR="strings/0x409"
mkdir -p "$STRINGS_DIR"
echo "6b65796d696d6570690" > "${STRINGS_DIR}/serialnumber"
echo "keymimepi" > "${STRINGS_DIR}/manufacturer"
echo "Generic USB Keyboard" > "${STRINGS_DIR}/product"
FUNCTIONS_DIR="functions/hid.usb0"
mkdir -p "$FUNCTIONS_DIR"
echo 1 > "${FUNCTIONS_DIR}/protocol" # Keyboard
echo 0 > "${FUNCTIONS_DIR}/subclass" # No subclass
echo 8 > "${FUNCTIONS_DIR}/report_length"
# Write the report descriptor
# Source: https://www.kernel.org/doc/html/latest/usb/gadget_hid.html
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > "${FUNCTIONS_DIR}/report_desc"
CONFIG_INDEX=1
CONFIGS_DIR="configs/c.${CONFIG_INDEX}"
mkdir -p "$CONFIGS_DIR"
echo 250 > "${CONFIGS_DIR}/MaxPower"
CONFIGS_STRINGS_DIR="${CONFIGS_DIR}/strings/0x409"
mkdir -p "$CONFIGS_STRINGS_DIR"
echo "Config ${CONFIG_INDEX}: ECM network" > "${CONFIGS_STRINGS_DIR}/configuration"
ln -s "$FUNCTIONS_DIR" "${CONFIGS_DIR}/"
ls /sys/class/udc > UDC
chmod 777 /dev/hidg0
Изучив вышеизложенноеbash
Скрипт,Выяснилось, что в этом проекте используется драйвер USB-гаджета системы Linux.,При необходимости вы можете просмотреть исходный код этой части самостоятельно.,родыLinuxЯдро:linux/drivers/usb/dwc2
иlinux/drivers/usb/gadget
в каталоге。
该водить машину细节существовать本文暂不进行研究。Запуск вышеизложенногоbash
Скрипт后,Если не случайно,Можно найти вUSB Tree View
Просмотрите смоделированный вывод устройства Raspberry Pi вUSBклавиатура。
Вот необходимые знания: USB — это структура «главный-подчиненный». Должен быть один USB-хост и одно USB-устройство, и связь всегда инициируется хостом.
В контексте этой статьи сначала откройте Wireshark, начните захват трафика USBPcap1, а затем подключите порт type-c (порт источника питания) Raspberry Pi. Четыре гнездовых порта USB устройства Raspberry Pi 4b можно использовать только в качестве. USB-хосты могут использоваться только в качестве USB-устройства) подключен к USB-порту хоста. В настоящее время хост не распознает Raspberry Pi как USB-устройство и подает только питание. поэтому Raspberry Pi начинает запускаться в это время.
Затем начните фильтровать все адреса трафика, которые может перехватить Wireshark. Они не принадлежат USB-трафику Raspberry Pi. Как показано на рисунке 6, синтаксис фильтрации используется в этой статье:
Рисунок 6. Синтаксис фильтра Wireshark.
Затем на Raspberry Pi используйтеrootРазрешения на запуск указанного вышеbash
Скрипт,Еще раз проверьте трафик, перехваченный Wireshark.,Как показано на рисунке 7:
Рисунок 7. Трафик, перехваченный Wireshark на основе синтаксиса фильтра.
Адрес USB-трафика, захватываемый с помощью USBPcap, обычно имеет вид: x.y.z, x — номер шины USB (обычно номер хост-контроллера). Для трафика USBPcap1, перехваченного в этом примере, значение x равно 1.
y - номер устройства,Наведите указатель на устройство в шине,ноUSBPcap
иUSBTreeView
Способы нумерации устройств разные.。USBPcap
Номера устройств определяются по порядку,иUSBTreeView
то в начале это уже для всехUSBИнтерфейсы пронумерованы,Независимо от того, подключено USB-устройство или нет.
По моему разумению,USBPcap
Может больше соответствовать реальной ситуации,Потому что он не составил свой собственный номер,Вместо этого он анализирует захваченный USB-трафик.
Значение z представляет номер конечной точки (конечная точка).,Я думаю, это немного похоже на файловый дескриптор программы (fd).,Связь между USB Хозяином и устройством осуществляется через номер конечной точки.,Когда USB-устройство не зарегистрировано на Хозяине,Используется по умолчанию0
номер конечной точки для связи。
Далее мы изучим протокол USB. После исследования мы обнаружили, что USB-устройство сообщает USB-хосту свою собственную информацию, отправляя хосту различные дескрипторы. Эти дескрипторы определяются через структуры в исходном коде Linux. Поле структуры. Вы можете обратиться к соответствующим статьям. Давайте проанализируем несколько важных дескрипторов USB.
Первый - дескриптор устройства,该描述符из结构体定义роды:linux/include/uapi/linux/usb/ch9.h
,Структура следующая:
/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdUSB;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0;
__le16 idVendor;
__le16 idProduct;
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__ ((packed));
Сначала проверьте дескриптор устройства, записанный на USBPcap, как показано на рисунке 8:
Рисунок 8. Просмотр дескриптора устройства в Wireshark.
Сравнить еще разUSBTree View
показано надескриптор Информация об устройстве, как показано на рисунке 9:
Рис. 9. Просмотр дескриптора устройства в дереве USB
Путем сравнения установлено, что контрольный дескриптор устройстваизродыbash
Скриптиз以下几行代码:
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
STRINGS_DIR="strings/0x409"
mkdir -p "$STRINGS_DIR"
echo "6b65796d696d6570690" > "${STRINGS_DIR}/serialnumber"
echo "keymimepi" > "${STRINGS_DIR}/manufacturer"
echo "Generic USB Keyboard" > "${STRINGS_DIR}/product"
Сначала определите протокол устройства как USB2.0.,Затем установите идентификатор поставщика (0x1d6b) и идентификатор продукта (0x0104).,Затем определилиbNumConfigurations
дескриптор конфигурации Количество0x01
,Остальное — это некоторая строковая идентификационная информация.
Информация о поставщике в большинстве случаев используется в качестве идентификационной информации, но в последующих статьях вы обнаружите, что некоторые драйверы запускаются при идентификации указанного поставщика и идентификаторов продукта. В настоящее время они только имитируют устройства с мышью и клавиатурой, они не полагаются на эти триггеры идентификатора, поэтому их можно изменять по желанию.
Затем хост запросит у устройства количество дескрипторов конфигурации, указанное в дескрипторе устройства.
дескриптор конфигурации Структура определяется как:linux/include/uapi/linux/usb/ch9.h
,Структура следующая:
struct usb_config_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 wTotalLength;
__u8 bNumInterfaces;
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed));
Сначала проверьте дескриптор конфигурации, записанный на USBPcap, как показано на рисунке 10:
Рис. 10. Инициируемый хостом запрос на получение дескриптора конфигурации.
Хост сначала запросит дескриптор конфигурации фиксированной длины 9, как показано на рисунке 11:
Рис. 11. Просмотр дескриптора конфигурации в Wireshark.
Затем Хозяинпроходитьдескриптор конфигурацииизwTotalLength
Поле,узналдескриптор Фактическая длина конфигурации, затем Хозяин запросит полный дескриптор с USB-устройства. конфигурации,Как показано на рисунке 12.,На рисунке 13 показано:
Рисунок 12. Инициируемый хостом запрос на получение дескриптора конфигурации.
Рис. 13. USB-устройство отвечает полным пакетом дескриптора конфигурации.
Из трафика, перехваченного USBPcap, можно обнаружить, что ответный пакет дескриптора конфигурации, помимо информации дескриптора конфигурации, также содержит дескриптор интерфейса и дескриптор конечной точки, а поскольку USB-клавиатура зарегистрирована как USB HID-устройство, Итак, дескриптор конфигурации также содержит дескриптор HID, как показано на рисунке 14:
Рисунок 14. Просмотр дескриптора конфигурации в Wireshark.
Используйте USB Tree View для просмотра дескриптора конфигурации, как показано на рисунке 15:
Рис. 15. Просмотр дескрипторов конфигурации в дереве USB.
Далее мы объясним несколько полей в дескрипторе конфигурации:
bmAttributes
:该Поледа控制电源из相关属性,Сообщает ли он Хозяину, имеет ли устройство автономное питание или требуется USB-питание Хозяину?,Можно ли разбудить устройство удаленно через USB.MaxPower
,Цель этого поля — сообщить Хозяину,Максимальный ток, который может принять USB-устройство.bNumInterfaces
:该Поле再次让Хозяин Знать,Существует несколько дескрипторов конфигурации USB-устройств.bConfigurationValue
,Это значение представляет собой серийный номер конфигурации дескриптора.,используется вSet Configuration Request
внутри,Используется для уведомления о настройках USB,Какой USBдескриптор конфигурация успешно зарегистрирована в ядре Хозяин.Полный анализдескриптор После конфигурирования вы можете узнать об управляющем дескрипторе. конфигурациисуществоватьbash
Скриптсерединаиз代码如下所示:
CONFIG_INDEX=1
CONFIGS_DIR="configs/c.${CONFIG_INDEX}"
mkdir -p "$CONFIGS_DIR"
echo 250 > "${CONFIGS_DIR}/MaxPower"
CONFIGS_STRINGS_DIR="${CONFIGS_DIR}/strings/0x409"
mkdir -p "$CONFIGS_STRINGS_DIR"
echo "Config ${CONFIG_INDEX}: ECM network" > "${CONFIGS_STRINGS_DIR}/configuration"
дескриптор интерфейса Структура определяется как:linux/include/uapi/linux/usb/ch9.h
,Структура следующая:
/* USB_DT_INTERFACE: Interface descriptor */
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
Сначала проверьте дескриптор интерфейса, записанный на USBPcap, как показано на рисунке 16:
Рис. 16. Просмотр дескриптора интерфейса в Wireshark.
Проверьте еще разUSBTree View
上издескриптор Информация о интерфейсе, как показано на рисунке 17:
Рис. 17. Просмотр дескриптора интерфейса в виде дерева USB.
В дескрипторе интерфейса значение полей следующее:
bNumEndpoints
定义了设备конечная точкаиз数量,в протоколе USB,Связь между конечными точками односторонняя.,Здесь определены два дескриптора конечной точки.,Один представляет ввод,Один представляет собой результат.bInterfaceClass
定义了接口из类型,На рисунке выше указано HID-устройство.,Таким образом, Хозяин снова прочитает HID-дескриптор.bInterfaceProtocol
定义了接口协议为клавиатура,Это позволит HID-драйверу клавиатуры обрабатывать последующую связь.bInterfaceClass
值对应из含义可以参考LinuxИсходный код ядра(同样дасуществоватьch9.hпо определению):
/*
* Device and/or Interface Class codes
* as found in bDeviceClass or bInterfaceClass
* and defined by www.usb.org documents
*/
#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PHYSICAL 5
#define USB_CLASS_STILL_IMAGE 6
#define USB_CLASS_PRINTER 7
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
#define USB_CLASS_CDC_DATA 0x0a
#define USB_CLASS_CSCID 0x0b /* chip+ smart card */
#define USB_CLASS_CONTENT_SEC 0x0d /* content security */
#define USB_CLASS_VIDEO 0x0e
#define USB_CLASS_WIRELESS_CONTROLLER 0xe0
#define USB_CLASS_PERSONAL_HEALTHCARE 0x0f
#define USB_CLASS_AUDIO_VIDEO 0x10
#define USB_CLASS_BILLBOARD 0x11
#define USB_CLASS_USB_TYPE_C_BRIDGE 0x12
#define USB_CLASS_MISC 0xef
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
#define USB_SUBCLASS_VENDOR_SPEC 0xff
bInterfaceProtocol
существоватьLinux源码середина只定义了鼠标иклавиатура,Как показано ниже,Значение остальных устройств установлено на 0,Или определяется драйвером производителя,Как показано на рисунке 18:
Рис. 18. Макроопределение значения bInterfaceProtocol в исходном коде Linux.
дескриптор конечной точки Структура определяется как:linux/include/uapi/linux/usb/ch9.h
,Структура следующая:
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval;
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
существоватьUSBPcap
上查看捕获到издескриптор конечной Информация о точках, как показано на рисунке 19:
Рисунок 19. Просмотр дескриптора конечной точки в Wireshark.
Номер порта и направление порта определяются в дескрипторе конечной точки, а также метод связи с использованием порта (метод связи, определенный на рисунке выше, представляет собой передачу с прерыванием) и максимальный размер пакета данных связи (8). байты.
Затем я перечислил файловую структуру каталога гаджетов Linux следующим образом:
$ tree
.
├── bcdDevice
├── bcdUSB
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── configs
│?? └── c.1
│?? ├── bmAttributes
│?? ├── hid.usb0 -> ../../../../usb_gadget/g1/functions/hid.usb0
│?? ├── MaxPower
│?? └── strings
│?? └── 0x409
│?? └── configuration
├── functions
│?? └── hid.usb0
│?? ├── dev
│?? ├── no_out_endpoint
│?? ├── protocol
│?? ├── report_desc
│?? ├── report_length
│?? └── subclass
├── idProduct
├── idVendor
├── max_speed
├── os_desc
│?? ├── b_vendor_code
│?? ├── qw_sign
│?? └── use
├── strings
│?? └── 0x409
│?? ├── manufacturer
│?? ├── product
│?? └── serialnumber
└── UDC
Найден после исследования,可以проходитьno_out_endpoint
документ,Количество конечных точек управления,По умолчанию есть две конечные точки IN/OUT.,еслиno_out_endpoint
документиз值为1,дескриптор конечной точки Есть только одинINконечная точка。другойreport_length
документ,Используется для контроля максимальной длины передаваемых пакетов данных.,То естьbMaxPacketSize
Поле。
Последний строковый дескриптор,结构体из定义也дародыch9.h
:
struct usb_string_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 wData[1]; /* UTF-16LE encoded */
} __attribute__ ((packed));
Основная функция строкового дескриптора — идентифицировать информацию.,напримерсуществоватьUSB Tree View
показано наUSBИнформация об устройстве,都дапроходитьстроковый Дескриптор получен.
Можно найти вUSBTree View
Посмотреть все встроковый дескриптор, как показано на рисунке 20:
Рис. 20. Просмотр строкового дескриптора в дереве USB
в дескрипторе интерфейсасередина,iInterface
Полеиз值就дастроковый Смещение дескриптора.
Когда USB-хост узнает, что USB-устройство является USB HID-устройством, через дескриптор интерфейса, он получит дескриптор отчета HID. Дескриптор отчета HID, записанный в USBPcap, показан на рисунке 21:
Рис. 21. Дескриптор отчета HID просмотра Wireshark
Определение Дескриптор отчета HIDиз代码существоватьbash
Скриптсередина如下所示:
# Write the report descriptor
# Source: https://www.kernel.org/doc/html/latest/usb/gadget_hid.html
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > "${FUNCTIONS_DIR}/report_desc"
Дескриптор отчета HID в этом примере взят из примера ядра Linux, как показано на рисунке 22:
Рис. 22. Документация по драйверу гаджета USB HID для Linux.
Итак, на следующем этапе нам нужно иметь возможность плавно читать дескриптор отчета HID. Вы можете обратиться к официальному документу. Преимущество официального документа в том, что он является полным по содержанию. Содержание для новичков не подходит для начала. Официальный документ больше подходит в качестве справочного руководства после начала работы.
Давайте сначала проверимkey-mime-pi
项目из通信代码,Как узнать Хозяина на Raspberry Pi,Какие клавиши управляются,Соответствующий код выглядит следующим образом:
def send(hid_path, control_keys, hid_keycode):
with open(hid_path, 'wb+') as hid_handle: # hid_path = "/dev/hidg0"
buf = [0] * 8
buf[0] = control_keys
buf[2] = hid_keycode
hid_handle.write(bytearray(buf))
hid_handle.write(bytearray([0] * 8))
Каждая операция кнопки требует отправки двух 8-байтовых буферов на USB-хост (максимальный размер пакета ограничен 8 байтами в дескрипторе конечной точки). Все 8 байтов второго buf установлены в 0, а первый байт первого buf является управляющим символом. После исследования выяснилось, что установлены 8 управляющих символов, как показано ниже:
#define KEY_LEFTCTRL 0xe0 // Keyboard Left Control
#define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift
#define KEY_LEFTALT 0xe2 // Keyboard Left Alt
#define KEY_LEFTMETA 0xe3 // Keyboard Left GUI
#define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control
#define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift
#define KEY_RIGHTALT 0xe6 // Keyboard Right Alt
#define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI
GUI
даwindowsизwinключ,Мак-команда.
Второй байт первого buf не установлен и по умолчанию равен 0. Байты с третьего по 8-й имеют длину 6 байт и являются ключами ввода.
Имея общее представление о том, как отправлять данные на USB-хост, давайте взглянем на дескриптор отчета HID:
static struct hidg_func_descriptor my_hid_data = {
.subclass = 0, /* No subclass */
.protocol = 1, /* Keyboard */
.report_length = 8,
.report_desc_length = 63,
.report_desc = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x06, /* USAGE (Keyboard) */
0xa1, 0x01, /* COLLECTION (Application) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
0x95, 0x05, /* REPORT_COUNT (5) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x03, /* REPORT_SIZE (3) */
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0x00, /* USAGE_MINIMUM (Reserved) */
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
0xc0 /* END_COLLECTION */
}
};
首先даUSAGE_PAGE
иUSAGE
,Эти два поля можно найти в справочной документации.,В настоящее время мы можем выбрать только те приложения, которые были определены.,Это повлияет на идентификацию функций некоторых драйверов.
В основном смотрите на содержимое части коллекции,Собрать сCOLLECTION
начинатьEND_COLLECTION
Заканчивать。
Содержимое сборника можно разделить на четыре части, первая часть выглядит следующим образом:
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
Первая часть определяет функциональные клавиши клавиатуры.,Минимальное значениеKeyboard LeftControl
(0xe0),Максимальное значениеKeyboard Right GUI
(0xe7)。逻辑Минимальное значение0,Максимальное значение – 1.,1 означает нажатие,0 означает выпуск. Одна кнопка занимает 1 бит,Есть 8 кнопок,Всего пришлось1байт。например:0b00000001выражатьLeftControl
ключ被按下了。Смотри отсюда,Мы можем нажать все 8 клавиш управления одновременно.
Вторая часть выглядит так:
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
Существует 8 1-битных значений, всего 1 байт, и они являются константами (Cnst везде постоянен), поэтому контекст не видит заданного значения, поэтому по умолчанию оно считается значением 0. При отправке данных оно является значением. не повлияет, даже если 0 не будет отправлен, т.к. драйвер не будет активно распознавать этот байт данных.
Третья часть выглядит так:
0x95, 0x05, /* REPORT_COUNT (5) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x03, /* REPORT_SIZE (3) */
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
Приведенный выше дескриптор HID определяет функцию светодиода, которая занимает в общей сложности 1 байт. Старшие 3 бита представляют собой постоянный 0, а младшие 5 бит представляют собой индикаторы на клавиатуре. К общим относятся: индикатор блокировки номера клавиатуры, индикатор блокировки верхнего регистра. , индикатор блокировки прокрутки. Подождите. И он отправляется на устройство хостом. Включение и выключение подсветки клавиатуры контролируется хостом USB.
Четвертая часть выглядит так:
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0x00, /* USAGE_MINIMUM (Reserved) */
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
Функция клавиатуры определена. Значение клавиши находится в диапазоне 0–0x65. Всего имеется 102 клавиши. Логическое значение также находится в диапазоне 0–0x65. Одна клавиша может занимать до 6 клавиш, общий размер которых составляет 6 байт.
На этом этапе было проанализировано определение дескриптора отчета HID клавиатуры. Мы обнаружили, что содержание определения дескриптора соответствует формату наших входных данных.
Первый отправленный байт buf представляет собой 8 кнопок управления, второй байт имеет фиксированное значение 0, а следующие 6 байтов представляют собой кнопки ввода.
В это время возникают две проблемы:
1. Клавиатура, смоделированная в тесте, представляет собой 104-клавишную клавиатуру. Почему имеется 102+8=110 значений? После исследования выяснилось следующее:
#define KEY_NONE 0x00 // No key pressed
#define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key")
// 0x02 // Keyboard POST Fail
// 0x03 // Keyboard Error Undefined
#define KEY_102ND 0x64 // Keyboard Non-US \ and |
#define KEY_BACKSLASH 0x31 // Keyboard \ and |
// Вышеупомянутые два конфликта, есть еще один
#define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~
// Я думаю, на моей клавиатуре нет клавиш, не связанных с США.
По приведенному выше расчету это именно 104-клавишная клавиатура.
2. Почему необходимо отправлять пакет данных со всеми нулями. После исследования выяснилось, что пакет данных, отправленный USB-устройством на USB-хост, представляет собой клавиатуру, информирующую USB-хост о текущем состоянии клавиатуры. Полная операция с ключом заключается в нажатии клавиши, а затем ее отпускании. Один отправленный пакет данных сообщает хосту, какие клавиши были нажаты, а второй пакет данных, все 0, сообщает хосту, что все клавиши были отпущены.
3 Следующие направления исследований
Справочная информация
Исследование данной статьи подошло к концу. В следующих статьях будут рассмотрены следующие направления исследований:
4 Справочные ссылки
Ссылки
[1] https://github.com/mtlynch/key-mime-pi
[2] https://zhuanlan.zhihu.com/p/558716468
[3] https://www.kernel.org/doc/html/latest/usb/gadget_hid.html
[4] https://usb.org/sites/default/files/hut1_4.pdf