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

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

    Скидки до 10%

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

Статья USB Flash [часть 2] – программирование

..предыдущая часть – теория (рекомендуется к прочтению).

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

В этой части:

1. Функции менеджера настройки: SetupDi**(), CM_Get**() ;
2. DeviceIoControl() – формат пакета Setup ;
3. Практика – сбор информации об USB-Flash накопителях, безопасное извлечение устройств ;
4. Назначение и состав библиотеки WinUsb.dll ;
5. Сниффинг пакетов USB ;
6. Заключение.
------------------------------------------

1. Системная поддержка

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

Основным источником различного рода информации является системный реестр, куда диспетчер Plug-and-Play сохраняет свойства всех сконфигурированных девайсов для следующей загрузки ОС. Основная ветка находится по адресу [HKLM\SYSTEM\CurrentControlSet\Enum\USB]. Разбирать эту огромную базу вручную нереально, поскольку данные об одном устройстве могут быть размазаны тонким слоем по всему периметру реестра. Поэтому мы воспользуемся услугами системного менеджера конфигурации, функции которого сосредоточены в библиотеке setupapi.dll. Ознакомимся с основными из них..


1.1. Менеджер настройки устройств SetupDi**()

Пара функций SetupDiGetClassDevs() и SetupDiEnumDeviceInfo() позволяют организовать циклический поиск информации в реестре. Первая вернёт хэндл требуемого набора информации, а вторая – заполняет структуру SP_DEVINFO_DATA, которая служит проводником на конкретное устройство в этом наборе. Вот их прототипы:


C-подобный:
HDEVINFO SetupDiGetClassDevsA
  ClassGuid        dd 0   ;// указатель на GUID класса устройств
  Enumerator       dd 0   ;// 0
  hwndParent       dd 0   ;// 0
  Flags            dd 0   ;// DIGCF_PRESENT = искать только активные девайсы

Здесь, в первый аргумент мы должны положить указатель на идентификатор класса USB, а в последний – флаг, предписывающий искать реально имеющиеся на данный момент устройства (present). Поскольку сейчас нас интересует класс USB, то в первое поле кладём линк на 16-байтную константу GUID_DEVCLASS_USB. Если вы захотите собрать инфу о каких-либо иных классах устройств, то ознакомьтесь с полным списком из 46 возможных вариантов:


C-подобный:
;//
;// Следующие идентификаторы классов GUID определяет ОС.
;// Всего: 46 единиц для fn. SetupDiGetClassDevs()
;//
 GUID_DEVCLASS_1394             dd   0x6bdd1fc1, 0x11d0810f, 0x0008c7be, 0x2f09e22b
 GUID_DEVCLASS_1394DEBUG        dd   0x66f250d6, 0x4a647801, 0xa8ee39b1, 0x240b450a
 GUID_DEVCLASS_ADAPTER          dd   0x4d36e964, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_APMSUPPORT       dd   0xd45b1c18, 0x11d1c8fa, 0x0000779f, 0x30f505f8
 GUID_DEVCLASS_AVC              dd   0xc06ff265, 0x48f0ae09, 0x75162c81, 0x83ba7c3d
 GUID_DEVCLASS_BATTERY          dd   0x72631e54, 0x11d078a4, 0xaa00f7bc, 0x2ab3b700
 GUID_DEVCLASS_BLUETOOTH        dd   0xe0cbf06c, 0x4647cd8b, 0x3b268abb, 0x74f9f043
 GUID_DEVCLASS_CDROM            dd   0x4d36e965, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_COMPUTER         dd   0x4d36e966, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_DISKDRIVE        dd   0x4d36e967, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_DISPLAY          dd   0x4d36e968, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_ENUM1394         dd   0xc459df55, 0x11d1db08, 0xa00009b0, 0xf61f08c9
 GUID_DEVCLASS_FDC              dd   0x4d36e969, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_GPS              dd   0x6bdd1fc3, 0x11d0810f, 0x0008c7be, 0x2f09e22b
 GUID_DEVCLASS_HDC              dd   0x4d36e96a, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_HIDCLASS         dd   0x745a17a0, 0x11d074d3, 0xa000feb6, 0xda570fc9
 GUID_DEVCLASS_IMAGE            dd   0x6bdd1fc6, 0x11d0810f, 0x0008c7be, 0x2f09e22b
 GUID_DEVCLASS_KEYBOARD         dd   0x4d36e96b, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_LEGACYDRIVER     dd   0x8ecc055d, 0x11d1047f, 0x000037a5, 0xd13e75f8
 GUID_DEVCLASS_MEDIA            dd   0x4d36e96c, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_MODEM            dd   0x4d36e96d, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_MONITOR          dd   0x4d36e96e, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_MOUSE            dd   0x4d36e96f, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_MTD              dd   0x4d36e970, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_MULTIFUNCTION    dd   0x4d36e971, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_MULTIPORTSERIAL  dd   0x50906cb8, 0x11d1ba12, 0x00005dbf, 0x30f505f8
 GUID_DEVCLASS_NET              dd   0x4d36e972, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_NETCLIENT        dd   0x4d36e973, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_NETSERVICE       dd   0x4d36e974, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_NETTRANS         dd   0x4d36e975, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_NODRIVER         dd   0x4d36e976, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_PCMCIA           dd   0x4d36e977, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_PNPPRINTERS      dd   0x4658ee7e, 0x11d1f050, 0xc000bdb6, 0xa772a34f
 GUID_DEVCLASS_PORTS            dd   0x4d36e978, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_PRINTER          dd   0x4d36e979, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_PRINTERUPGRADE   dd   0x4d36e97a, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_PROCESSOR        dd   0x50127dc3, 0x415e0f36, 0xb34ccca6, 0x650b91be
 GUID_DEVCLASS_SBP2             dd   0xd48179be, 0x11d1ec20, 0xc000b8b6, 0xa772a34f
 GUID_DEVCLASS_SCSIADAPTER      dd   0x4d36e97b, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_SMARTCARDREADER  dd   0x50dd5230, 0x11d1ba8a, 0x00005dbf, 0x30f505f8
 GUID_DEVCLASS_SOUND            dd   0x4d36e97c, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_SYSTEM           dd   0x4d36e97d, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_TAPEDRIVE        dd   0x6d807884, 0x11cf7d21, 0x00081c80, 0x1803e12b
 GUID_DEVCLASS_UNKNOWN          dd   0x4d36e97e, 0x11cee325, 0x0008c1bf, 0x1803e12b
 GUID_DEVCLASS_USB              dd   0x36fc9e60, 0x11cfc465, 0x45445680, 0x00005453 ;//<---- Наш клиент
 GUID_DEVCLASS_VOLUME           dd   0x71a27cdd, 0x11d0812a, 0x0008c7be, 0x2f09e22b
 GUID_DEVCLASS_VOLUMESNAPSHOT   dd   0x533c5b84, 0x11d2ec70, 0xc0000595, 0xafde794f
 GUID_DEVCLASS_WCEUSBS          dd   0x25dbce51, 0x4a726c8f, 0x4cb56d8a, 0x35c84f2b

