• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья USB Flash [часть 1] – устройство

В этом году интерфейсу USB исполнилось ровно 25-лет, а потому предлагаю рассмотреть его реализацию как на уровне архитектуры, так и программную составляющую. Сырая версия 1.0 была опубликована в начале 1996 года, а уже осенью 98-го, мир увидел первую стабильную редакцию v1.1. Однако на практике оказалось, что из шины USB можно выжать намного больший потенциал. Так, в 2000 году была опубликована очередная спецификация USB 2.0, в которой предусмотрено повышение пропускной способности сразу в десятки раз. Это позволило существенно расширить круг подключаемых к шине высоко-скоростных устройств:

USB_Property.png



Содержание:

1. Топология шины: хост-контролёр, Root-Hub, режимы работы Control-Bulk-Interrupt;
2. Логическая организация: конечные точки Endpoint, протокол шины USB;
3. Типы дескрипторов устройств.
------------------------------------------------------------


1. Топология шины USB

Начнём с того, что USB поддерживает некоммерческая организация под названием USB-IF, или "Implementers Forum". Ребята из этого форума поставили перед собой задачу, найти универсальное решение для подключения к компьютеру гремучей смеси разнообразных устройств. В приоритете было удовлетворить основные требования подсистемы Рlug-and-Рlay (подключи и играй), которая используется для простой настройки вновь обнаруженных устройств.

В результате, не знакомые на тот момент с технологией PnP внешние порты теперь остались в прошлом, в числе которых последовательный RS-232, параллельный LPT, порты PS/2 клавиатуры и мыши, Gameport, и т.д. Весь этот хлам съедал и без того ограниченный пул системных ресурсов, например аппаратные прерывания IRQ, и каналы прямого доступа к памяти DMA. С приходом универсальной последовательной шины USB удалось избавиться от этих проблем + организовать поддержку "горячего" подключения девайсов, без катастрофических для платы последствий. Только ознакомившись с технической стороной данного усовершенствования понимаешь, какой самоотдачи это требовало от инженеров
. Но обо всём по порядку..


1.1. Хост-контролёр и Root-Hub

Внутри системной архитектуры, USB-интерфейс придерживается топологии "многоуровневая звезда". Как правило, на одной материнской плате имеется сразу несколько шин USB и каждую из них обслуживает свой хост-контролёр, который способен адресовать не более 127 физических устройств. Непосредственно к хосту, сразу-же присавокупляется корневой концентратор Root-Hub, в тушке которого уже парочка Usb-портов.

Таким образом, отсчёт 127-ми устройств на одной шине начинается именно с корневого хаба, и именно он в топологии является центром звезды. Подключив в порт рута внешний концентратор с несколькими портами, получим ещё одну подчинённую звезду и т.д. Подобная схема физ.устройств чем-то напоминает размножение "методом почкования" и у него имеются свои ограничения – общее число концентраторов на одной шине, не должно превышать шести (каскадирование хабов).

В терминологии USB, все подключаемые в порты концентраторов устройства принято называть конечной точкой, что на их манер звучит как Endpoint. В свою очередь шинный контролёр и вся его подчинённая свита собираются под общим названием Usb-Node – физический узел. Вот как эту картину можно представить в графике:

USB1.png


Здесь видно, что Usb-контролёры в качестве магистральной используют параллельную PCI-шину компьютера. Это действительно только для скоростей USB-1 и 2. Если-же в системную архитектуру включён хост USB-3 с поддержкой SuperSpeed, он подключается уже к высокоскоростной последовательной трассе PCI-Express. В остальном, на программном уровне все три контролёра и внутренняя организация их узлов мало чем отличаются – в спецификациях USB-1,2,3 фундаментальных различий нет и они лишь дополняют некоторыми особенностями одна другую.

Выше упоминалось, что в одной системе присутствуют сразу несколько независимых шин USB, и каждой заправляет собственный хост-контролёр. Ознакомиться с топологией конкретной системы позволит как-минимум её диспетчер устройств, если раскрыть в нём соответствующую ветвь. На скрине ниже представлен вариант моей тестовой платы где видно, что данный боард с чипсетом G41 имеет целых 5-шин и столько-же корневых хабов, причём всего один из контролёров поддерживает USB-2:

