Русский
Русский
English
Статистика
Реклама

Uds

Хакаем CAN шину авто. Мобильное приложения вместо панели приборов

28.02.2021 04:04:14 | Автор: admin
Мобильное приложение VAG Virtual CockpitМобильное приложение VAG Virtual Cockpit

Я продолжаю изучать CAN шину авто. В предыдущих статьях я голосом открывал окна в машине и собирал виртуальную панель приборов на RPi. Теперь я разрабатываю мобильное приложение VAG Virtual Cockpit, которое должно полностью заменить приборную панель любой модели VW/Audi/Skoda/Seat. Работает оно так: телефон подключается к ELM327 адаптеру по Wi-Fi или Bluetooth и отправляет диагностические запросы в CAN шину, в ответ получает информацию о датчиках.

По ходу разработки мобильного приложения пришлось узнать, что разные электронные блоки управления (двигателя, трансмиссии, приборной панели и др.) подключенные к CAN шине могут использовать разные протоколы для диагностики, а именно UDS и KWP2000 в обертке из VW Transport Protocol 2.0.

Программный сниффер VCDS

Программный сниффер VCDS: CAN-SnifferПрограммный сниффер VCDS: CAN-Sniffer

Чтобы узнать по какому протоколу общаются электронные блоки я использовал специальную версию VCDS с программным сниффером в комплекте. В этот раз никаких железных снифферов на Arduino или RPi не пришлось изобретать. С помощью CAN-Sniffer можно подсмотреть общение между VCDS и автомобилем, чтобы затем телефон мог прикинуться диагностической утилитой и отправлять те же самые запросы.

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

  • VW/Skoda/Seat (2006-2012) - приборная панель UDS. Двигатель и трансмиссия VW TP 2.0

  • Audi (2006-2012) - приборная панель VW TP 2.0. Двигатель UDS. Трансмиссия VW TP 2.0

  • VW/Skoda/Seat/Audi (2012-2021) - везде UDS

Протокол UDS

Unified Diagnostic Services (UDS) - это диагностический протокол, используемый в электронных блоках управления (ЭБУ) автомобильной электроники. Протокол описан в стандарте ISO 14229-1 и является производным от стандарта ISO 14230-3 (KWP2000) и ныне устаревшего стандарта ISO 15765-3 (Diagnostic Communication over Controller Area Network (DoCAN)). Более подробно в википедии.

Диагностические данные от двигателя по протоколу UDS (Skoda Octavia A7)Диагностические данные от двигателя по протоколу UDS (Skoda Octavia A7)

В моей машине (Skoda Octavia A5) приборка использует UDS протокол, это дало мне легкий старт разработки, т.к. данные были в простом формате Single Frame SF(фрейм, вся информация которого умещается в один CAN пакет) и большинство значений легко поддавались расшифровке. Volkswagen не дает документацию на формат значений, поэтому формулу расшифровки для каждого датчика приходилось подбирать методом логического мышления. Про UDS протокол очень хорошо и с подробным разбором фреймов написано на canhacker.ru.

Разбор UDS пакета в формате Single FrameРазбор UDS пакета в формате Single Frame

Пример запроса и ответа температуры моторного масла:

7E0 0x03 0x22 0x11 0xBD 0x55 0x55 0x55 0x557E8 0x05 0x62 0x11 0xBD 0x0B 0x74 0x55 0x55

Запрос температуры моторного масла:

  • 7E0 - Адрес назначения (ЭБУ двигателя)

  • Байт 0 (0x03) - Размер данных (3 байта)

  • Байт 1 (0x22) - SID идентификатор сервиса (запрос текущих параметров)

  • Байт 2, 3 (0x11 0xBD) - PID идентификатор параметра (температура моторного масла)

  • Байт 4, 5, 6, 7 (0x55) - Заполнитель до 8 байт

Ответ температуры моторного масла:

  • 7E8 - Адрес источника (Диагностический прибор)

  • Байт 0 (0x05) - Размер данных (5 байт)

  • Байт 1 (0x62) - Положительный ответ, такой SID существует. 0x22 + 0x40 = 0x62. (0x7F) - отрицательный ответ

  • Байт 2, 3 (0x11 0xBD) - PID идентификатор параметра (температура моторного масла)

  • Байт 4, 5 (0x0B 0x74) - значение температуры моторного масла (20.1 C формулу пока что не смог подобрать)

  • Байт 6, 7 (0x55) - Заполнитель до 8 байт

Первая версия мобильного приложения VAG Virtual Cockpit умела подключаться только к приборной панели по UDS.

VAG Virtual Cockpit - экран с данными от приборной панели по протоколу UDSVAG Virtual Cockpit - экран с данными от приборной панели по протоколу UDS

VW Transport Protocol 2.0

Volkswagen Transport Protocol 2.0 используется в качестве транспортного уровня, а данные передаются в формате KWP2000. Keyword Protocol 2000 - это протокол для бортовой диагностики автомобиля стандартизированный как ISO 14230. Прикладной уровень описан в стандарте ISO 14230-3. Более подробно в википедии.

Т.к. KWP2000 использует сообщения переменной длины, а CAN шина позволяет передавать сообщения не больше 8 байт, то VW TP 2.0 разбивает длинное сообщение KWP2000 на части при отправке по CAN шине и собирает заново при получении.

Диагностические данные от двигателя по протоколу KWP2000 (Skoda Octavia A5)Диагностические данные от двигателя по протоколу KWP2000 (Skoda Octavia A5)

ЭБУ двигателя моей машины использует протокол VW TP 2.0, поэтому мне пришлось изучить его. Видимо Volkswagen разрабатывала транспортный протокол не только для работы по надежной CAN шине, но и для менее надежных линий связи, иначе нет объяснения для чего требуется такая избыточная проверка целостности данных. Главным источником информации по VW TP 2.0 является сайт https://jazdw.net/tp20.

Разбор протокола VW TP 2.0 на примере подключения к первой группе двигателя:

200 01 C0 00 10 00 03 01

Настраиваем канал с двигателем. Байт 0: 0x01 - двигатель, 0x02 - трансмиссия. Байт 5,4: 0x300 - адрес источника

201 00 D0 00 03 40 07 01

Получили положительный ответ. Байт 5,4: 0x740 - к двигателю обращаемся по этому адресу

740 A0 0F 8A FF 32 FF

Настраиваем ЭБУ на отправку сразу 16 пакетов и выставляем временные параметры

300 A1 0F 8A FF 4A FF

Получили положительный ответ

740 10 00 02 10 89

Отправляем команду KWP2000 startDiagnosticSession. Байт 0: 0x10 = 0b0001 - последняя строка данных + 0x0 счетчик отправляемых пакетов 0 (0x0 - 0xF)

300 B1

Получили первый ACK

300 10 00 02 50 89

Получили положительный ответ. Байт 0: 0x10 - cчетчик принимаемых пакетов 0

740 B1

Мы отправили первый ACK, что получили ответ

740 11 00 02 21 01