C-подобный:
BOOL SetupDiEnumDeviceInfo
  DeviceInfoSet    dd 0   ;// хэндл набора (возвращает предыдущая функция)
  MemberIndex      dd 0   ;// индекс устройства в наборе, начиная с нуля
  DeviceInfoData   dd 0   ;// указатель на структуру PSP_DEVINFO_DATA

struct SP_DEVINFO_DATA
  cbSize           dd  sizeof.SP_DEVINFO_DATA
  ClassGuid        db  16 dup(0)   ;// сюда получим GUID обнаруженного устройства
  DevInst          dd  0           ;// handle для fn.CM_GET**
  Reserved         dd  0           ;// резерв..
ends

Здесь уже интересней.. Индекс во-втором аргументе служит для организации циклического обхода всех устройств в данном наборе. При первом вызове, эта функция вернёт информацию не о Flash-накопителе, а о хост-контролёре, поскольку узел Node начинается именно с него и он тоже принадлежит к классу USB. Значит нужно будет увеличить индекс на 1 и повторно вызвать эту функцию. Тогда получим линк на сл.девайс в цепочке – корневой концентратор, и только на третий раз можем наткнуться на флеш. И то, если повезёт, т.к. контролёров у нас аж 5-штук, и у каждого своё древо устройств. Таким образом, индекс позволяет в цикле обходить всё древо, пока не найдём нужное устройство. Функция возвращает нуль, если в наборе больше нет устройств данного класса.

Отдельного внимания заслуживает и структура SP_DEVINFO_DATA. Она является ключевой для менеджера конфигурации СМ (config manager), а указатель на её содержимое, в виде аргумента ожидает почти весь состав библиотеки setupapi.dll. При каждом вызове с увеличением индекса, SetupDiEnumDeviceInfo() перезаписывает эту структуру новыми данными, где можно найти GUID очередного устройства, и что немаловажно – в члене "DevInst" хэндл девайса, для функций конфигуратора с префиксом CM_**().

По непонятной причине, в описании этой функции Microsoft не делает акцент на данном хэндле, лишь вскользь упоминая о нём. В результате, столкнувшись с диспетчером конфигурации вообще не ясно, от куда брать чёртов хэндл для CM_GET**(). Можно-же было обозвать это поле иначе, например прицепив к нему ярлык "бла-бла-бла Hndl", ан нет.. нужно, чтобы всё было через известное место.

Теперь, обнаружив хаб и порт нашей USB-Flash, мы можем послать по этому хэндлу команду на безопасное извлечение флеш, посредством функции CM_Request_Device_Eject(). Советую красным фломастером отметить структуру SP_DEVINFO_DATA и поле "DevInst" в ней!


1.2. Чтение свойств и характеристик устройств

Будем считать, что двойкой SetupDiGetClassDevs() и SetupDiEnumDeviceInfo() мы получили указатель на свой брелок в реестре. Следующие две функции позволяют вытягивать уже его свойства, в виде текстовых строк и hex-значений – это SetupDiGetDeviceRegistryProperty() и родственная ей SetupDiGetDevicePropertyW(). Учтите, что вторая возвращает строки в кодировке юникод (нет ansi версии), выводить на консоль которые можно при помощи printf() со-спецификатором %ls (long-string).

Самих-же свойств – как звёзд на небе, и я собрал их в отдельный инклуд (см.скрепку в конце статьи).
Для первой функции константы начинаются с префикса SPDRP_**, а для второй – DEVPKEY_Device_**. Вот их прототипы:


C-подобный:
BOOL SetupDiGetDeviceRegistryPropertyA  ;//<--- Ansi функция
  DeviceInfoSet        dd 0  ;// хэндл набора от SetupDiGetClassDevs()
  DeviceInfoData       dd 0  ;// указатель на структуру PSP_DEVINFO_DATA
  Property             dd 0  ;// ключ SPDRP_** (требуемое свойство)
  PropertyRegDataType  dd 0  ;// 0
  PropertyBuffer       dd 0  ;// адрес приёмного буфера для строки/значения
  PropertyBufferSize   dd 0  ;// размер этого буфа = 256
  RequiredSize         dd 0  ;// 0

BOOL SetupDiGetDevicePropertyW  ;//<--- Unicode функция
  DeviceInfoSet        dd 0  ;// хэндл набора от SetupDiGetClassDevs()
  DeviceInfoData       dd 0  ;// указатель на структуру PSP_DEVINFO_DATA
  PropertyKey          dd 0  ;// ключ DEVPKEY_Device_**
  PropertyType         dd 0  ;// указатель на переменную для идентификатора (не важен, но для вызова нужен)
  PropertyBuffer       dd 0  ;// адрес приёмного буфера для строки
  PropertyBufferSize   dd 0  ;// размер этого буфа = 256*2
  RequiredSize         dd 0  ;// 0
  Flags                dd 0  ;// 0

SetupDi.png