SysDevMan.png


Здесь может возникнуть резонный вопрос: -"Если на каждую шину USB можно повесить до 127 девайсов, то зачем нужны ещё доп.четыре шины?". Всё верно, ..только в пользу зоопарка имеется весомый аргумент! Дело в том, что число 127 здесь чисто теоретическое и привязано исключительно к 7-битной адресации устройств внутри одного узла Node. В реальной-же ситуации, шина USB является разделяемым ресурсом, что в конечном счёте отрицательно сказывается на её пропускной способности.

К примеру, если USB-2 имеет заявленную скорость 60 Мбайт/с (480 Мбит), то это лишь с расчётом на один девайс. Если к этой-же шине подключить второе устройство, они разделят транспорт "по-братски" и каждое из устройств получит уже по 30 Мбайт/с. Связано это с тем, что USB последовательная шина, шириной всего 1-бит, и соответственно контролёру придётся чередовать информационные пакеты двух девайсов по очереди. Так-что архитектурное решение с несколькими шинами вполне оправдано, чтобы каждая из конечных точек имела возможность работать на макс.скорости.

Другой особенностью интерфейса USB является то, что практически всегда шинные транзакации инициирует исключительно хост-контролёр. Такая тактика была выбрана не случайно, ведь усмотреть сразу за сотней резвящимися на одной шине "разнополыми" пакетами – задача не простая. Конечная точка Endpoint (в лице нашего Usb-брелка или иного девайса) лишь отвечает на запросы, и лишена возможности самостоятельно начинать диалог. Это накладывает ограничение на прямую связь между двумя устройствами – в любом случае трафик проходит через посредника Hub.

Все девайсы томятся в ожидании команды от хоста на передачу данных, однако исключением является ситуация, когда девайс по истечении определённого таймаута был вогнан контролёром в режим низкого энергопотребления. Тогда устройству разрешается подать хосту сигнал "Wakeup" о своём пробуждении. Данный алгоритм является типичным при взаимодействии хоста с бездействующей Usb-клавой или мышью.


1.2. Режимы работы хост-контролёра

Спецификация USB строго гласит, что любой хост-контролёр обязан поддерживать 4 базовых режима передачи данных, которые далее могут комбинироваться:


1. Сontrol Transfers – одинарные пакеты с управляющими сообщениями. Режим используется контролёром для конфигурирования устройств во время их подключения, и для управления ими в процессе работы. Протокол применяется буквально для всех классов USB-устройств, и обеспечивает гарантированную доставку данных.
2. Interrupt – короткие передачи по прерываниям, которые имеют спонтанный характер и должны обслуживаться не замедлительно. Клиентами этого режима являются как-правило клавиатура и мышь. Нажал клавишу – получил прерывание. При случайных ошибках обмен повторяется.
3. Bulk Transfers – передача больших массивов данных. Именно в этот режим переходит контролёр при работе с USB-Flash и другими накопителями класса "Mass Storage Device". Протокол с самым низким приоритетом передач. При загруженной шине транзакации могут приостанавливаться, хотя доставка пакетов гарантированна. Это герой данной статьи, так-что запомним его на будущее!
4. Isochronous – изохронные передачи. Их требуют потоковые устройства типа: видеокамеры, микрофон/колонки, девайсы для записи CD/DVD и т.д. Режим был введён только со-второй версии спецификации USB-2.0, для передачи непрерывных потоков данных на высокой скорости High-Speed. На приёмном узле нету времени на проверку целостности пакетов, поэтому некоторые из них могут теряться.

Программная модель взаимодействия хост-контролёра с подчинёнными устройствами построена так, что именно устройство запрашивает у контролёра требуемые ему ресурсы, а хост обязан их предоставить. В общем случае дела обстоят следующим образом..