Делаем запрос. Байт 0: 0x11 - счетчик отправляемых пакетов 1. Байт 3: 0x21 - запрос параметров. Байт 4: 0x01 - из группы 1

300 B2

Получили второй ACK

300 22 00 1A 61 01 01 C8 13

Байт 0: 0x22 - 0b0010 (не последняя строка данных) + 0x02 (cчетчик принимаемых пакетов 2). Байт 1,2: 0x00 0x1A длина 26 байт. Байт 3,4: 0x61 0x01 - положительный ответ на команду запроса параметров 0x21+0x40=0x61 из 0x1 группы. Байт 5: 0х01 - Запрос RPM (соответсвует протоколу KW1281). Байт 6,7: (0xC8 * 0x13)/5 = 760 RPM (формула соответствует протоколу KW1281)

300 23 05 0A 99 14 32 86 10

Байт 1: 0x05 - запрос ОЖ. Байт 2,3: (0x0A * 0x99)/26 = 57.0 C. Байт 4: 0x14 = запрос лямбда контроль %. Байт 5,6: 0x32*0x86; Байт 7: 0х10 - двоичная настройка

300 24 FF BE 25 00 00 25 00

0x25 0x00 x00 - Заполнитель, до 8 параметров

300 15 00 25 00 00 25 00 00

Байт 0: 0x15 - 0b0001 (последняя строка данных) + 0x5 (счетчик принимаемых пакетов 5)

740 B5

Отправляем ACK. Прибывляем к нашему предыдущему ACK количество полученных пакетов 0xB1 + 0x4 = 0xB5

300 A3

Запрос KeepAlive, что мы еще на связи

740 A1 0F 8A FF 4A FF

Ответ KeepAlive

740 A8

Мы разрываем связь

300 A8

ЭБУ в ответ тоже разрывает связь

Во второй версии мобильного приложения VAG Virtual Cockpit появилась возможность диагностировать двигатель и трансмиссию по протоколу VW TP 2.0.

VAG Virtual Cockpit - экран с данными от двигателя по протоколу VW TP 2.0VAG Virtual Cockpit - экран с данными от двигателя по протоколу VW TP 2.0

Диагностический адаптер ELM327

Для меня некоторое время было вопросом, как получить данные из CAN шины и передать на телефон. Можно было бы разработать собственный шлюз с Wi-Fi или Bluetooth, как это делают производители сигнализаций, например Starline. Но изучив документацию на популярный автомобильный сканер ELM327 понял, что его можно настроить с помощью AT команд на доступ к CAN шине.

Копия диагностического сканера ELM327Копия диагностического сканера ELM327Не все ELM327 одинаково полезны

Оригинальный ELM327 от компании elmelectronics стоит порядка 50$, в России я таких не встречал в продаже. У нас продаются только китайские копии/подделки, разного качества и цены 10-30$. Бывают полноценные копии, которые поддерживают все протоколы, а бывают и те которые умеют отвечать только на несколько команд, остальные игнорируют, такие адаптеры не имеют доступ к CAN шине. Я например пользуюсь копией Viecar BLE 4.0, который поддерживает 100% всех функций оригинала.

Для работы с протоколом UDS через ELM327 нужно указать адреса назначения, источника и разрешить длинные 8 байтные сообщения, по умолчанию пропускается максимум 7 байт.

Последовательность ELM327 AT команд для работы с UDS по CAN шине:

ATZ // сброс настроекAT E0 // отключаем эхоAT L0 // отключаем перенос строкиAT SP 6 // Задаем протокол ISO 15765-4 CAN (11 bit ID, 500 kbaud)AT ST 10 // Таймаут 10 * 4 мс, иначе EBU шлет повторные ответы каждые 100 мс, а мы не отвечаем, потому что ожидаем конца, а нам нужен только первый ответAT CA F0AT AL // Allow Long (>7 byte) messagesAT SH 7E0 // задаем ID, к кому обращаемся (двигатель)AT CRA 7E8 // CAN Receive Address. Можно задать несколько 7XeAT FC SH 7E0AT FC SD 30 00 00AT FC SM 1 // Режим Flow Control 1 должен быть определен после FC SH и FC SD, иначе в ответ придет "?"03 22 F4 0С 55 55 55 55 // UDS запрос оборотов двигателя

Для работы с протоколом KWP2000 через ELM327 нужно только указать адреса назначения и источника.

Последовательность ELM327 AT команд для работы с VW TP 2.0 по CAN шине:

ATZ // сброс настроекAT E0 // отключаем эхоAT L0 // отключаем перенос строкиAT SP 6 // Задаем протокол ISO 15765-4 CAN (11 bit ID, 500 kbaud)AT PB C0 01AT SP B // Задаем протокол USER1 CAN (11* bit ID, 125* kbaud)AT ST 10 // Таймаут 10 * 4 мс, иначе EBU шлет повторные ответы каждые 100 мс, а мы не отвечаем, потому что ожидаем конца, а нам нужен только первый ответAT SH 200 // Обращаемся к 200 IDAT CRA 201 // Ждем ответа от 201 Блок управления двигателем, 202 - Transmission, 203 - ABS, 207 -  Приборная панель01 C0 00 10 00 03 01 // Initiate channel setup with ECU module - 01, request it use CAN ID 0x300; Transmission 02; ABS 03AT SH 740 // адрес блока 740 получен в ответе на предыдущую командуAT CRA 300 // Ждем ответа от 300 IDA0 0F 8A FF 32 FF // Tell ECU module to send 16 packets at a time, and set timing parameters10 00 02 10 89 // Send KWP2000 startDiagnosticSession request 0x10 with 0x89 as a parameter.B1 // ACK11 00 02 1A 9B // Запрос названия блока KWP2000

Мобильное приложение VAG Virtual Cockpit

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

  • Сниффером собрать трафик от диагностической утилиты VCDS

  • Изучить работу протоколов UDS, VW TP 2.0, KWP2000

  • Настроить диагностический сканер ELM327 на работу с UDS и VW TP 2.0

  • Изучить новый для меня язык программирования Swift

Мобильное приложение VAG Virtual Cockpit для iOSМобильное приложение VAG Virtual Cockpit для iOS

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

Пару слов про точность данных. Штатная панель приборов не точно показывает скорость - завышает показания на 5-10 км/ч, стрелка охлаждающей жидкости всегда на 90 C, хотя реальная температура может быть 80 - 110 C, стрелка уровня топлива до середины идет медленно, хотя топлива уже меньше половины и при нуле на самом деле топливо еще есть в баке. Производитель это делает для удобства и безопасности водителя.

На данный момент приложение показывает следующие параметры:

Приборная панель

Двигатель

Трансмиссия (температура)

1) Какая дверь открыта
2) Скорость
3) Обороты
4) Температура масла
5) Температура ОЖ
6) Топливо в баке в л.
7) Запас хода в км.
8) Средний расход
9) Время в машине
10) Пробег
11) Температура за бортом

