Чтобы предотвратить снижение IQ во время самоизоляции, возникло желание сделать что-то полезное для себя, а если повезет — не только. Нарезая n-ый круг по квартире, мой взгляд зацепился за автомобильный сканер, который я брал у знакомого для дальнейшего изучения, а именно Lexia 3, он же Actia XS Evolution. Вот такой:
image
Его огромным минусом было то, что работать с ним может только софт DiagBox, предназначенный для диагностики автомобилей марки Peugeout/Citroen. С последним мириться было нельзя (с), поэтому возникла мысль, а что если этот сканер заставить отправлять и получать произвольные сообщения в CAN-шину автомобиля, тем самым превратив его в универсальный адаптер.

Итак, план действий:

  1. Собрать обмен по шине USB между ПК и нашим пациентом.
  2. Разобраться как происходит общение между драйвером адаптера и программным обеспечением.
  3. Повторить обмен и почувствовать себя молодцом (спойлер: вышло все несколько сложнее).

1. Обмен


Программный комплекс DiagBox свободно гуляет по интернету, поэтому трудностей с пунктом 1 не возникло, тем более в комплекте с ним идет небольшая утилита для идентификации адаптера, что несколько упростило задачу из пункта 2. При получении обмена по шине USB помогла пробная версия программы USBLyzer.
Идем к успеху. На картинке отправленный пакет данных, вид устройства в диспетчере и та самая утилитка.

image

Глядя на IRP'ы при открытии устройства и обмене с ним можно сделать вывод, что работа производится через штатный механизм ввода-вывода Windows, а именно, для открытия виртуального файла устройства функция CreateFile и для обмена — DeviceIOControl.

image

2. Разбор полетов


Данные собраны, IOCTL ID и флаги получены, осталось дело за малым, открыть файл-устройство и отправить туда пачку байт. Вот только что открывать? Поиск через WinObj результатов не дал, при подключении адаптер не имеет точного имени, лишь неявную ссылку с постоянно меняющимся ID, то есть просто открыть устройство наподобие COM-порта не получится

CreateFile("\\\\.\\COM1", ...)

Изучение файлов программы внесло ясность, оказалось все довольно просто — адаптер имеет свой уникальный GUID, с помощью которого у операционной системы запрашивается список всех устройств с таким GUID и если они присутствуют, мы получим ссылку на устройство, вида
\\?\USB#VID_103A&PID_F000#6&268bff9b&0&7#{75a835f4-d77d-4402-8585-c42247f25b76}\vcommusb0 открытие которой будет успешным.

Функция, возвращающая путь взамен GUID:

CM_Get_Device_Interface_List(InterfaceClassGuid, pDeviceID, Buffer, BufferLen, ulFlags);

«файл» успешно открывается, не забываем про флаги, которые добыли через USBLyzer

Device = CreateFile(DeviceName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

3. Финал


Отправляем пакет байт, а в ответ — тишина…

bStatus = DeviceIoControl(Device, 0x220003, send_buf, send_len, out_buf, max_out_len, &returned_len, NULL);

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

image

Окончательно потеряв всякую надежду на успех, у меня оставалось последнее — тяжелая артиллерия в виде диска от журнала «Хакер» с программой OllyDbg. Запускаем, аттачимся к процессу, ставим брякпоинт на функции DeviceIoControl и что же мы видим.

image

Царь-то не настоящий. Абсолютно другой IOCTL код, данные совсем не похожи на те, что видно через USBLyzer.

То, что было отправлено в устройство:

<b>00 FA</b> <i>AA</i> BA 7C 15 00 00 00 00 00 00

А это ушло по USB:

40 05 15 C0 <b>00 FA</b> 00 00 <i>AA</i> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41

Подсказка крылась в названии устройства, видимом в диспетчере задач. На сайте Microsoft'а нашел статью о том, что такое UMDF USB драйвер. Наглядная блок-схема:

image

Выходит, то, что мы видим в USBLyzer'е это транспортный уровень передачи клиентских данных, который реализован в UMDF драйвере (направление 7 на картинке), в то время как мы передаем данные устройству (направление 2 на картинке), которые дойдут сначала в драйвер, а потом уйдут в шину USB. Надеюсь понятно объяснил.

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

image

Согласно ему, в сообщении выше была запрошена версия прошивки (00 FA), на что был получен ответ со строкой APPLI_XS_Fuji_ P106138A V4.3.0 @ACTIA 02.01.12.

ВСЕ. Настраиваем CAN, отправляем адреса запрос-ответ для общения по протоколу ISO-TP и получаем полноценно работающий адаптер с автомобилем. Для удобства пользования была написана обертка, которая насколько это возможно соответствует стандарту PassThru J2534, для того, чтобы этот адаптер можно было использовать с различным автомобильным софтом, в том числе собственной разработки.

Видео работы (stdout выведен в консоль для удобства отладки):


Исходники можно посмотреть тут