Судя по приведённой выше схеме, чтобы найти устройство USB-Flash среди всех остальных, придётся искать его по какому-то признаку, ведь SetupDiEnumDevsInfo() будет возвращать и контролёры, и хабы, и (если есть) иные физические устройства, типа: колонки, веб-камеры и прочее барахло. Значит нужен штам, который безошибочно определит именно Mass-Storage-Device. Хорошей идеей будет использовать в этом качестве имя сервиса обслуживающего данное устройство – в текстовом виде его возвращает SetupDiGetDeviceRegistryProperty() с ключом SPDRP_SERVICE=4. Наши USB-Flash драйвы работают под управлением сервиса "USBSTOR" (драйвер), на который и сделаем ставку. Вот фрагмент кода обнаружения USB-Flash, по указанному алго:


C-подобный:
.data
devData    SP_DEVINFO_DATA
hndl       dd  0
index      dd  0
buff       rb  1024
;//------------------
.code
start:
;// Получаем хэндл класса устройств USB
        invoke  SetupDiGetClassDevs,GUID_DEVCLASS_USB,0,0,DIGCF_PRESENT
        mov     [hndl],eax

;// Поиск в цикле Flash-накопителя – заполняем структуру SP_DEVINFO_DATA
@findNextDevice:
        invoke  SetupDiEnumDeviceInfo,[hndl],[index],devData
        or      eax,eax       ;// выйти, если больше нет устройств данного класса
        jz      @exit

;// Запрашиваем имя сервиса, который обслуживает данное устройство
        invoke  SetupDiGetDeviceRegistryProperty,[hndl], devData, SPDRP_SERVICE,\
                                                  0,buff,256,0
        call    @f
        db      'USBSTOR'     ;// маска для поиска
@@:     pop     esi           ;// её адрес
        mov     edi,buff      ;// адрес найденного сервиса
        mov     ecx,7         ;// длина строки/маски
        repe    cmpsb         ;// сравнить строки EDI с ESI!
        or      ecx,ecx       ;// по всей строке совпало?
        jnz     @next         ;// пропустить, если нет
;//------------------------
;// Мы здесь, значит нашли USB-Flash - читаем нужные его свойства!
;//------------------------
        invoke  SetupDiGetDeviceProperty,,,,
        invoke  SetupDiGetDeviceRegistryProperty,,,,
        jmp     @exit

@next:  inc     [index]            ;// если облом, индекс +1 (инкремент)
        jmp     @findNextDevice    ;// продолжить поиск..

@exit: cinvoke  exit,0


1.3. Порт и имя концентратора Hub

Чтобы посылать устройству USB-Flash пакеты "Setup", нам нужны 2 свойства девайса – имя хаба в системном пространстве имён, и номер его порта, куда подключена флеш. Это основная проблема, с которой сталкиваемся при программировании интерфейса USB. Конечно-же можно запросить эти данные у стороннего софта, но это не наши методы. Если мы хотим написать универсальный софт, то определять концентратор и порт нужно динамически, чтобы код не был привязан к конкретной конфигурации и одному устройству. Иначе, если пользователь воткнёт брелок в другой порт Usb – вся конструкция рухнет, как карточный дом.

Обычно с номером порта проблем не возникает – после того, как мы найдём устройство, порт возвращает SetupDiGetDeviceRegistryProperty() с ключом SPDRP_ADDRESS. А вот имя хаба (под которым он числится в системе) выглядит как субстанция подозрительного характера, типа:

\\?\USB#ROOT_HUB20#4&17a4e2cc&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}

Это-же имя можно обнаружить и в пространстве имён системных объектов, если запустить софт WinObj (см.скрин ниже). Обратите внимание на идентификатор GUID хабов – у всех версий концентраторов USB-1,2,3 он одинаковый и имеет константу {f18a0e88-c30c-11d0-8815-00a0c906bed8}:


WinObj.png


Проблема в том, что в таком виде это имя не возвращает ни один из ключей в запросах характеристик устройств. Стандартными средствами я не нашёл возможности получить и более дружелюбное его имя: \Device\USBPDO-4 (Physical Device Object).