1) Обороты
2) Массовый расход воздуха
3) Температура забора воздуха
4) Температура выхлопа (рассчитанная)
5) Критический уровень масла
6) Уровень масла
7) Наддув турбины (реальный)
8) Наддув турбины (ожидаемый)
9) Пропуски зажигания в цилиндрах
10) Углы откатов зажигания в цилиндрах

1) ATF AISIN (G93)
2) DSG6 (G93)
3) Блок управления DSG6 (G510)
4) Масло диска сцепления DSG6 (G509)
5) Мехатроник DSG7 (G510)
6) Процессор DSG7
7) Диск сцепления DSG7

Я стремлюсь чтобы приложение поддерживало как можно больше моделей автомобилей. Пока что поддерживаются производители: Volkswagen, Skoda, Seat, Audi. На разных комплектациях могут отображаться не все параметры, но это поправимо.

Сейчас я провожу тестирование версии 3.0. Приложение доступно только на iOS, после релиза 3.0 перейду к разработке версии для Android.

Если интересно потестировать и есть желание принять участие в проекте, то установить приложение можно по ссылке https://testflight.apple.com/join/Yx9vcPxQ. Также я веду бортжурнал на drive2.ru, где делюсь полезной информацией и новостями о VAG Virtual Cockpit.

Подробнее..

Из песочницы Идем по приборам

06.11.2020 12:09:31 | Автор: admin


Много ли нужно, чтобы изменить пробег или залезть в память приборной панели?


Есть только один способ узнать попробовать сделать это самому.


Постановка задачи


В данной статье мы покопаемся в механизмах диагностики нескольких приборных панелей и посмотрим, насколько далеко можно зайти без специализированного дилерского оборудования. Основными инструментами будут скрипты на Python и обычный CAN-USB адаптер. Все воздействия будут сводиться к сообщениям на CAN-шине. Так будет сложнее, но интереснее, так как все сделанное таким способом теоретически можно повторить на настоящем автомобиле через OBDII-разъем. В первую очередь нас будет интересовать доступ к памяти устройства где хранятся его конфигурация и пробег.


Немного дисклеймеров:
Работая с CAN-шиной реального автомобиля есть риск вывести его из строя. Ни в коем случае не беритесь за работу с CAN-шиной без должного опыта, инструментов и мер предосторожности.


Корректировка пробега с корыстной целью занятие для редисок.


Поиск испытуемых


Чтобы разнообразить эксперимент, мы будем использовать три приборных панели от автомобилей разных производителей. С этой целью на Авито и авторазборках были закуплены три б/у панели согласно следующим критериям:


  • низкая цена детали (возможно с дефектом);
  • популярность автомобиля в прошедшем десятилетии;
  • доступность автомобиля на вторичном рынке;
  • в выборке панелей не должно быть родственных автопроизводителей (например, Hyundai и Kia, Mitsubishi и Citroen и т.п.)

Итак, в порядке закупки и повествования:



Приборная панель Hyundai Solaris 2013 г.в.
Партномер: 94003-4l715
Установлен сегментый экран с фиксированным количеством символов и обозначений. По сравнению с остальными просто набита разъемами, целых 4 штуки. Много сигналов поступает не по CAN-шине, а "аналоговым" методом по отдельным проводам.



Приборная панель Ford Focus 3 2012 г.в.
Партномер: BM5T-10849-BAE
Стоит монохромный экран малого разрешения. При подаче питания находится в состоянии спячки, чтобы увидеть экран и побаловаться со стрелками нужно периодически будить CAN-сообщениями.



Приборная панель Mitsubishi Lancer X 2008 г.в.
Партномер: 8100A117A


Оказалась достаточно распространенной и устанавливалась сразу на ряд моделей Mitsubishi, Citroen и Peugeout. Самая шумная из всех, при каждой подаче питания громко пищит и дает понять, что существовать отдельно от автомобиля ей не нравится.


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


Основы диагностики


Прежде чем идти дальше, сделаем короткую остановку и ознакомимся с используемыми диагностическими протоколами. Большинство систем диагностики строятся на двух схожих между собой протоколах: KWP2000 (Keyword Protocol 2000) и UDS (Unified Diagnostic Services). Первым появился KWP2000, затем на его базе был создан UDS, как более современная реализация. Применение этих протоколов предоставляет следующие возможности для автопроизводителей и автосервисов:


  • чтение кодов неисправностей;
  • запись/чтение прошивки;
  • запись/чтение конфигурации и отдельных настроек;
  • чтение данных с датчиков;
  • приведение в действие актуаторов.

Например, автопроизводитель может пользоваться этими механизмами, чтобы сконфигурировать ЭБУ под конкретную комплектацию собранного автомобиля, а в автосервисе могут использовать команду разжатия колодок при обслуживании тормозной системы.


В обоих протоколах основными участниками процесса являются две сущности: клиент и сервер. Клиент представляет интересы тестового оборудования, а сервер интересы ЭБУ. Работа с каждым из ЭБУ ведется отдельно и для налаживания взаимодействия по CAN нужно знать адресную пару клиент-сервер. Упрощенно такую систему можно представить так:



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


Для защиты важных сервисов могут использоваться разные уровни доступа. Как правило, для заполучения нужного уровня доступа нужно запросить у сервера "семя" и подготовить корректный ответ с помощью заранее известного алгоритма.


Каждый отдельный сервис выполняет только одну определенную функцию, например чтение памяти по адресу или сброс ошибок.


Получается, чтобы составить беглое представление о неизвестном модуле и возможных операциях над ним, нужно совершить следующие шаги:


  1. Определить адреса клиента и сервера;
  2. Найти все доступные сессии (стандартные, расширенные, диагностические и т.д.);
  3. Для каждой из сессий найти все существующие уровни доступа (0x01, 0x03, 0x05 и т.д.);
  4. Для каждой из сессий найти все доступные сервисы (чтение/запись по адресу, управление рутинами и т.д.)

Инструментарий


Начнем с программной составляющей.
Для взаимодействия с устройством использовались Python-библиотеки:


  • python-udsoncan (диагностический клиент, предоставлет возможность пользоваться сервисами);
  • python-can-isotp (транспортный уровень ISO 15765-2, позволяет передавать данные длиной более 8 байт);
  • python-can (API для общения по CAN-шине).

Они интересны по нескольким причнам:


  • подробная документация с множеством примеров;
  • широкая поддержка железа + не так сложно добавить новое;
  • их можно подключать по мере надобности, начиная с голого CAN и поднимаясь выше;
  • из них можно собрать полноценный стек для UDS-диагностики.


Использованный набор библиотек отчасти и мотивировал меня написать данную статью, поскольку показался любопытным и полезным для быстрых экспериментов. Связка в виде скриптов на Python и адаптера CAN-USB дают гибкость и способность на лету прикидывать последовательности обмена данными. Можно автоматизированно подбирать пароли к уровням доступа, притворяться дилерским диагностическим оборудованием, просто слушать CAN-шину и многое другое.