При первом обнаружении устройства в системе, контролёр замораживает все текущие транзакации и назначает обнаруженному устройству адрес(0). Этот адрес зарезервирован для служебных целей, и не используется в уже сконфигурированной системе, поэтому адреса всех активных на данный момент устройств начинаются с единицы и до 127. Теперь, в режиме "Control Transfer", устройству с нулевым адресом контролёр посылает серию управляющих запросов Setup, на что обнаруженное устройство возвращает контролёру все свои характеристики в виде дескрипторов: Device, Configuration, Interface, Endpoint, Power и String (позже мы их программно считаем).

На основании этих данных, контролёр выделяет устройству затребованную полосу пропускания шины, определяет режим работы с устройством (Bulk, Int, Isochronous), скорость обмена и потребляемый от шины ток. Только полностью сконфигурировав устройство, контролёр назначает ему свободный адрес в диапазоне 1..127, после чего продолжает приостановленные задачи.

Сканирование устройств осуществляется постоянно, в реальном времени отслеживая изменения физической топологии шины. Например, концентраторы Hub фиксируют и сразу сообщают контролёру о подключение/отключение устройств к своим портам. Тот, в свою очередь определяет, кем/чем является неизвестное устройство – хабом или обычным девайсом, и в зависимости от этого создает определённый канал обмена с ним, Pipe. В момент, когда устройство отключается, хаб прихлопывает соответствующий свой порт и сразу-же докладывает контролёру, который удаляет сведения о данном устройстве из всех служебных структур.


2. Логическая организация шины USB

Мы выяснили, что на одной шине контролёр может обслуживать макс 127 устройств, которые в топологии называют конечными точками Endpoint. Однако на логическом уровне, каждый Endpoint это целый набор независимых буферов, общее число которых может достигать 16. Эти буферы имеют размеры до 64-байт и нумеруются как ЕР0..ЕР16. Другими словами, одна физическая точка Endpoint в виде нашего USB-брелка, на логическом уровне может содержать в себе до 16-ти логических точек 0..16. В программировании интерфейса USB логический уровень играет огромную роль, поэтому разберёмся хотя-бы с основными его понятиями.


2.1. Конечные точки "Endpoint"

Выше упоминалось, что на этапе конфигурирования нового устройства, хост запрашивает все его дескрипторы, куда производитель обязан прошить полный паспорт своего продукта. Иначе контролёр просто не сможет опознать USB-девайс и настроить с ним связь должным образом. Среди полученных данных (а точнее в дескрипторе интерфейса) устройство вернёт и кол-во своих логических точек(ЕР), а контролёр привяжет к каждой из них свой пайп.

Здесь мы рассматриваем устройства класса Mass-Storage (т.е. накопители) поэтому авансом скажу, что у этого класса устройств, логических точек всегда только три: ЕР1 служит каналом чтения данных, ЕР2 канал для записи во флеш, и нулевой канал ЕР0. Вне зависимости от класса, ЕР0 присутствует буквально во всех USB-устройствах, т.к. именно через него контролёр посылает девайсу сервисные запросы при настройке в режиме "Control". Надеюсь схема ниже немного прояснит обстановку дел на этом фронте:

Endpoint.png


Канал конфигурации ЕР0 – это единственный двунаправленный пайп и активен он всегда, в то время-как информационные каналы In/Out могут быть отключены в какой-либо из конфигураций, или-же при первом обнаружении устройства. Набор конечных точек начиная с ЕР1 напрямую зависит от типа устройства, но точки In/Out всегда ходят парами. К примеру у девайса может быть один обязательный канал ЕР0 и пара инфо-каналов, как у второго устройства с адресом[127] на рисунке выше. Не точно, но 4-канальную схему с ЕР1..ЕР4 вроде используют девайсы USB-3, но это лишь предположение, т.к. проверить сейчас не могу. У всех устройств USB-2 всегда только одна пара In/Out.

Каждая точка ЕРх имеет свой набор характеристик, которые хранятся в дескрипторе "Usb-Endpoint" устройства. Битовая маска описывает: поддерживаемый точкой тип передачи данных (изохронный, Control, Bulk, Int, или все сразу CBI), размер инфо-пакета в байтах 64-512, и требования к частоте обслуживания. В практической части мы попытаемся сдампить на консоль всю эту инфу, а пока достаточно лишь знать об этом.

