International conference of developers
and users of free / open source software

Реверс-инжиниринг протокола USB-устройств

Vasily Khoruzhick, Минск, Belarus

LVEE 2013

Another reverse-engineering guide. Guides one through USB protocol basics and reverse-engineering tools for Linux. Generic advises on understanding vendor-specific USB protocol

Введение

Наверное, сегодня нет человека, который никогда не использовал шину USB. USB-устройства весьма распространены: мышки, клавиатуры, веб-камеры, принтеры, звуковые карты. Все данные устройства делятся на 2 больших группы: устройства класса определённого в USB-стандарте и устройства с закрытым протоколом (т.н. vendor-specific class). Первые, как правило, поддерживаются стандартными драйверами операционной системы (например, в современных операционных системах не требуется установка драйверов для флэшек или USB-клавиатур). Вторые же требуют специального драйвера от производителя устройства. К сожалению, многие производители ограничиваются поддержкой определённых версий Windows, и при этом не желают открывать описания протокола своего устройства. В этом случае для поддержки устройства в остальных операционных системах требуется “расшифровать” протокол устройства. К счастью, особенности USB-шины позволяют довольно легко перехватывать траффик между устройством и компьютером.

Базовые сведения о USB протоколе

USB является хост-ориентированной шиной с топологией многоуровневой звезды. На шине может присутствовать только один хост и до 127 устройств (для поддержки большего количества устройств используется несколько хост-контроллеров, каждый из которых отвечает за свою шину), каждое устройство на шине идентифицируется уникальным адресом (от 1 до 127). Каждое устройство может иметь до 32 концевых точек (endpoint) — 16 на приём и 16 на передачу. Все передачи на шине инициирует только хост — устройство может передавать данные только тогда, когда хост запросит их.

На уровне программного обеспечения, минимальная неделимая единица – трансфер (на уровне железа это не так, для более глубокого понимания рекомендуется ознакомиться с 1). Трансферы и концевые точки бывают 4 типов:

  1. control-трансферы
  2. bulk-трансферы
  3. interrupt-трансферы
  4. isochronous-трансферы

control-трансферы – это двунаправленные message-ориентированные трансферы, состоящие из 3 фаз:

  1. setup-фаза — направление от хоста в девайсу — во время данной фазы передаётся setup-пакет, который по сути является заголовком сообщения
  2. data-фаза — направление зависит от содержимого setup-пакета — во время данной фазы передаётся тело сообщения. Данная фаза опциональна
  3. status-фаза — направление всегда противоположено data-фазе (при отсутствии data-фазы — от устройства к хосту) — подтверждает что сообщение корректно обработано.

Все остальные трансферы являются однонаправленными:

  • bulk-трансферы — трансферы, для которых гарантируется доставка, но не гарантируется пропускная способность или задержка
  • interrupt-трансферы — трансферы, для которых гарантируется доставка и задержка, размер передаваемых данных ограничен
  • isochronous-трансферы — трансферы, для которых гарантируется пропускная способность, но не гарантируется доставка

Таким образом для формирования трансфера хост должен знать:

  • адрес устройства, которому предназначен трансфер
  • тип, направление и номер концевой точки

Для получения более подробной информации рекомендуется ознакомиться с 1

Краткий обзор инструментария реверс-инженера

Особенности USB протокола — в частности наличие хост-контроллера и, соответственно, драйвера хост-контроллера, через который обязаны общаться драйвера устройств с устройствами — позволяют перехватывать траффик, который генерирует драйвер. ОС GNU/Linux содержит штатное средство для перехвата USB-траффика — драйвер usbmon 2. Для ОС Windows существует драйвер usbpcap 3.

Для облегчения анализа USB-траффика рекомендуется использовать сетевой анализатор Wireshark — для Linux реализация libpcap, который Wireshark использует для захвата траффика, умеет работать с usbmon, для Windows драйвер usbpcap уже предоставляет перехваченные данные в pcap-формате.

Драйвер usbmon для Linux позволяет перехватывать траффик сразу на всех USB-шинах, или на какой-либо отдельной шине. usbpcap для Windows работает только с отдельными устройствами.

Анализ протокола

Рассмотрим анализ протокола с использованием ОС Linux. Непосредственно перед анализом протокола необходимо определить на какой шине находится устройство:

$ lsusb
Bus 002 Device 002: ID 04f2:0402 Chicony Electronics Co., Ltd Genius LuxeMate i200 Keyboard
Bus 002 Device 004: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Bus 002 Device 005: ID 1241:1166 Belkin MI-2150 Trust Mouse
Bus 002 Device 008: ID 08ff:2550 AuthenTec, Inc. AES2550 Fingerprint Sensor
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

Нас интересует устройство с ID 08ff:2550, и оно на ходится на шине 2.

Далее необходимо убедиться, что драйвер usbmon загружен, и если он не загружен загрузить его:

lsmod | grep usbmon || sudo modprobe usbmon

После этого можно отключить устройство, запустить Wireshark, начать захват траффика с нужной шины, и подключить устройство:

Для анализа протокола нам необходимо перехватить следующие части генерируемого траффика:

  • инициализация устройства
  • начало выполнения какой-либо функции
  • окончание выполнения какой-либо функции
  • деинициализация устройства

Обычно данные части отделены по времени, т.к. инициировать выполнения функции устройством можно в произвольное время (например, подвинуть мышку, нажать кнопку на клавиатуре, начать захват видео с веб-камеры и т.д.)

Как правило, изучаемые устройства делятся на два типа:

  • Register-oriented устройства
  • Message-oriented устройства

Для первых в перехватываемых трансферах будет чётко прослеживаться следующая структура:

  • заголовок трансфера (например, размер в байтах)
  • адрес регистра
  • значение (несколько значений)
  • хвост трансфера (например, контрольная сумма)

Данные устройства предоставляют некое адресное пространство, в которое мы можем производить запись/чтение с помощью определённых USB-трансферов.

Пример данного трансфера мы видим на иллюстрации:

См. трансфер №375, поле leftover capture data. В данном случае трансфер содержит пары адрес-регистра — значение. Трансфер №376 является подтверждением, что устройство приняло трансфер. Трансфер №377 является запросом от хоста на приём данных от устройства, трансфер №378 – ответом устройства. Можно заметить, что любой трансфер в Wireshark виден как пара пакетов вида запрос хоста — ответ устройства (надеюсь, читатель еще помнить что все трансферы на USB шине инициирует хост?). Стоит отметить, что не всегда эта пара расположена линейно.

Для второго типа устройств в перехватываемых трансферах будет видна следующая структура:

  • заголовок трансфера
  • тип сообщения
  • тело сообщения
  • хвост трансфера

Данные устройства, как правило, требуют больших усилий по расшифровке протокола.

Как правило, практически невозможно по дампу траффика, без экспериментов с устройством, расшифровать протокол. Для экспериментов удобно будет написать прототип драйвера с использованием библиотеки libusb. Данная библиотека работает в пространстве пользователя, т.о. при падении Вашего прототипа не упадёт вся ОС.

В качестве первого прототипа разумно будет повторить ту последовательность, которая записана в дампе. При запуске прототипа стоит записать еще один дамп, и сравнить его с оригинальным. Отличия между дампами сигнализируют о том, что что-то прототип делает не так. Модификациями прототипа необходимо добиться идентичности дампа (в некоторых случаях это необязательно или невозможно, например при захвате картинки с камеры).

Пример простейших прототипов можно посмотреть в 4 и 5. Как можно заметить данные прототипы состоят из следующих частей:

  • инициализация библиотеки: libusb_init()
  • открытие устройства: libusb_open_device_with_vid_pid()
  • выбор нужного интерфейса: libusb_claim_interface()
  • некоторого количества трансферов: libusb_bulk_transfer()/libusb_control_transfer()/libusb_interrupt_transfer()
  • разбор ответа от устройства
  • деинициализация: libusb_close(); libusb_exit()

Заключение

Не смотря на то, что разбор протокола USB-устройства кажется сложной задачей, на самом деле таковым не является — необходимо лишь упорство, терпение и желание экспериментировать.

1 http://www.beyondlogic.org/usbnutshell/usb1.shtml
2 https://www.kernel.org/doc/Documentation/usb/usbmon.txt
3 http://desowin.org/usbpcap/
4 https://github.com/anarsoul/fprint_aes1660
5 https://github.com/anarsoul/fprint_aes2550

Abstract licensed under Creative Commons Attribution-ShareAlike 3.0 license

Back