Об отрицательном опыте использования: не всегда получалось добиться отклика в заданных временных рамках, например, скрипт мог не успеть отправить flow-control сообщение и тем самым обрывал пересылку большого куска данных. Также были случаи, когда CAN-сообщения отправлялись в неправильном порядке, но разбор полетов показал, что корень проблемы находился не в библиотеках. В конечном счете данные проблемы получилось обойти, главное всегда помнить, что настольный ПК это все-таки не real-time устройство.


Для некоторых задач, например подбора адресов клиента и сервера, нам пригодится утилита caringcaribou, которая тоже использует python-can.


Что касается аппаратной части и адаптеров CAN-USB, то в ход шло все, что удавалось раздобыть и одолжить, библиотеки это позволяют. В данном конкретном случае, работа была проделана с помощью Vector VN1610 и VN1611, а также адаптера на базе протокола Lawicel.
Что до последнего, то он сравнительно прост и предcтавляет из себя виртуальный COM-порт, который преобразует по определенным правилам строки в CAN-сообщения и наборот. При некотором желании похожий девайс можно сделать своими силами, на хабре есть интересная статья "CAN-USB адаптер из stm32vldiscovery" на эту тему.


Для работы с приборными панелями на столе достаточно иметь блок питания, способный выдать 12 В. В обзоре на каждую панель будут приведены краткие справки по железу, распиновке и настройке CAN.


Приборная панель Hyundai Solaris


Обзор


Железо:



Распиновка панели и конфигурация CAN:



Замечание: у панели Solaris сзади 4 разъема, поэтому они обозначены слева направо как A, B, C, D.


Сессии и уровни доступа:



Таблица сервисов:



Знаки вопросов указывают на предполагаемое имя сессии, поскольку точной информации нет.
Нумерация сессий почему-то перемешана, 0x03 взят из UDS, а 0x85 и 0x90 взяты из KWP2000.
Из результатов сканирования видно, что панель достаточно демократично относится к чтению и записи в память, эти операции доступны во всех сессиях, в том числе сразу после подачи питания. В таком случае, можно не терять время и сразу приступить к сканированию адресов и типов памяти.


В поисках памяти


Для чтения памяти и в KWP2000 и в UDS используется сервис ReadMemoryByAddress (0x23), есть только некоторые отличия по формату запроса. Для KWP2000 он будет выглядеть вот так:


Byte # Value Parameter
0 23 Service ID
1-3 XX memoryAddress[3]
4 XX memorySize

Все просто, указывается ID сервиса, адрес чтения и сколько байт нужно прочитать. В некоторых системах старший байт может быть использован как memoryIdentifier, чтобы выбирать из какой именно памяти произодится чтение (EEPROM, flash), но при этом ширина адреса уменьшается до 16 бит.


Теперь про UDS:


Byte # Value Parameter
0 23 Service ID
1 XX addressAndLengthFormatIdentifier
~ XX memoryAddress[]
~ XX memorySize[]

Здесь ширина адреса и размера памяти может меняться, это настраивается байтом addressAndLengthFormatIdentifier, в котором старший ниббл определяет ширину memorySize, а младший memoryAddress. Например, для 32-битного memoryAddress и 16-битного memorySize этот байт будет равен 24. Старший байт адреса так же может быть использован как memoryIdentifier.


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


Byte # Value Parameter
0 23 Service ID
1-4 XX memoryAddress[4]
5 XX memorySize[2]

То есть нечто промежуточное: поля шире, чем в KWP2000, но при этом addressAndLengthFormatIdentifier из UDS не применяется. Далее перебираем адреса и находим, что старший байт адреса выбирает тип памяти: 0x00 для флеш-памяти микроконтроллера (виден кусочек таблицы векторов прерываний):


TX: <7C6> (8)    07 23 00 00 00 00 00 04RX: <7CE> (8)    05 63 80 07 00 07 00 00

и 0x04 для внешней EEPROM.


TX: <7C6> (8)    07 23 04 00 00 00 00 04RX: <7CE> (8)    05 63 0d a8 00 00 00 00

Скрипт чтения флеш-памяти и EEPROM
import canimport isotpimport loggingimport timeimport argparsedef process_stack_receive(stack, timeout=1):    t1 = time.time()    while time.time() - t1 < timeout:        stack.process()        if stack.available():            break        time.sleep(stack.sleep_time())    return stack.recv()def process_stack_send(stack, timeout=1):    t1 = time.time()    while time.time() - t1 < timeout:        stack.process()        if not stack.transmitting():            break        time.sleep(stack.sleep_time())def my_error_handler(error):    logging.warning('IsoTp error happened : %s - %s' % (error.__class__.__name__, str(error)))def print_frame(frame):    if frame:        print(''.join(format(x, '02x') for x in frame))    else:        print("None")bus = can.interface.Bus(bustype='slcan',                         channel="COM3",                         ttyBaudrate=115200,                         bitrate=500000)addr = isotp.Address(isotp.AddressingMode.Normal_11bits, rxid=0x7ce, txid=0x7c6)stack = isotp.CanStack(bus, address=addr, error_handler=my_error_handler, params = {'tx_padding':0})parser = argparse.ArgumentParser()parser.add_argument("-e", "--eeprom", help="read EEPROM", action="store_true")parser.add_argument("-f", "--flash", help="read Flash", action="store_true")args = parser.parse_args()if args.eeprom:    for addr in range(0x00000000, 0x000007ff, 0x10):        addr0 = (addr >>  0) & 0xff        addr1 = (addr >>  8) & 0xff        array = [0x23, 0x04, 0x00, addr1, addr0, 0x00, 0x10]        stack.send(bytearray(array))        process_stack_send(stack)        print_frame(process_stack_receive(stack))if args.flash:    for addr in range(0x00000000, 0x0003ffff, 0x10):        addr0 = (addr >>  0) & 0xff        addr1 = (addr >>  8) & 0xff        addr2 = (addr >> 16) & 0xff        array = [0x23, 0x00, addr2, addr1, addr0, 0x00, 0x10]        stack.send(bytearray(array))        process_stack_send(stack)        print_frame(process_stack_receive(stack))bus.shutdown()

Раз уж получилось прочесть, почему бы не попробовать и записать что-нибудь? Берем сервис WriteMemoryByAddress (0x3d) и, наученные прошлым опытом, поменьше верим стандартам и побольше собственным догадкам. Рабочим вариантом зароса оказывается:


Byte # Value Parameter
0 3d Service ID
1-4 XX memoryAddress[4]
5-N XX dataRecord

то есть не нужно даже указывать memorySize, достаточно просто указать адрес и сами байты.


Теперь пишем один байт и тут же читаем назад для проверки. Запрос записи во флеш-память подтверждается успешным ответом 0x7d, но по факту память остается нетронутой:


TX: <7C6> (8)    06 3d 00 00 00 00 81 00RX: <7CE> (8)    01 7d 00 00 00 00 00 00TX: <7C6> (8)    07 23 00 00 00 00 00 04RX: <7CE> (8)    05 63 80 07 00 07 00 00

