Учим французский или как получить универсальный адаптер из диагностического сканера концерна PSA
Чтобы предотвратить снижение IQ во время самоизоляции, возникло желание сделать что-то полезное для себя, а если повезет — не только. Нарезая n-ый круг по квартире, мой взгляд зацепился за автомобильный сканер, который я брал у знакомого для дальнейшего изучения, а именно Lexia 3, он же Actia XS Evolution. Вот такой:
Его огромным минусом было то, что работать с ним может только софт DiagBox, предназначенный для диагностики автомобилей марки Peugeout/Citroen. С последним мириться было нельзя ©, поэтому возникла мысль, а что если этот сканер заставить отправлять и получать произвольные сообщения в CAN-шину автомобиля, тем самым превратив его в универсальный адаптер.
Итак, план действий:
1. Собрать обмен по шине USB между ПК и нашим пациентом.
2. Разобраться как происходит общение между драйвером адаптера и программным обеспечением.
3. Повторить обмен и почувствовать себя молодцом (спойлер: вышло все несколько сложнее)
1. Обмен
Программный комплекс DiagBox свободно гуляет по интернету, поэтому трудностей с пунктом 1 не возникло, тем более в комплекте с ним идет небольшая утилита для идентификации адаптера, что несколько упростило задачу из пункта 2. При получении обмена по шине USB помогла пробная версия программы USBLyzer.
Идем к успеху. На картинке отправленный пакет данных, вид устройства в диспетчере и та самая утилитка.
Глядя на IRP’ы при открытии устройства и обмене с ним можно сделать вывод, что работа производится через штатный механизм ввода-вывода Windows, а именно, для открытия виртуального файла устройства функция CreateFile и для обмена — DeviceIOControl.
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);
Долго я сидел и смотрел в монитор, вот тот самый обмен, вот приходят ответы от устройства, вот они флаги, в точности такие, как я повторил, но ничего не выходит.
Окончательно потеряв всякую надежду на успех, у меня оставалось последнее — тяжелая артиллерия в виде диска от журнала «Хакер» с программой OllyDbg. Запускаем, аттачимся к процессу, ставим брякпоинт на функции DeviceIoControl и что же мы видим.
Царь-то не настоящий. Абсолютно другой IOCTL код, данные совсем не похожи на те, что видно через USBLyzer.
То, что было отправлено в устройство: 00 FA AA BA 7C 15 00 00 00 00 00 00
А это ушло по USB: 40 05 15 C0 00 FA 00 00 AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41
Подсказка крылась в названии устройства, видимом в диспетчере задач. На сайте Microsoft’а нашел статью о том, что такое UMDF USB драйвер. Наглядная блок-схема:
Выходит, то, что мы видим в USBLyzer’е это транспортный уровень передачи клиентских данных, который реализован в UMDF драйвере (направление 7 на картинке), в то время как мы передаем данные устройству (направление 2 на картинке), которые дойдут сначала в драйвер, а потом уйдут в шину USB. Надеюсь понятно объяснил.
Теперь все стало ясно, откуда другие IOCTL ID и данные. Чтож, так даже проще, не нужно ломать голову над тем, как реализовать транспорт, у нас простейший интерфейс запрос-ответ. Также в файлах программы случайно завалялось описание протокола обмена (частичное), жаль только на французском.
Согласно ему, в сообщении выше была запрошена версия прошивки (00 FA), на что был получен ответ со строкой APPLI_XS_Fuji_ P106138A V4.3.0 @ACTIA 02.01.12.
ВСЕ. Настраиваем CAN, отправляем адреса запрос-ответ для общения по протоколу ISO-TP и получаем полноценно работающий адаптер с автомобилем. Для удобства пользования была написана обертка, которая насколько это возможно соответствует стандарту PassThru J2534, для того, чтобы этот адаптер можно было использовать с различным автомобильным софтом, в том числе собственной разработки.
Видео работы (stdout выведен в консоль для удобства отладки):
Исходники можно посмотреть тут: github.com/kolyandex/Lexia_J2534