Однако половину этой строки можно заполучить посредством вызова SetupDiGetDeviceProperty() с ключом DEVPKEY_Device_Parent (родитель устройства). Этот вызов возвращает стринг вида: USB\ROOT_HUB20\4&17a4e2cc&0, т.е. вместо хештегов(#) – прямые слэши(\), и соответственно придётся пройтись по этой строке, заменив их на решётки(#).

Более того, в своём выхлопе ключ не возвращает и идентификатор GUID, поэтому его нужно будет добавить в ручную, путём конкатенации (соединении) двух строк через lstrcat(). Другими словами, помещаем в секцию-данных заготовку в требуемом формате, и скопировав в буфер "hubStr" имя хаба, присавокупляем к нему строку GUID'а. Вот пример этого участка кода:


C-подобный:
.data
hubName    db  '\\?\'
hubStr     db  64 dup(0)
hubGuid    db  '#{f18a0e88-c30c-11d0-8815-00a0c906bed8}',0

devData    SP_DEVINFO_DATA
hndl       dd  0
dpt        dd  0
buff       rb  1024
;//------------------
.code
start:
;//.....
        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_Parent,dpt,buff,256*2,0,0  ;// в буфере имя хаба в юникоде

        call    Unicode2Ansi      ;// переводим имя в ansi
        push    ecx               ;// в ECX процедура вернёт длину имени хаба
        mov     eax,'\#'          ;// AH = что искать, AL = на что менять
        push    eax               ;// аргумент для "ParseBuffString"
        call    ParseBuffString   ;// приводим имя хаба в нужный вид!
        pop     ecx               ;// длина строки с именем хаба
        mov     esi,buff          ;// источник
        mov     edi,hubStr        ;// приёмник
        rep     movsb             ;// скопируем имя из буфера, к себе в заготовку
        invoke  lstrcat,hubName,hubGuid   ;//<--- добавим к заготовке GUID хаба
       cinvoke  printf,<10,'     Kernel name.....:  %s',0>,hubName  ;// вывод на консоль
;//.....

;//----- Вспомогательные процедуры --------------
proc    Unicode2Ansi
        xor      ecx,ecx        ;// очистить ECX под размер строки
        mov      esi,buff       ;// источник
        mov      edi,esi        ;//  ..он-же приёмник.
@@:     lodsw                   ;// берём из ESI по 2-байта юникоде
        or       ax,ax          ;// если нуль,
        je       @f             ;//  ..выйти из цикла.
        stosb                   ;// записать 1-байт ansi в приёмник
        inc      ecx            ;// размер строки +1
        jmp      @b             ;// пройтись по всей строке..
@@:     mov      byte[edi],0    ;// вставить в хвост терминальный нуль
        ret                     ;// выход из процедуры – в ECX длина строки ansi
endp
proc    ParseBuffString char    ;//<---- меняет в строке символы
        invoke  lstrlen,buff    ;// получить длину строки
        xchg    ecx,eax         ;// отправить её в ECX
        mov     edi,buff        ;// источник для scasb (поиск байта)
        mov     eax,[char]      ;// EAX = аргумент (AL = что ищем, AH = на что меняем)
@@:     repne   scasb           ;// поиск AL в строке EDI..
        or      ecx,ecx         ;// конец строки?
        je      @f              ;// да – на выход!
        mov     byte[edi-1],ah  ;// иначе: перезаписать найденный символ
        jmp     @b              ;// на повтор..
@@:     ret                     ;// return
endp

Так мы получим имя хаба, и теперь можно открыть его функцией CreateFile().
Полученный в ответ хэндл позволит через DeviceIoControl() отправлять любые пакеты "Setup" устройствам USB, в том числе и на чтение дескрипторов.


2. DeviceIoControl() – формат пакета "Setup"

Этот гигант способен на многое, но в данном случае мы будем использовать его для отправки устройству запросов на чтение 5-ти стандартных дескрипторов флешки. Управляющий код для функции DeviceIoControl() должен быть IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION с константой 0x00220410. Это самый простой вариант чтения, и доступен он даже пользователю с правами юзера.

Открыв свободный доступ к дескрипторам для прикладных программ, инженеры ограничили его на уровне драйверов. Так, с этим управляющим кодом мы можем только читать без записи (в поле bmRequestType принудительно выставляется 0x80), и только дескрипторы (bRequest сбрасывается до значения 0x06). Если-же нужен доступ на запись, то можно воспользоваться кодом IOCTL_USB_USER_REQUEST = 0x00220438. Вот как выглядит стандартный пакет чтения дескрипторов:


SetupPacket.png


На программной уровне, для него предусмотрена одноимённая структура, которую (с учётом ограничения драйверов) я оформил так:


C-подобный:
struct USB_SETUP_PACKET
  bmRequestType    db  0x80   ;// тип запроса: стандартный, к хосту
  bRequest         db  0x06   ;// код запроса: дескрипторы
  wValueLow        db  0      ;// 0
  wValueHigh       db  0      ;// Device/Config/Interface/Endpoint/String descr
  wIndex           dw  0      ;// 0 или индекс строки в базе
  wLenght          dw  0      ;// число байт для приёма/передачи
ends

Теперь этот пакет необходимо завернуть в конверт Request, в котором указывается USB-порт назначения, и сразу-же после пакета следует буфер "Data", для приёма запрашиваемых дескрипторов. Функция DeviceIoControl() на входе ожидает именно конверт Request, а не распакованный пакет. Размер приёмного буфа можно ограничить одним кило, хотя встречаются исходники, где программисты выделяют и по 2, и даже 4 Кб. К чему такая расточительность непонятно, т.к. размер одного дескриптора макс. 64-байта. Если мы и запросим буфер сразу для 10-ти дескрипторов, то в сумме получим всего 640 байт, но никак не 4К.


C-подобный:
struct USB_DESCRIPTOR_REQUEST
  ConnectionIndex      dd  0              ;// USB-порт
  SetupPacket          USB_SETUP_PACKET   ;// вложенный в эту структуру пакет
  Data                 db  1024 dup(0)    ;// на выходе, содержит извлечённые дескрипторы
ends

В
предыдущей статье с теорией говорилось, что на один запрос дескриптора-конфигурации, устройство возвращает в придачу сразу все остальные дескрипторы, а это: Interface, Endpoint и String. Значит когда мы оформляем пакеты, можно использовать следующие предопределённые данные (красным выделены поля в структуре):

PacketValue.png


Два первых поля здесь драйвер всё-равно проигнорирует и запишет туда 0x80 (стандартный запрос от устройства к хосту) и 0x06 = чтение дескрипторов. Далее, в аргументах мы подставляем константы требуемых дескрипторов и размеры буферов для приёма данных. Исключением из правил является запрос на чтение строк, когда младший байт аргумента ожидает идентификатор языка 0x0409 = ингиш, и непосредственно индекс запрашиваемой строки. Пример вызова DeviceIoControl() с этапом оформления пакета представлен ниже:


C-подобный:
.data
hubName    db  128 dup(0)  ;// подготовленная строка с именем хаба
usbPort    dd  0           ;// здесь должен лежать номер порта нашей Flash
hubHndl    dd  0           ;// сюда вернётся хэндл хаба
retSize    dd  0           ;// будет размером выхлопа
align      16
request    USB_DESCRIPTOR_REQUEST  ;// конверт запроса
;//--------------
.code
start:
;//......
;// Открываем хаб, чтобы получить его хэндл
        invoke  CreateFile,hubName,GENERIC_READ + GENERIC_WRITE,\
                                   FILE_SHARE_READ + FILE_SHARE_WRITE,\
                                   0,OPEN_EXISTING,0,0
        mov     [hubHndl],eax

;// Оформляем пакет "Setup"
        mov     eax,[usbPort]
        mov     [request.ConnectionIndex],eax
        mov     [request.SetupPacket.wValueHigh], USB_DEVICE_DESCRIPTOR_TYPE   ;//<-- тип дескриптора
        mov     [request.SetupPacket.wValueLow ], 0
        mov     [request.SetupPacket.wIndex    ], 0
        mov     [request.SetupPacket.wLenght   ], sizeof.USB_DEVICE_DESCRIPTOR

;// Обращаемся к драйверу с запросом на чтение дескриптора
        invoke  DeviceIoControl,[hubHndl],IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST, retSize, 0

;// Выводим содержимое дескриптора на консоль
        mov     esi,request.Data                ;//<---- указатель на буфер с данными
        movzx   eax,[esi + USB_DEVICE_DESCRIPTOR.bcdUSB]
        movzx   ebx,al
        shr     eax,8
        movzx   ecx,[esi + USB_DEVICE_DESCRIPTOR.bNumConfigurations]
        movzx   edx,[esi + USB_DEVICE_DESCRIPTOR.bMaxPacketSize]
       cinvoke  printf,<10,'     Device type.....:  USB-%x.%x',\
                        10,'     Total config....:  %d',\
                        10,'     EP0 packet size.:  %d byte',0>,eax,ebx,ecx,edx


3. Практика – сбор информации об USB-Flash, безопасное извлечение устройств

В приведённом ниже примере я собрал всё сказанное под один капот, и постарался вывести на консоль наиболее значимую информацию о своей флешке. В коде присутствуют ещё парочка функций, которые являются вспомогательными, т.е. можно обойтись без них. Первая GetLogicalDriveStrings() возвращает имена всех томов, которые смонтированы на данный момент в системе. К примеру на своём узле я получил от этой функции буквы: C:, D:, E:, F:

Чтобы узнать, какая из них принадлежит USB-Flash, можно в цикле вскармливая эти литеры, запрашивать GetDriveType() проверяя её выхлоп на константу DRIVE_REMOVABLE=2 (съёмное устройство). Теперь у нас есть буква флеш-драйва, по которой функцией QueryDosDevice() можно узнать имя объекта, в системном пространстве имён типа: Device\HarddiskVolume2.

В таком формате его потребует CreateFile(), чтобы открыть флеш и через GetVolumeInformation() собрать информацию о файловой системе накопителя, его общим/свободном пространстве, метке и серийнике. Вот собственно и исходник:


C-подобный:
format   pe console
include 'win32ax.inc'
entry    start
;//----------
section '.inc' data readable
include 'equates\setupapi.inc'
include 'equates\usbstor.inc'
;//----------
.data
ntName     db  '\\?\GLOBALROOT'
dosName    db  32 dup(0)

hubName    db  '\\?\'
hubStr     db  64 dup(0)
hubGuid    db  '#{f18a0e88-c30c-11d0-8815-00a0c906bed8}',0

hubHndl    dd  0
usbPort    dd  0
epCount    dd  0

volume     dd  0
volTotal   dd  0,0
volFree    dd  0,0
volLabel   db  16 dup(0)
volFsys    db  08 dup(0)
volSn      dd  0
maxLen     dd  0
flags      dd  0
sysTime    SYSTEMTIME

gByte      dd  1024*1024*1024
fResult1   dq  0
fResult2   dq  0

hndl       dd  0
index      dd  0
dpt        dd  0
retSize    dd  0

align      16
request    USB_DESCRIPTOR_REQUEST
align      16
devData    SP_DEVINFO_DATA
align      16
buff       dd  0
;//----------
.code
start:  invoke  SetConsoleTitle,<'*** USB-Flash storage info ***',0>

        invoke  GetLogicalDriveStringsA,64,buff
        mov     esi,buff
@@:     push    esi
        invoke  GetDriveType,esi
        pop     esi
        cmp     eax,DRIVE_REMOVABLE  ;// съёмный диск?
        je      @ok                  ;// да!
        add     esi,4
        cmp     byte[esi],0
        jne     @b

       cinvoke  printf,<10,' ERROR! USB-Flash not found.',0>
        jmp     @exit

@ok:    push    esi
        mov     eax,[esi]
        mov     [volume],eax
       cinvoke  printf,<10,' Volume DOS-name.....:  %s',0>,esi
        pop     esi
        mov     byte[esi+2],0
        invoke  QueryDosDevice,esi,dosName,32
       cinvoke  printf,<10,' Volume kernel name..:  %s',10,0>,ntName

;//**** Получаем хэндл набора USB ******************
        invoke  SetupDiGetClassDevs,GUID_DEVCLASS_USB,0,0,DIGCF_PRESENT
        mov     [hndl],eax

;//**** Поиск USB-Flash в цикле ********************
@findNextDevice:
        invoke  SetupDiEnumDeviceInfo,[hndl],[index],devData
        or      eax,eax
        jz      @exit

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,SPDRP_SERVICE,\
                                                  0,buff,256,0
        call    @f
        db      'USBSTOR'
@@:     pop     esi
        mov     edi,buff
        mov     ecx,7
        repe    cmpsb
        or      ecx,ecx
        jnz     @next

;//**** Нашли! Выводим информацию из реестра **************
       cinvoke  printf,<10,' Driver info:',0>

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_DriverProvider, dpt,buff,256*4,0,0
        invoke  CharToOem,buff,buff
       cinvoke  printf,<10,'     Provider........:  %ls',0>,buff

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_DriverInfPath, dpt,buff,256*4,0,0
       cinvoke  printf,<10,'     File name.......:  %ls',0>,buff

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_DriverVersion, dpt,buff,256*4,0,0
       cinvoke  printf,<10,'     Version.........:  %ls',0>,buff

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_DriverDate, dpt,buff,256*4,0,0
        invoke  FileTimeToSystemTime,buff,sysTime
        movzx   eax,[sysTime.wDay]
        movzx   ebx,[sysTime.wMonth]
        movzx   ecx,[sysTime.wYear]
       cinvoke  printf,<10,'     Create date.....:  %02d.%02d.%d',0>,eax,ebx,ecx

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_DriverRank, dpt,buff,256*4,0,0
       cinvoke  printf,<10,'     Driver rank.....:  0x%08x',0>,[buff]

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                  SPDRP_SERVICE, 0,buff,256,0
        invoke  CharToOem,buff,buff
       cinvoke  printf,<10,'     System service..:  %s',0>,buff
        mov     [buff],0

;//**********************************
       cinvoke  printf,<10,10,' Hub info:',0>

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                  SPDRP_LOCATION_PATHS,0,buff,256,0
        mov     eax,'# '
        push    eax
        call    ParseBuffString
       cinvoke  printf,<10,'     Location path...:  %s',0>,buff

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_Parent,dpt,buff,256*4,0,0
        call    Unicode2Ansi
        push    ecx
        mov     eax,'\#'
        push    eax
        call    ParseBuffString
        pop     ecx
        mov     esi,buff
        mov     edi,hubStr
        rep     movsb
        invoke  lstrcat,hubName,hubGuid
       cinvoke  printf,<10,'     Kernel name.....:  %s',0>,hubName

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                  SPDRP_COMPATIBLEIDS,0,buff,256,0
        mov     eax,'& '
        push    eax
        call    ParseBuffString
       cinvoke  printf,<10,'     Device class....:  %s',0>,buff

;//*****************************
       cinvoke  printf,<10,10,' Device info:',0>

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                  SPDRP_LOCATION_INFORMATION,0,buff,256,0
        invoke  CharToOem,buff,buff
       cinvoke  printf,<10,'     Location path...:  %s',0>,buff

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                  SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,\
                                                  0,buff,256,0
        invoke  CharToOem,buff,buff
       cinvoke  printf,<10,'     Kernel name.....:  %s',0>,buff


        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                 SPDRP_ADDRESS,0,buff,256,0
        mov     eax,dword[buff]
        mov     [usbPort],eax     ;//<---------- Запомнить порт устройства!!!
       cinvoke  printf,<10,'     Hub port index..:  %d',0>,[buff]

;//*****************************
       cinvoke  printf,<10,10,' Vendor info:',0>

        invoke  SetupDiGetDeviceRegistryProperty,[hndl],devData,\
                                                  SPDRP_DEVICEDESC,0,buff,256,0
        invoke  CharToOem,buff,buff
       cinvoke  printf,<10,'     Device type.....:  %s',0>,buff

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_BusReportedDeviceDesc, dpt,buff,256*4,0,0
        invoke  CharToOem,buff,buff
       cinvoke  printf,<10,'     Device name.....:  %ls',0>,buff

        invoke  SetupDiGetDeviceProperty,[hndl],devData,\
                                          DEVPKEY_Device_InstanceId, dpt,buff,256*4,0,0
        call    Unicode2Ansi
        mov     edi,buff
        mov     al,'&'
        repne   scasb
        mov     byte[edi-1],' '
        mov     al,'\'
        repne   scasb
        mov     byte[edi-1],0
        push    edi
       cinvoke  printf,<10,'     Vendor Id.......:  %s',0>,buff
        pop     edi
       cinvoke  printf,<10,'     Serial number...:  %s',0>,edi

;//******************************************
       cinvoke  printf,<10,10,' Volume info:',0>

        invoke  GetDiskFreeSpaceEx,volume,0,volTotal,volFree
        invoke  GetVolumeInformation,volume,volLabel,16,volSn,maxLen,flags,volFsys,8
        finit
        fild    qword[volTotal]
        fidiv   [gByte]
        fstp    [fResult1]
        fild    qword[volFree]
        fidiv   [gByte]
        fstp    [fResult2]
       cinvoke  printf,<10,'     Capacity........:  %.2f Gb',\
                        10,'     Free space......:  %.2f Gb',0>,\
                                 dword[fResult1],dword[fResult1+4],\
                                 dword[fResult2],dword[fResult2+4]
        mov     eax,[volSn]
        movzx   ebx,ax
        shr     eax,16
       cinvoke  printf,<10,'     Volume label....:  %s',\
                        10,'     Volume serial...:  %04X-%04X',\
                        10,'     File system.....:  %s',0>,\
                        volLabel,eax,ebx,volFsys

;//######[ READ DESCRIPTORS ]##########################
       cinvoke  printf,<10,10,' [xxxxxxxxxxxxxxx STANDART DESCRIPTORS xxxxxxxxxxxxxx]',10,0>

        invoke  CreateFile,hubName,GENERIC_READ + GENERIC_WRITE,\
                                   FILE_SHARE_READ + FILE_SHARE_WRITE,\
                                   0,OPEN_EXISTING,0,0
        mov     [hubHndl],eax

;// Запрос стандартных дескрипторов устройства
        mov     eax,[usbPort]
        mov     [request.ConnectionIndex],eax
        mov     [request.SetupPacket.bRequest  ], GET_DESCRIPTOR

        mov     [request.SetupPacket.wValueHigh], USB_DEVICE_DESCRIPTOR_TYPE
        mov     [request.SetupPacket.wValueLow ], 0
        mov     [request.SetupPacket.wIndex    ], 0
        mov     [request.SetupPacket.wLenght   ], sizeof.USB_DEVICE_DESCRIPTOR

        invoke  DeviceIoControl,[hubHndl],IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST, retSize, 0
        mov     esi,request.Data
        movzx   eax,[esi + USB_DEVICE_DESCRIPTOR.bcdUSB]
        movzx   ebx,al
        shr     eax,8
        movzx   ecx,[esi + USB_DEVICE_DESCRIPTOR.bNumConfigurations]
        movzx   edx,[esi + USB_DEVICE_DESCRIPTOR.bMaxPacketSize]
       cinvoke  printf,<10,'     Device type.....:  USB-%x.%x',\
                        10,'     Total config....:  %d',\
                        10,'     EP0 packet size.:  %d byte',0>,eax,ebx,ecx,edx

;// Запрос дескриптора "конфигурации" и сразу всех остальных
        mov     [request.SetupPacket.wValueHigh], USB_CONFIG_DESCRIPTOR_TYPE
        mov     [request.SetupPacket.wValueLow ], 0  ;// Номер конфигурации
        mov     [request.SetupPacket.wIndex    ], 0
        mov     [request.SetupPacket.wLenght   ], sizeof.USB_CONFIGURATION_DESCRIPTOR * 16

        invoke  DeviceIoControl,[hubHndl],IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST, retSize, 0
        mov     esi,request.Data
        movzx   eax,[esi + USB_CONFIGURATION_DESCRIPTOR.MaxPower]
        shl     eax,1
       cinvoke  printf,<10,'     Max power.......:  %d mA',0>,eax

;// Читаем сразу дескриптор "интерфейса"
        mov     esi,request.Data
        add     esi,sizeof.USB_CONFIGURATION_DESCRIPTOR
        push    esi
        movzx   eax,[esi + USB_INTERFACE_DESCRIPTOR.bNumEndpoints]
        mov     [epCount],eax
        dec     [epCount]       ;//<------- Всего конечных точек для цикла!!!
        mov     ebx,eax
        inc     ebx
       cinvoke  printf,<10,'     Total Endpoints.:  %d + EP0 = %d',0>,eax,ebx

;// Читаем сразу дескрипторы "конечных точек EP"
        jmp     @f
bulk    db      'Bulk',0
iso     db      'Isochronius',0
input   db      'In',0
output  db      'Out',0

@@:     pop     esi
        add     esi,sizeof.USB_INTERFACE_DESCRIPTOR
        push    esi
        movzx   eax,[esi + USB_ENDPOINT_DESCRIPTOR.bEndpointAddress]
        movzx   ebx,[esi + USB_ENDPOINT_DESCRIPTOR.wMaxPacketSize]
        movzx   ecx,[esi + USB_ENDPOINT_DESCRIPTOR.bmAttributes]
        movzx   edi,[esi + USB_ENDPOINT_DESCRIPTOR.bInterval]
        imul    edi,125
        mov     edx,input
        test    al,10000000b   ;//<---- проверить In или Out
        jnz     @f
        mov     edx,output
@@:     and     al,11b         ;//<---- оставить только номер
        mov     ebp,bulk
        cmp     ecx,2          ;//<---- Bulk или изохронная
        je      @f
        mov     ebp,iso
@@:    cinvoke  printf,<10,'     Endpoint info...:  EP%d, 0.%d ms, %d byte, %s, %s',0>,eax,edi,ebx,ebp,edx

        pop     esi      ;//<---- Цикл вывода EP1..EP16 по счётчику
@printEP_info:
        add     esi,sizeof.USB_ENDPOINT_DESCRIPTOR
        push    esi
        movzx   eax,[esi + USB_ENDPOINT_DESCRIPTOR.bEndpointAddress]
        movzx   ebx,[esi + USB_ENDPOINT_DESCRIPTOR.wMaxPacketSize]
        movzx   ecx,[esi + USB_ENDPOINT_DESCRIPTOR.bmAttributes]
        movzx   edi,[esi + USB_ENDPOINT_DESCRIPTOR.bInterval]
        imul    edi,125
        mov     edx,input
        test    al,10000000b
        jnz     @f
        mov     edx,output
@@:     and     al,11b
        mov     ebp,bulk
        cmp     ecx,2
        je      @f
        mov     ebp,iso
@@:    cinvoke  printf,<10,'     Endpoint info...:  EP%d, 0.%d ms, %d byte, %s, %s',0>,eax,edi,ebx,ebp,edx

        pop     esi
        dec     [epCount]        ;//<--- есть ещё точки?
        jnz     @printEP_info    ;//<--- да, на повтор..

;// Запрос дескриптора "строк"
        mov     [request.SetupPacket.wValueHigh], USB_STRING_DESCRIPTOR_TYPE
        mov     [request.SetupPacket.wValueLow ], 2       ;// Индекс строки
        mov     [request.SetupPacket.wIndex    ], 0x0409  ;// ID языка
        mov     [request.SetupPacket.wLenght   ], 128

        invoke  DeviceIoControl,[hubHndl],IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST,\
                                request, sizeof.USB_DESCRIPTOR_REQUEST, retSize, 0
        mov     esi,request.Data
        lea     esi,[esi + USB_STRING_DESCRIPTOR.bString]
       cinvoke  printf,<10,10,'     String..........:  %ls',10,0>,esi


;//********* Безопасное извлечение устройства *************
       cinvoke  printf,<10,' Safe eject Usb-Flash:  y/n... ',0>
       cinvoke  getch
        cmp     al,'y'
        jne     @f

       cinvoke  printf,<'y',0>
        mov     eax,[devData.DevInst]
        invoke  CM_Request_Device_Eject,eax,0,0,0,0

@@:    cinvoke  printf,<10,' Press any key for exit...',10,0>

;//**** Индексированный поиск устройств, пока не найдём.
@next:  inc     [index]
        jmp     @findNextDevice

@exit: cinvoke  getch   ;//<---- GAME OVER!!!
       cinvoke  exit,0

;//###########<--- ПРОЦЕДУРЫ --->#######################
proc    Unicode2Ansi
        xor      ecx,ecx
        mov      esi,buff
        mov      edi,esi
@@:     lodsw
        or       ax,ax
        je       @f
        stosb
        inc      ecx
        jmp      @b
@@:     mov      byte[edi],0
        ret
endp
proc    ParseBuffString char
        invoke  lstrlen,buff
        xchg    ecx,eax
        mov     edi,buff
        mov     eax,[char]
@@:     repne   scasb
        or      ecx,ecx
        je      @f
        mov     byte[edi-1],ah
        jmp     @b
@@:     ret
endp
;//---------------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',setupapi,'setupapi.dll',\
         kernel32,'kernel32.dll',user32,'user32.dll'
import   msvcrt, printf,'printf',fflush,'fflush',getch,'_getch',exit,'exit'
import   setupapi,SetupDiGetClassDevs,'SetupDiGetClassDevsA',\
                  SetupDiEnumDeviceInfo,'SetupDiEnumDeviceInfo',\
                  SetupDiGetDeviceProperty,'SetupDiGetDevicePropertyW',\
                  SetupDiGetDeviceRegistryProperty,'SetupDiGetDeviceRegistryPropertyA',\
                  CM_Request_Device_Eject,'CM_Request_Device_EjectA'

include 'api\kernel32.inc'
include 'api\user32.inc'

Result.png


Здесь видно, что у моего устройства USB-2.0 всего две конечные точки: канал ЕР1 служит для записи(Out), а канал ЕР2 на чтение(In). Если у кого имеется флешка USB-3, просьба показать кол-во её конечных точек – программа должна определить их сама, для чего предусмотрен цикл. Интересует, два или четыре пайпа в устройствах USB-3.


4. Функции из библиотеки WinUsb.dll

В арсенале системы имеется библиотека winUsb.dll, которая ходит со-своим драйвером winUsb.sys. Она была включена в состав ОС для устройств, у которых нет класс-драйвера в системе. К числу таких девайсов можно отнести платы Arduino, сотовые телефоны, собранные "на коленке" разнообразные поделки на PIC-контролёрах, и прочие.

Библиотека выдаёт на экспорт всего 22 функции, но они предоставляют более гибкий и удобный интерфейс общения с устройствами. Либа прекрасно документирована на сайте MSDN, поэтому с прототипами функций проблем не возникает, а полный их список можно посмотреть в любом дизассемблере типа Win32Dasm:


Код:
+++++++++++++++++++ EXPORTED FUNCTIONS ++++++++++++++++++
Number of Functions = 0022 (decimal)

 Addr:10002530 Ord:   1 (0001h) Name: WinUsb_AbortPipe
 Addr:1000231C Ord:   2 (0002h) Name: WinUsb_ControlTransfer
 Addr:100025F4 Ord:   3 (0003h) Name: WinUsb_FlushPipe
 Addr:100016C0 Ord:   4 (0004h) Name: WinUsb_Free
 Addr:1000172E Ord:   5 (0005h) Name: WinUsb_GetAssociatedInterface
 Addr:10001B27 Ord:   6 (0006h) Name: WinUsb_GetCurrentAlternateSetting
 Addr:10001984 Ord:   7 (0007h) Name: WinUsb_GetDescriptor
 Addr:100026B8 Ord:   8 (0008h) Name: WinUsb_GetOverlappedResult
 Addr:10001DCF Ord:   9 (0009h) Name: WinUsb_GetPipePolicy
 Addr:10001FE4 Ord:  10 (000Ah) Name: WinUsb_GetPowerPolicy
 Addr:100012A8 Ord:  11 (000Bh) Name: WinUsb_Initialize
 Addr:1000120D Ord:  12 (000Ch) Name: WinUsb_ParseConfigurationDescriptor
 Addr:100011C5 Ord:  13 (000Dh) Name: WinUsb_ParseDescriptors
 Addr:100018C0 Ord:  14 (000Eh) Name: WinUsb_QueryDeviceInformation
 Addr:1000185B Ord:  15 (000Fh) Name: WinUsb_QueryInterfaceSettings
 Addr:10001BE4 Ord:  16 (0010h) Name: WinUsb_QueryPipe
 Addr:100020BA Ord:  17 (0011h) Name: WinUsb_ReadPipe
 Addr:1000246C Ord:  18 (0012h) Name: WinUsb_ResetPipe
 Addr:10001A63 Ord:  19 (0013h) Name: WinUsb_SetCurrentAlternateSetting
 Addr:10001C98 Ord:  20 (0014h) Name: WinUsb_SetPipePolicy
 Addr:10001EAF Ord:  21 (0015h) Name: WinUsb_SetPowerPolicy
 Addr:100021EB Ord:  22 (0016h) Name: WinUsb_WritePipe

Обратите внимание, что библиотека помимо чтения позволяет производить и запись в пайпы, причём с правами смертного юзера. Проблема в том, что например, на моей машине драйвер winUsb.sys не подгружается автоматически при запуске системы, и не факт, что он будет загружен на удалённом узле. Соответственно, чтобы использовать эти функции, нужно будет сначала подтянуть драйвер в оперативную память, а это не всегда удобно. Вывод – если для устройства в системе имеются класс-драйверы (типа HID, Storage, Audio, коммуникации CDC и прочие), то лучше не рисковать и использовать их, по приведённому выше алго. К winUsb.dll следует обращаться в крайних случаях, когда других вариантов уже нет.


5. Снифф пакетов USB

Интересную информацию можно почерпнуть посредством снифферов USB. В природе, лично мне встречались несколько таких зверьков, но больше всех порадовал "USB-Lyzer". Во-первых, он уже в коробке идёт со-встроенным драйвером usbPcap.sys, так-что установив этот софт появляется возможность ловить USB-пакеты и в завоевавшем рынок Wireshark. Во-вторых, при запуске лузера сразу становится очевидно, что он был написан грамотно, с учётом всех мелочей. 30-дневную пробную версию софтины можно скачать с их сайта по
– настоятельно рекомендую:

Sniff.png



6. Заключение

Устройства USB поддерживают и интерфейс SCSI, который намного мощнее обычного. Но к сожалению ограниченный объём статьи не позволяет охватить всё сразу, так-что придётся отложить это дело до лучших времён. Если проснифать пакеты, то оказывается, что в своей массе, система поддерживает диалог с шиной USB исключительно по SCSI, периодически посылая ей пакеты CBW (Command Block Wrapper) и принимая CSW (Command Status Wrapper). Реализовать данный обмен можно посредством всё того-же DeviceIoControl() с управляющим кодом IOCTL_SCSI_PASS_THROUGH_DIRECT.

В скрепку кладу исполняемый файл представленного выше кода, и пару инклудов, с описанием необходимых структур. На этом тчк, всем удачи и до скорого.
 

Вложения

  • USBinfo.zip
    18,4 КБ · Просмотры: 334

rusrst

Green Team
04.10.2020
22
27
BIT
0
О, классно:) до сих пор помню Вашу статью по getEnumDevice() :) обходил по ней дерево com портов.
Можно попробовать хабы обойти через GUID_DEVINTERFACE_USB_HUB.
По поводу winUSB, она вроде же автоматом ставится на устройства, которые выдают спец значение при энумерации, абы куда ее вроде не поставить. И при работе драйвер не должен выгружаться из памяти, если устройство подключено, разве нет?
 
  • Нравится
Реакции: Marylin

Marylin

Mod.Assembler
Red Team
05.06.2019
304
1 355
BIT
279
По поводу winUSB, она вроде же автоматом ставится на устройства, которые выдают спец значение при энумерации,
..да, но только начиная с Win8, о чём на сайте Microsoft.

До Windows 8 для загрузки Winusb.sys в качестве функционального драйвера требовалось предоставить собственный INF. В Windows 8 входящий файл Winusb.inf был обновлен, чтобы позволить Windows автоматически сопоставлять INF с устройством WinUSB.

Здесь более развернутая инфа на эту тему: и
 
  • Нравится
Реакции: Mikl___ и rusrst
Мы в соцсетях:

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