Устройство может выполнять несколько различных функциональных задач. Например, райдер DVD может исполнять роль плеера и работать как устройство хранения данных. Для решения каждой задачи в устройстве определяется Interface – набор и правила использования точек, для выполнения конкретной задачи. У многофункциональных устройств может быть несколько интерфейсов. Набор одновременно поддерживаемых интерфейсов составляет "конфигурацию" устройства. В свою очередь, устройство может иметь сразу несколько конфигураций, из которых на этапе инициализации хост выбирает одну, делая её активной.


2.2. Протокол шины USB

Прежде чем перейти к программной поддержки интерфейса, не помешает коротко рассмотреть протокол шины, т.е. какого вида информацию гоняет по шине контролёр и чем ему отвечают устройства. Как и принято в этой "науке", здесь всё продумано до мелочей, однако некоторую путаницу вносит зоопарк режимов работы хост-контролёра, таких как Control, Bulk, Interrupt. Поскольку это принципиально разные диалоги, то и протоколы взаимодействия с устройствами у них разные, но некоторая стабильность всё-же просматривается – на ней и остановимся.

Сердцебиение шины USB в непрерывном режиме поддерживают кадры Frame, которые хост выставляет на шину с периодом 1 мс. Каждый фрейм начинается с маркера SOF (Start of Frame) и заканчивается маркером EOF. По большому счёту, фремы играют роль синхроимпульсов для концентраторов Hub. Однако для непосредственно Usb-устройств, такие периоды слишком большие, поэтому каждый кадр делится ещё и на 8 микрокадров длительностью по 125 мкс. Теперь эти микрокадры можно рассматривать как транспорт для передачи упакованных в пакеты данных.

Во время передачи хостом маркеров ЕOF, все устройства на шине должны молчать. Если это не так и какой-либо девайс позволит себе выставить на шину хоть 1-бит, то его называют "болтливым", и к нему применяются жёсткие меры, вплоть до полного отключения соответствующего порта.

Неотъемлемой частью интерфейса USB является способность хоста усыплять неактивные устройства. Это достигается за счёт того, что на время 3 мс его канал Pipe просто запирается для микро-фреймов. В результате девайс автоматически уходит в спячку с энергопотреблением от шины ~0.5 мA, хотя рабочий ток большинства устройств составляет около 500 мA для USB-2, и порядка 800 мA для USB-3.

Один цикл-обмена контролёра с конечной точкой назвали транзакацией. В зависимости от режима обмена данными, транзакции состоят из двух (при изохронных передачах) или трёх пакетов для Bulk и прочих. Трёхпакетная передача подразумевает гарантированную доставку данных, для чего в протокол добавляется третий пакет размером в байт – Handshake (рукопожатие).

В любом случае, транзакации начинаются с пакет-маркера под названием Token, после которого идут уже полезные данные. В рассматриваемом нами режиме Bulk (передача массивов данных) размер пакета Token составляет 24-бита, куда запрессована наиболее ценная информация, например 7-битный адрес получателя на шине (итого 127), и 4-битный номер его конечной точки ЕРх (итого 16-точек). На рисунке ниже представлен формат одной транзакации, и битовая карта её пакета Token.

Контролёр выставляет информацию на шину начиная с младшего бита, и заканчивая старшим (справа-налево). Каждый из трёх пакетов предваряет неуказанная здесь 32-битная последовательность Sync (синхронизация), и 8-битный EOP (End-of-Packet):

Token.png


По сути, этот запрос от контролёра является широковещательным. Приняв его по шине, все устройства начинают судорожно сравнивать поле "Device Address" в пакете токена со своим, и при его не совпадении обязаны проигнорировать транзакацию. Как видим, в токене сначала идёт тетрада "PID", что подразумевает идентификатор пакета. Следующее поле "Check" является копией PID и введено для надёжности. Далее следуют адрес USB-устройства на шине, номер его конечной точки ЕР и контрольная сумма токена. В тетраде PID закодирована следующая информация:


0001 = транзакация на запись информации в устройство (Out);
1001 = транзакация на чтение (In);
1101 = транзакации конфигурирования устройства в режиме Control (пакет Setup);
0100 = маркер транзакации PING (начиная с USB-2).


3. Дескрипторы USB-устройств

Все USB-устройства имеют иерархию дескрипторов, которые хранятся в постоянной памяти ROM, контролёра самого Usb-устройства. Производитель прошивает эту информацию для хоста, чтобы он мог правильно идентифицировать подключённый девайс. Имеются стандартные и расширенные дескрипторы. Стандартные должны поддерживать буквально все классы USB-устройств, в то время как вторые весьма специфичны и могут отсутствовать в таблице.

Для чтения дескрипторов, в режиме Control хост посылает конечной точке ЕР0 устройства, пакет под названием "Setup", в котором указывается тип дескриптора и его размер. В ответ на этот запрос устройство возвращает хосту требуемую информацию. Общая схема инициализации устройства и типы дескрипторов выглядит примерно так:

Desc.png


Как видим, стандартных дескрипторов всего 5, и они содержат базовые сведения об устройстве.
В следующих таблицах представлена детальная информация, которую несёт в себе каждый из них.


3.1. Дескриптор физического устройства

Все дескрипторы имеют общий формат. Первый байт указывает длину дескриптора, а второй – его тип. Оформляя запрос Setup, мы резервируем буфер под эти структуры, указав лишь первые два поля, а устройство в ответ заполняет её данными. Если мы укажем длину меньше чем определено в
, хост посчитает запрос недействительным и отвергнет его. Однако, если размер больше ожидаемого, он проигнорирует лишние байты. Второй байт с типом введён для программного обеспечения, чтобы оно знало, с кем имеет дело. Вот какую информацию мы получим у себя в буфере:

DevDesc.gif


По смещению(4) здесь видим коды класса/подкласса устройства и используемый им протокол. Например устройство может принадлежать к классу HID, или накопителям Storage. На сайте USB-IF имеется , с которым имеет смысл ознакомиться.

Интересными являются и поля начиная со-смещения(14). Это т.н. индексы тесктовых строк, которые необходимо будет передать при оформлении запроса "Usb-String-Descriptor". Если в поле(14) хранится значение =1, то передав его в запрос, получим вендора устройства в текстовом виде. Значения этих индексов уникальны и не повторяются ни в одном из других дескрипторов, поскольку являются указателями на конкретную строку в глобальной таблице строк устройства.


3.2. Дескриптор конфигурации

Устройство USB может иметь несколько различных конфигураций, хотя это скорее исключение, чем правило – большинство устройств просты и имеют всего один конфиг. Радует то, что на этот запрос устройство возвращает сразу все остальные дескрипторы в данной конфигурации, и нам не нужно запрашивать каждый из них в отдельности. То-есть потянув один раз за конфиг, мы получим в нагрузку: Interface, Endpoint и String:


ConfigDesc.gif



3.3. Дескриптор интерфейса

В этом дескрипторе интерес представляет поле по смещению(4), в котором указывается общее кол-во конечных точек ЕР в данном устройстве. Обратите внимание, что это значение не учитывает точку конфигурирования ЕР0, а только число информационных точек In/Out.


FaceDesc.gif



3.4. Дескриптор описания конечных точек ЕР1+

Хост-контролёр использует информацию из этого дескриптора для определения требований к пропускной способности шины устройством. Если взведён старший бит(7) байта по смещению(2), то перед нами канал чтения информации Input, иначе это пайп записи Output. В младшей тетраде этого-же байта лежит порядковый номер точки ЕР1..ЕР16.

В байте по смещению(3) хранится режим передачи данных точкой, и для Usb-накопителей он будет Bulk. Далее идёт размер информационного пакета (как-правило 512-байт), и частота передачи в закодированном виде:


EpDesc.gif



3.5. Дескриптор текстовых строк