что в принципе ожидаемо, т.к. просто так изменить прошивку без применения загрузчика вряд ли получится.


А вот запись в EEPROM прошла успешно:


TX: <7C6> (8)    06 3d 04 00 00 00 0e 00RX: <7CE> (8)    01 7d 00 00 00 00 00 00TX: <7C6> (8)    07 23 04 00 00 00 00 04RX: <7CE> (8)    05 63 0e a8 00 00 00 00

байт 0x0d поменял значение на 0x0e.


Этого достаточно, чтобы переходить к следующему этапу: поиску и изменению значений пробега.


Загадка со змейкой


Сейчас нам необходимо понять какие именно из байт EEPROM отвечают за хранение пробега. Для этого применяется очень простой подход: посылаем на панель сигнал скорости, пока она не увеличивает пробег на 1 км, снова читаем EEPROM и сравниваем с предыдущим дампом. Для симуляции сигнала скорости необходимо найти CAN-сообщение, отвечающее за скорость и отправлять его с определенным периодом. Впрочем, бывают и исключения, как в случае с данной панелью: она оказалась от автомобиля с МКПП, где скорость передается отдельным импульсным сигналом.


Итак, 1 км пройден, давайте сравнивать:



Из всего EEPROM поменялся только один байт и его значение уменьшилось на 0x20? Явно надо идти глубже.


"Накатываем" еще 23 км:



И начинаем наблюдать некоторые закономерности:


  1. для хранения используются 64 байта
  2. pост пробега = вычитание значений
  3. таблица проходится зигзагом в 2 отдельных прохода:
  4. вычитаемое определяется номером столбца:

0,1 2,3 4,5 6,7 8,9 A,B C,D E,F
-0x01 -0x02 -0x04 -0x08 -0x10 -0x20 -0x40 -0x80

Более подробный разбор спрятан под спойлер, так как он занимает много места.


Разгадка

Итак, начнем с исходных данных для пробега 64890 км, для удобства разбитых на четыре группы:


[13, F8, 27, F0, 4F, E0, 9F, C0, 3F, 81, 7F, 02, FE, 04, FC, 09][F8, 13, F0, 27, E0, 4F, C0, 9F, 81, 3F, 02, 9F, 05, 3E, 0A, 7C][13, F8, 27, F0, 4F, E0, 9F, C0, 3F, 81, 7F, 02, FE, 04, FC, 09][F8, 13, F0, 27, E0, 4F, C0, 9F, 81, 3F, 02, 9F, 05, 3E, 0A, 7C]

Далее для 0-ой и 2-ой строк попарно меняем значения в столбцах:


[F8, 13, F0, 27, E0, 4F, C0, 9F, 81, 3F, 02, 7F, 04, FE, 09, FC][F8, 13, F0, 27, E0, 4F, C0, 9F, 81, 3F, 02, 9F, 05, 3E, 0A, 7C][F8, 13, F0, 27, E0, 4F, C0, 9F, 81, 3F, 02, 7F, 04, FE, 09, FC][F8, 13, F0, 27, E0, 4F, C0, 9F, 81, 3F, 02, 9F, 05, 3E, 0A, 7C]

Вычитаем каждый байт из FF, чтобы заменить разности суммами:


[07, EC, 0F, D8, 1F, B0, 3F, 60, 7E, C0, FD, 80, FB, 01, F6, 03][07, EC, 0F, D8, 1F, B0, 3F, 60, 7E, C0, FD, 60, FA, C1, F5, 83][07, EC, 0F, D8, 1F, B0, 3F, 60, 7E, C0, FD, 80, FB, 01, F6, 03][07, EC, 0F, D8, 1F, B0, 3F, 60, 7E, C0, FD, 60, FA, C1, F5, 83]

Попарно объединяем значения в каждой строке, чтобы получить 2-ух байтные:


[07EC, 0FD8, 1FB0, 3F60, 7EC0, FD80, FB01, F603][07EC, 0FD8, 1FB0, 3F60, 7EC0, FD60, FAC1, F583][07EC, 0FD8, 1FB0, 3F60, 7EC0, FD80, FB01, F603][07EC, 0FD8, 1FB0, 3F60, 7EC0, FD60, FAC1, F583]

Для переполненных значений переносим младшие разряды в старшие:


[07EC, 0FD8, 1FB0, 3F60, 7EC0, FD80, 1FB00, 3F600][07EC, 0FD8, 1FB0, 3F60, 7EC0, FD60, 1FAC0, 3F580][07EC, 0FD8, 1FB0, 3F60, 7EC0, FD80, 1FB00, 3F600][07EC, 0FD8, 1FB0, 3F60, 7EC0, FD60, 1FAC0, 3F580]

Теперь нормализуем значения, чтобы получить набор простых счетчиков; не забываем, что делитель зависит от номера столбца:


[07EC, 07EC, 07EC, 07EC, 07EC, 07EC, 07EC, 07EC][07EC, 07EC, 07EC, 07EC, 07EC, 07EB, 07EB, 07EB][07EC, 07EC, 07EC, 07EC, 07EC, 07EC, 07EC, 07EC][07EC, 07EC, 07EC, 07EC, 07EC, 07EB, 07EB, 07EB]

Наконец, суммируем все счетчики и получаем ожидаемые FD7A (64890).


Осталось только преобразовать желаемый пробег в соответствующую таблицу, записать ее обратно в EEPROM и:



Шалость удалась!


Приборная панель Ford Focus 3


Обзор


Железо:



Распиновка панели и конфигурация CAN:



Замечание: к панели Focus подключаются 2 CAN-шины: MS (Medium Speed) и MM (Multimedia). Нам понадобится MS CAN.


Сессии и уровни доступа:



Таблица сервисов:



По результатам сканирования видно, что реализация диагностического сервера придерживается стандарта UDS вплоть до нумерации сессий. Сессия 0x01 используется по умолчанию при каждой подаче питания, сессия 0x02 используется для изменения прошивки и сессия 0x03 применяется для изменения настроек. Прямого доступа к памяти посредством ReadMemoryByAddress/WriteMemoryByAddress нет ни в одной из сессий, поэтому начинаем с чтения всех возможных DID с помощью сервиса ReadDataByIdentifier.


Первая попытка


Перебирая DID, читаем значения одно за другим, пока на глаза не попадается DID 0x61BB размером в 3-байта, в котором содержится значение пробега:


RX: <720> (8)    03 22 61 bb 00 00 00 00RX: <728> (8)    06 62 61 bb 00 e9 20 00

Просто так перезаписать это значение вызовом WriteDataByIdentifier не вышло, для этого необходимо перейти в расширенную диагностическую сессию и получить уровень доступа 0x03.


Краткое отступление по поводу алгоритма. При запросе нужного уровня доступа сервер вернет "семя" длиной 3 байта, для которого нужно подобрать корректный ответ той же длины. Подсказки как правильно это сделать, можно найти в публикациях Beneath the Bonnet: a Breakdown of Diagnostic Security и Adventures in Automotive Networks and Control Units.