Когда в пакете "Setup" хост запрашивает дескриптор строк, то передаёт устройству полностью эту структуру. При этом младший байт поля "wValue" пакета Setup содержит индекс интересующей строки (смотри дескрипторы выше). Если индекс равен нулю, устройство вернёт коды используемых языков
. Код инглиша = 0x0409, и походу это единственный знакомый Win код. Если индекс больше нуля, запрашивается строка с указанным индексом. Кстати строки возвращаются в кодировке Юникод:

strDesc.gif


На этом с теорией покончим, и в следующей части займёмся программированием интерфейса USB. Что примечательно, в большинстве случаях мы можем не только читать дескрипторы, но и записывать в них информацию. Пока!
 
Последнее редактирование:

rusrst

Green Team
04.10.2020
22
27
BIT
0
Статья безусловно очень интересная, но хотелось бы внести несколько дополнений. Endpoint - это не конечное устройство, это конечная функция устройства. Т.е. при адресации устройств конечных устройств 127, а конечных точек гораздо больше. Конечные точки прием/передача не всегда должны идти парами (как пример ep01/ep81), технически возможен прием данных через ep01, возврат небольшого количества данных через ep0(которая есть всегда и двунаправленная). Или наоборот. То, что при запросе конфигураций выдаются все сразу с одной стороны плюс, но с другой размер ep0 - 64 байта, если конфигураций несколько и они со своими интерфейсами, то на устройстве весело с этими маленькими буферами работать...
Есть ещё win специфичные дескрипторы, для работы со стандартным драйвером winUSB, многие из используют :)

Вот ссылка как pnp менеджер win определяет подключенте нового устройства.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
305
1 378
BIT
318
@rusrst, хорошо, что вы в теме.
Здесь я пытался сжать спеку USB-2 из 650 страниц в одну статью, поэтому деталями (по-возможности) пришлось принебречь. На счёт, что поинты могут ходить не парами - не знаю.. Просто тогда получится устройство или только на чтение, или только на запись. Но поскольку речь здесь идёт о флешках, то это маловероятно.

Если есть ЕР01, то ЕР81 не может быть, а только ЕР82h (ну или наоборот 81h + 02h). Такую комбинацию возвращает обычно софт, и 81 указывается в HEX. Если посмотреть на таблицу дескриптора Endpoint выше, то во-втором байте там старший бит[7] определяет направление передачи (взведённый старший бит = 80h). Тогда получаем ЕР01h это точка(1)Out, а ЕР82h = точка(2)In. Кстати вот софтина от Microsoft, которая показывает содержимое всех дескрипторов.
 

Вложения

  • usbview.zip
    36,7 КБ · Просмотры: 273

rusrst

Green Team
04.10.2020
22
27
BIT
0
Устройство может общаться через ep0, она двунаправленная. Хоть и по умолчанию она для управления/конфигурирования, но если есть драйвер, то можно слать/принимать данные. Для этого нужно реализовать не стандартное, а дополнительное требование USB, но конечное устройство должно его понимать.
Честно, я сам не дошел до создания конечных точек на usb устройстве, остановился на ep0, но вся документация с которой я сталкивался не запрещала указывать два направления одной точки ep1 (т.е. ep01 и ep81, где, как вы верно заметили старший бит указывает направление).
Спасибо за ссылку, ознакомлюсь!

Вот прикрепляю выдержку из статьи, там как раз показа возможная конфигурация с ep1 двунаправленной
 

Вложения

  • IMG_20210419_143715.jpg
    IMG_20210419_143715.jpg
    47,4 КБ · Просмотры: 226

rusrst

Green Team
04.10.2020
22
27
BIT
0
@rusrst @Marylin А возможно ли перепрограммировать чип флешки или разделить на два логических диска?
Если Вы имеете ввиду диски C, E, D etc, то это чисто вопрос файловой системы. У стандартных fat16/32 таблица раздела находится вначале и чтобы проверить наличие второго раздела, надо читать за границами первого, а это нетривиально так то, но возможно.

Флешки перешивают, но это на любителей, я ни разу не делал. Но утилиты есть и даже в открытом доступе
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!