Получив уровень доступа 0x03, пробуем произвести запись еще раз, увеличив значение на 1 км:


RX: <720> (8)    02 10 03 00 00 00 00 00RX: <728> (8)    06 50 03 00 32 01 f4 00RX: <720> (8)    02 27 03 00 00 00 00 00RX: <728> (8)    05 67 03 XX XX XX 00 00RX: <720> (8)    05 27 04 XX XX XX 00 00RX: <728> (8)    02 67 04 00 00 00 00 00RX: <720> (8)    06 2e 61 bb 00 e9 21 00RX: <728> (8)    03 6e 61 bb 00 00 00 00

Тут же читаем обратно, чтобы проверить:


RX: <720> (8)    03 22 61 bb 00 00 00 00RX: <728> (8)    06 62 61 bb 00 e9 21 00

Все получилось. А если уменьшить значение?.. Пробуем:


RX: <720> (8)    02 10 03 00 00 00 00 00RX: <728> (8)    06 50 03 00 32 01 f4 00RX: <720> (8)    02 27 03 00 00 00 00 00RX: <728> (8)    05 67 03 XX XX XX 00 00RX: <720> (8)    05 27 04 XX XX XX 00 00RX: <728> (8)    02 67 04 00 00 00 00 00RX: <720> (8)    06 2e 61 bb 00 e9 20 00RX: <728> (8)    03 7f 2e 31 00 00 00 00

Панель отвечает ошибкой 0x31 requestOutOfRange, то есть новое значение не проходит проверку. Что ж, значит нужно искать другой путь.


Вторичный загрузчик


Если вернуться к результатам сканирования, то можно найти в них reprogrammingSession. Обычно использование этой сессии подразумевает наличие вторичного загрузчика, и в случае с Ford его легко можно найти в открытом доступе. Поэтому мы попробуем его загрузить и посмотреть, что будет. Вдруг у него есть доступ к EEPROM?


Вторичный загрузчик (Secondary Bootloader) применяется для обновления прошивки ЭБУ следующим образом: SBL загружается в ОЗУ устройства, затем происходит передача управления SBL, после чего начинается чтение/запись флеш-памяти микроконтроллера. Упрощенно это можно представить так:



Для работы с прошивками Ford испольует формат VBF или Volvo Binary Format. Такой файл состоит из двух частей: человекочитаемого заголовка с адресом загрузки, типом данных и контрольной суммой; и бинарными данными для отправки в ЭБУ.


Последовательность загрузки SBL состоит из следующих шагов:


  1. DiagnosticSessionControl: переход в сессию программирования 0x02;
  2. SecurityAccess: получениe уровня доступа 0x01;
  3. RequestDownload: адрес загрузки 0x03ff0000 (начало ОЗУ), размер передачи указывается как в VBF-файле;
  4. TransferData: непосредственный процесс передачи данных;
  5. RequestTransferExit: запрос завершения передачи данных;
  6. RoutineControl: запуск рутины 0x0301, которая передает управление SBL.

После этого можно приступать к работе с флеш-памятью микроконтроллера: читать ее с помощью RequestUpload и записывать с помощью RequestDownload.


Скрипт чтения флеш-памяти (работает с извлеченным из VBF бинарником)
import canimport isotpimport timeimport binasciifrom udsoncan.connections import PythonIsoTpConnectionfrom udsoncan.client import Clientfrom udsoncan import Response, MemoryLocation, DataFormatIdentifierfrom ford_glfsr import calculate_keydownload_payload_size = 0xc8upload_payload_size = 0x20def my_error_handler(error):   logging.warning('IsoTp error happened : %s - %s' % (error.__class__.__name__, str(error)))bus = can.interface.Bus(bustype='slcan',                         channel="COM3",                         ttyBaudrate=115200,                         bitrate=125000)tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x720, rxid=0x728)stack = isotp.CanStack(bus=bus,                        address=tp_addr,                        params = {'tx_padding':0},                        error_handler=my_error_handler)conn = PythonIsoTpConnection(stack)# wake up the IPCping_msg = can.Message(arbitration_id=0x080,                        data=[0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],                        is_extended_id=False)bus.send(ping_msg)time.sleep(0.2)with Client(conn) as client:    # TesterPresent    client.tester_present()    # ChangeSession    client.change_session(0x02)    time.sleep(0.3)    # SecurityAccess    response = client.request_seed(0x01)    seed_int = int.from_bytes(response.service_data.seed, "little")    key_int = calculate_key(seed_int, 0x01)    key_bytes = key_int.to_bytes(3, byteorder="big")    client.send_key(0x01, key_bytes)    # RequestDownload    memloc = MemoryLocation(address=0x03ff0000,                             memorysize=0x54d4,                             address_format=32,                             memorysize_format=32)    dfi = DataFormatIdentifier(compression=0, encryption=0)    client.request_download(memory_location=memloc, dfi=dfi)    # TransferData    f = open("BM5T-14C025-AD.bin", "rb")    ipc_sbl = f.read()    f.close()    end_block = 0x6d  # memorysize/upload_payload_size    print("DOWNLOAD END BLOCK:", (hex(end_block)))    # sequence should start with "0x01", not "0x00"    for block_num in range(0x01, end_block + 0x01):        print(hex(block_num))        start_i = (block_num - 0x01) * download_payload_size        end_i = start_i + download_payload_size        client.transfer_data(block_num, ipc_sbl[start_i:end_i])    # TransferExit    client.request_transfer_exit()    # StartRoutine    client.start_routine(0x0301, data = b'\x03\xff\x00\x00')    # RequestUpload    memloc = MemoryLocation(address=0x00007000,                             memorysize=0x000f9000,                             address_format=32,                             memorysize_format=32)    dfi = DataFormatIdentifier(compression=0, encryption=0)    client.request_upload(memory_location=memloc, dfi=dfi)    upload_result = bytearray()    end_block = 0x7c80  # memorysize/upload_payload_size    print("UPLOAD END BLOCK:", (hex(end_block)))    # sequence should start with "0x01", not "0x00"    for block_num in range(0x01, end_block + 0x01):        print(hex(block_num))        response = client.transfer_data(block_num % 0x100)        upload_result.extend(response.service_data.parameter_records)    f = open("flash_dump.bin", "wb")    f.write(upload_result)    f.close()    # TransferExit    try:        client.request_transfer_exit()    except:        pass    # ECUReset    client.ecu_reset(1)

Тем не менее, оказалось, что у SBL нет доступа к внешней EEPROM. Было перепробовано несколько разных SBL, но все попытки закончились ничем. С положительной стороны у нас теперь есть возможность читать и записывать прошивку микроконтроллера. Что мешает отредактировать ее и залить обратно?


Модификация прошивки


Получается, что есть отдельный DID для значений пробега, но запись работает только на увеличение; сам пробег лежит в EEPROM, куда прямого доступа у нас нет, ни из основной программы, ни из вторичного загрузчика. С такими входными данными нам стоит попробовать модифицировать родную прошивку таким образом, чтобы получить возможность мотать пробег назад. Для этого с помощью уже изученного процесса выкачивается содержимое флеш-памяти и изучается на предмет интересных функций.


Перед этим был экспериментально получен предел увеличения километража: по какой-то странной причине максимально возможное для установки значение составляет 4294967 км.


Этот "волшебное" число пригодилось для поиска операций с граничными значениями, в пределах которых можно изменять пробег в меньшую сторону. Найдя нечто похожее, заменяем одну из проверок на "nop":



затем заливаем прошивку обратно, повторяем эксперимент:


TX: <720> (8)    02 10 03 00 00 00 00 00RX: <728> (8)    06 50 03 00 32 01 f4 00TX: <720> (8)    02 27 03 00 00 00 00 00RX: <728> (8)    05 67 03 XX XX XX 00 00TX: <720> (8)    05 27 04 XX XX XX 00 00RX: <728> (8)    02 67 04 00 00 00 00 00TX: <720> (8)    03 22 61 bb 00 00 00 00RX: <728> (8)    06 62 61 bb 01 e2 40 00TX: <720> (8)    06 2e 61 bb 01 38 d5 00RX: <728> (8)    03 6e 61 bb 00 00 00 00TX: <720> (8)    03 22 61 bb 00 00 00 00RX: <728> (8)    06 62 61 bb 01 38 d5 00

Теперь все работает и можно мотать пробег в меньшую сторону, как самый настоящий злодей:



Скорее всего, коммерческие инструменты для изменения пробега работают другим способом, менее хлопотным.


Подмена битмапов


После того, как основная работа проделана, можно и с битмапами поиграться. Например, взять и поменять приветственное лого панели.


Начинается все снова с изучения дампа, но в этот раз нужно найти в нем графические данные. Для этого очень удобно визуализировать файл дампа целиком, пример такого анализа с помощью GIMP был взят отсюда.


Если вкратце, то нужно скормить GIMPу файл под видом raw-данных и, меняя цветовое представление и ширину поля, искать нечто похожее на изображения. Экран у выбранной панели монохромный (бинарный), поэтому тип изображения устанавливаем как "Ч/б 1 бит", а ширину подбираем экспериментально.


При ширине визуализации в 16 пикселей, обнаруживаются первые изображения (шрифт для 1, 2, 3, 4, 5, 6). Но при включении панели отображается логотип Ford, поэтому нужно продолжать поиски. Как оказалось, шрифты и маленькие иконки найти проще, чем крупное изображение, разбитое на куски. Поэтому логотип попался на глаза не с первой попытки:



Видите эти загогулины? Это два верхних участка буквы F в логотипе Ford. Если собрать все части воедино и склеить в картинку, то мы увидим следующий результат:



Битмап Ford
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 FC 00 FE 00 FF 80 E3 80 C1 C0 C0 40 C0 60 E0 60 E0 20 70 30 18 30 00 30 00 30 00 30 80 30 C0 20 F0 60 38 60 1C 60 0C 60 06 60 03 C0 C1 C0 20 C0 20 C0 C0 C0 00 C0 01 C0 01 80 01 80 01 80 03 80 03 80 03 80 07 80 07 80 07 80 07 80 0F 80 0F 80 0F 80 07 80 07 80 07 80 03 80 03 C0 01 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 C0 00 C0 00 C0 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 00 7E 00 FF 80 FF C0 E1 E0 C0 70 C0 30 80 10 80 00 80 01 C0 01 C0 03 E0 03 F0 01 F0 11 78 08 3C 04 1F 84 0F C4 07 E4 01 FE 80 3F C0 0F E0 07 78 03 FC 02 FF 82 CF C2 03 C2 01 E2 00 63 00 66 00 29 80 00 80 40 E0 60 F8 C0 7F C0 1F 00 06 00 07 80 83 C0 E1 E0 F0 60 FC 70 3E F0 1F E0 07 80 0F 80 39 C0 70 E0 70 F0 31 F0 31 E0 19 00 FC 00 FE 00 FF 80 1F 80 07 C0 01 E0 00 E0 00 70 80 70 C0 E0 E0 E0 FF 80 7F 80 3F E0 0F F0 07 FC 80 7E C0 1F E0 0F 60 03 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 03 00 03 00 07 00 07 00 07 00 07 00 03 00 03 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 00 00 01 00 03 00 07 00 07 00 07 00 07 00 07 00 03 00 03 00 03 00 01 00 00 00 00 00 00 00 04 00 07 00 07 00 07 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 07 00 0F 00 0E 00 0E 00 0E 00 06 00 07 00 03 00 07 00 0F 00 0F 00 0C 00 0C 00 06 00 07 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Получается, что размер лого составляет 114x48 пикселей, оно состоит из 6 фрагментов, а адрес можно приблизительно понять с помощью сетки координат GIMP. Нам остается только отредактировать этот участок прошивки, загрузить его обратно:



и получить новое стартовое лого.


Битмап Habr
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 FC 00 FC 00 FC 00 FC 00 FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 FC 00 FC 00 FC 00 FC 00 FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF F0 01 F8 00 7C 00 7C 00 FC 00 FC 01 FC FF FC FF F8 FF F0 FF E0 FF C0 FF 00 00 00 00 00 00 00 3C 80 FF C0 FF E0 FF F0 FF F8 FF F8 C3 F8 81 F8 00 F8 00 F8 00 F8 00 F0 81 E0 C3 F8 FF F8 FF F8 FF F8 FF F8 FF 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF E0 C3 F0 81 F8 00 F8 00 F8 00 F8 00 F8 81 F8 C3 F8 FF F0 FF E0 FF C0 FF 80 FF 00 3C 00 00 00 00 00 00 F8 FF F8 FF F8 FF F8 FF F8 FF F8 FF E0 03 F0 01 F8 00 78 00 7C 00 FC 00 F8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 00 00 00 00 00 00 00 00 01 00 03 00 07 00 0F 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 0F 00 07 00 1F 00 1F 00 1F 00 1F 00 1F 00 00 00 00 00 00 00 00 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 07 00 0F 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 0F 00 07 00 03 00 01 00 00 00 00 00 00 00 00 00 1F 00 1F 00 1F 00 1F 00 1F 00 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Приборная панель Mitsubishi Lancer X


Обзор


Железо:



Замечание: точно определить модель микроконтроллера не получилось, т.к. маркировка не соответствует модели.


Распиновка панели и конфигурация CAN:



Сессии и уровни доступа:



Таблица сервисов:



Сканирование показало, что доступны только две сессии: сессия по умолчанию и расширенная диагностическая. Установить точные наименования сессий и внести некоторую ясность помогла удачная находка. На просторах интернета можно найти обрывки документации начала 2000-х, в которой описаны основы применения KWP2000 для диагностики автомобилей альянса DaimlerChrysler-Mitsubishi, а именно марок Dodge, Chrysler, Jeep, Mitsubishi, Mercedes-Benz и Smart. Этот альянс уже давно распался, что добавляет нашим экспериментам немного автомобильной археологии.


Возвращаясь к доступным сервисам их очень мало в defaultSession, по сути доступны только чтение ошибок, идентификационных данных и некоторых настроек. Записывать ничего нельзя, доступ к памяти отсутствует. В extenedDiagnosticSession открываются возможности запуска рутин и записи данных по идентификатору, но доступа к памяти все равно нет. В таком случае остается только перебирать доступные для чтения значения с помощью ReadDataByLocalIdentifier и попытаться изменить некоторые из них.


Не все коту масленица


В этот раз перебор значений закончился гораздо быстрее, т.к. сервис ReadDataByLocalIdentifier может адресовать только 0xFF уникальных идентификаторов, в отличии от ReadDataByIdentifier где их может быть до 0xFFFF.


Значения пробега были обнаружены под DID 0xAD в виде трех байт:


TX: <6A0> (8)    02 21 ad 00 00 00 00 00RX: <514> (8)    05 61 ad d0 18 03 00 00

Если попробуем использовать сервис WriteDataByLocalIdentifier и записать значение на 1 км больше, то сервер ответит отказом:


TX: <6A0> (8)    05 3b ad d1 18 03 00 00RX: <514> (8)    03 7f 3b 12 00 00 00 00

Код ошибки 0x12 в ответном сообщении означает "Sub Function Not Supported" то есть сервис записи по идентификатору работает корректно, но запись для него невозможна.


У нас осталось еще несколько непроверенных уровней доступа: вдруг если мы их получим, то операция станет возможной или откроются другие способы корректировки? Но для этого надо знать, как правильно это сделать. Поиск информации привел к очень полезной статье на хабре "Что можно сделать через разъем OBD в автомобиле", где автор смог извлечь алгоритм из более свежей цветной панели Mitsubishi с микроконтроллером другой архитектуры внутри. Оказалось, что алгоритм работает и для панели постарше, с помощью этой информации удалось подобрать и получить все три уровня доступа (0x01, 0x07 и 0x09). Самое время для повторной попытки:


TX: <6A0> (8)    02 10 92 00 00 00 00 00RX: <514> (8)    02 50 92 d0 18 03 1e 00TX: <6A0> (8)    02 27 01 00 00 00 00 00RX: <514> (8)    06 67 01 XX XX XX XX 00TX: <6A0> (8)    06 27 02 XX XX XX XX 00RX: <514> (8)    03 67 02 34 1c ad 4f 00TX: <6A0> (8)    02 27 07 00 00 00 00 00RX: <514> (8)    06 67 07 XX XX XX XX 00TX: <6A0> (8)    06 27 08 XX XX XX XX 00RX: <514> (8)    03 67 08 34 36 74 26 00TX: <6A0> (8)    02 27 09 00 00 00 00 00RX: <514> (8)    06 67 09 XX XX XX XX 00TX: <6A0> (8)    06 27 0a XX XX XX XX 00RX: <514> (8)    03 67 0a 34 9f 1f f5 00TX: <6A0> (8)    05 3b ad d1 18 03 00 00RX: <514> (8)    03 7f 3b 12 9f 1f f5 00

которая снова возвращает ошибку. Кстати, забавный момент, по какой-то причине в ответах сервера застревают байты от предыдущих сообщений. Кто-то из разработчиков забыл про очистку буфера?


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


Из упомянутой ранее документации можно узнать, что теоретически должна существовать еще одна сессия: ECUFlashReprogrammingSession (0x85) в которой открываются возможности для загрузки прошивки, но найти способ попасть в нее так и не удалось.


На этом этапе можно было бы взять программатор EEPROM, разобрать панель и просто перезаписать значения, но это отходит от диагностической темы данной статьи.


Впрочем, вполне вероятно, что-то было упущено и истина была где-то рядом.


Извлеченный урок


Тем не менее, время потраченное на изучение панели Mitsubishi не пропало зря, поскольку в процессе получилось изучить отличия KWP2000 и UDS поближе, а также составить сравнительную таблицу:



Для сравнения были использованы подмножество KWP2000 DaimlerChrysler-Mitsubishi, которое немного отличается от "чистого" KWP2000 и стандарт UDS 2006 года.


Сравнение интересно тем, что можно увидеть эволюцию, которую прошли протоколы диагностики. Например, избыточные сочетания сервисов были заменены одним сервисом, но с выбором подфункций: DisableNormalMessageTransmission / EnableNormalMessageTransmission заменены на CommunicationControl. То же касается и тройки StartRoutineByLocalIdentifier / StopRoutineByLocalIdentifier / RequestRoutineResultsByLocalIdentifier, которая превратилась в RoutineControl.


Если присмотреться внимательнее, то можно заметить, что в KWP2000 не хватает пары для StartDiagnosticSession, которая называлась бы StopDiagnosticSession и сбрасывала бы сервер в сессию по умолчанию. На самом деле, такой сервис может существовать, но он не входит в стандарт ISO 14230-3, и легко заменятся обычным вызовом StartDiagnosticSession с указанием перейти в сессию 0x81.


Поменялась логика чтения кодов неисправностей, добавлен сервис AccessTimingParameter для настройки продолжительности устанавливаемого соединения, SecuredDataTransmission для защиты сессии от прослушивания, LinkControl для настройки скорости соединения во время сессии.


Ну и самое заметное отличие это упразднение ReadECUIdentification, который использовался для чтения идентификационных параметров модуля: VIN, серийный номер и т.д. В UDS вся эта информация читается обычным ReadDataByIdentifier, а для стандартных DID добавляется префикс 0xF1. Например для чтения VIN KWP2000 использует команду 0x1A90, а UDS 0x22F190. Этим удобно пользоваться, когда в руки попал незнакомый модуль и нужно быстро понять, какой из протоколов применять.


Заключение


На основании проведенных экспериментов, можно сделать простой вывод добиться необходимого результата можно если не средствами диагностики, то редактированием прошивки или EEPROM. Для разграничений уровней доступа применяются относительно несложные алгоритмы, подсказки к решению которых можно найти в диагностическом ПО, самой прошивке или прослушанной сессии. В некоторых случаях производитель может использовать проприентарный подход для сокрытия важной информации, который тоже можно обойти.


Тем не менее, ситуация может изменится к лучшему, совсем недавно вышла новая редакция стандарта UDS 14229-1:2020, в которой был добавлен отдельный сервис Authentication (0x29) для применения более сложных методов аутентификации. Также все больше автомобилей комплектуются телематикой и IoT услугами, откуда могут перекочевать новые дыры в безопасности разнообразные механизмы защиты, например secure boot, anti-rollback прошивки и т.п.


Так что вполне вероятно, что провернуть похожий трюк лет 10 спустя станет сложнее.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru