После экспериментов над CAN шиной в автомобиле появилось дикое желание копнуть несколько глубже, в самое святая святых. Думаю, всем известен такой термин, как «чип-тюнинг», в переводе на русский это простая прошивка блоков управления (двигатель, коробка передач и т.д.). Производитель оборудования изначально закладывает в свои устройства функционал для обновления либо смены ПО микроконтроллера, но его механизм никому не раскрывается по понятным причинам, и чтобы усложнить этот процесс, сама программа, с помощью которой происходит работа с энергонезависимой памятью не хранится в прошивке, а загружается в контроллер только в момент обслуживания. Эта статья о том, как заставить микроконтроллер приборной панели выполнять чужой код имея доступ к диагностическому разъему авто.
В общем виде механизм загрузки данных в ECU (electronic control unit) описан в диагностическом протоколе UDS, а именно функции:
34 — Request Download
36 — Transfer Data
Но в плане реализации UDS автопроизводители не брезгуют вносить изменения/дополнения в протокол, создавая проприетарную надстройку. В моем случае процесс обновления выглядит примерно так:
Пункты 1-3 сложности не представляли, а что делать дальше? Где взять адрес и максимальный объем данных? Как после загрузки выполнить то, что было загружено? Собственно, ради этого и пишется статья.
В качестве подопытного была выбрана комбинация приборов, так как, во-первых, у меня есть еще одна на случай апокалипсиса, во-вторых, ее контроллер можно прочесть простым USB-RS232 адаптером. Изучив внутренности имеем контроллер Fujitsu MB91F223. Это 32-битный мк с ядром FR60Lite, 512 кб памяти и 16 кб RAM. Datasheet, RM, Assembler manual, программа-программатор на него легко ищутся в интернете, останавливаться здесь не буду. Вот он красавец:
План действий:
Чтобы выполнить пункт 1 необходимо изучить обработчик прерываний от CAN шины и понять куда сохраняются данные для дальнейшей обработки. Во множестве контроллеров есть так называемая таблица векторов прерываний, в которой лежат адреса функций ответственных за их обработку. В семействе мк Fujitsu FR эта таблица находится в еепроме, а указатель на нее хранится в регистре TBR (Table base register). Простой поиск текста в IDA дает положительный результат и адрес таблицы прерываний у нас.
Согласно мануалу, адрес прерывания CAN находится по смещению 0x370 от начала TBR. Вот и он.
Из базы данных дилерского диагностического ПО у меня были идентификаторы SID и LID (31E1) протокола UDS, которыми запускалась процедура выполнения кода, это упростило задачу и позволило действовать с конца в начало. В обработчике функции 31E1 был найден фрагмент, где загружается адрес, принадлежащий области RAM, и затем происходит вызов по этому адресу. Не это ли мы ищем?
Поиск использования константы 0x3F100 приводит нас в другое место в прошивке, в обработчик функции UDS 34 — Request download! Это именно то, что нужно, адрес для записи данных и максимальный объем (0x700 байт) в оперативную память найден.
Теперь, после отправки команды на запрос разрешения загрузки данных 3403F1000000010C (жирным указан тот самый адрес, курсивом объем, который хотим передать), в ответ приборная панель отвечает добром 740401. Далее происходит загрузка пользовательских данных с помощью функции Transfer data и дается команда на выполнение. С загрузкой и выполнением разобрались, но ведь нужно теперь найти что загружать. В открытом доступе среды разработки для этого микроконтроллера я не обнаружил, но спустя месяц стука в техподдержку cypress (да-да, не fujitsu, они то ли поглотили их, в общем, не знаю) они дали ссылку на IDE под названием Softune Workbench лохматого 97 года с которой шел компилятор под ядро FR.
Вот так она выглядит, не чета vscode.
На скриншоте фрагмент программы для мигания светодиодами (не пинайте за стиль написания на ассемблере, это мой первый опыт).
Этот же код, но уже на си
Ну и сам результат
С другими узлами, все выглядит примерно также, за исключением архитектуры контроллеров и порядка выполнения команд. Безопасно ли оставлять такие лазейки в автомобильном оборудовании? Видимо да, раз производитель так делает. Зачем я этим занимался? Просто было интересно, ну и ассемблер меня давно интересовал, познакомился с ним, так сказать.
Подопытный — панель приборов Mitsubishi 8100B197, общение по CAN шине велось адаптером Tactrix OpenPort 2.0, софт на компьютере собственной разработки.
В общем виде механизм загрузки данных в ECU (electronic control unit) описан в диагностическом протоколе UDS, а именно функции:
34 — Request Download
36 — Transfer Data
Но в плане реализации UDS автопроизводители не брезгуют вносить изменения/дополнения в протокол, создавая проприетарную надстройку. В моем случае процесс обновления выглядит примерно так:
- Вход в расширенную диагностическую сессию
- Перезагрузка в bootloader
- Получение security access для разрешения операции загрузки данных
- Передача адреса в памяти, куда будет вестись запись и объем данных
- Загрузка данных
- Выполнение того, что было загружено
- Далее — программирование EEPROM программой, которую загрузили до этого
Пункты 1-3 сложности не представляли, а что делать дальше? Где взять адрес и максимальный объем данных? Как после загрузки выполнить то, что было загружено? Собственно, ради этого и пишется статья.
В качестве подопытного была выбрана комбинация приборов, так как, во-первых, у меня есть еще одна на случай апокалипсиса, во-вторых, ее контроллер можно прочесть простым USB-RS232 адаптером. Изучив внутренности имеем контроллер Fujitsu MB91F223. Это 32-битный мк с ядром FR60Lite, 512 кб памяти и 16 кб RAM. Datasheet, RM, Assembler manual, программа-программатор на него легко ищутся в интернете, останавливаться здесь не буду. Вот он красавец:
План действий:
- Найти обработчики диагностических запросов
- Найти адреса в памяти, куда можно что-то писать
- Найти способ выполнить записанный код
Чтобы выполнить пункт 1 необходимо изучить обработчик прерываний от CAN шины и понять куда сохраняются данные для дальнейшей обработки. Во множестве контроллеров есть так называемая таблица векторов прерываний, в которой лежат адреса функций ответственных за их обработку. В семействе мк Fujitsu FR эта таблица находится в еепроме, а указатель на нее хранится в регистре TBR (Table base register). Простой поиск текста в IDA дает положительный результат и адрес таблицы прерываний у нас.
Согласно мануалу, адрес прерывания CAN находится по смещению 0x370 от начала TBR. Вот и он.
Он же, но уже в полный рост и обработчик приема сообщений по протоколу ISO-TP
Обработчик ISO-TP не полностью, но куда расходятся кадры разного типа отчетливо видно
Обработчик ISO-TP не полностью, но куда расходятся кадры разного типа отчетливо видно
Из базы данных дилерского диагностического ПО у меня были идентификаторы SID и LID (31E1) протокола UDS, которыми запускалась процедура выполнения кода, это упростило задачу и позволило действовать с конца в начало. В обработчике функции 31E1 был найден фрагмент, где загружается адрес, принадлежащий области RAM, и затем происходит вызов по этому адресу. Не это ли мы ищем?
Поиск использования константы 0x3F100 приводит нас в другое место в прошивке, в обработчик функции UDS 34 — Request download! Это именно то, что нужно, адрес для записи данных и максимальный объем (0x700 байт) в оперативную память найден.
Теперь, после отправки команды на запрос разрешения загрузки данных 3403F1000000010C (жирным указан тот самый адрес, курсивом объем, который хотим передать), в ответ приборная панель отвечает добром 740401. Далее происходит загрузка пользовательских данных с помощью функции Transfer data и дается команда на выполнение. С загрузкой и выполнением разобрались, но ведь нужно теперь найти что загружать. В открытом доступе среды разработки для этого микроконтроллера я не обнаружил, но спустя месяц стука в техподдержку cypress (да-да, не fujitsu, они то ли поглотили их, в общем, не знаю) они дали ссылку на IDE под названием Softune Workbench лохматого 97 года с которой шел компилятор под ядро FR.
Вот так она выглядит, не чета vscode.
На скриншоте фрагмент программы для мигания светодиодами (не пинайте за стиль написания на ассемблере, это мой первый опыт).
Этот же код, но уже на си
void delay(int loops)
{
while(--loops)
{
#pragma asm
NOP
NOP
#pragma endasm
__asm(" nop");
}
}
#define DDR2 (*((char*)0x402))
#define PDR2 (*((char*)0x2))
#define WPR (*((char*)0x485))
#define LVRC (*((char*)0x57D))
void wdt_reset(void)
{
WPR = 0xA5;
WPR = 0x5A;
LVRC = 0x10;
}
void main(void)
{
int current_pin = 2;
DDR2 |= 0x7E;
while(1)
{
wdt_reset();
PDR2 |= current_pin;
delay(0x7FFF);
PDR2 &= ~current_pin;
delay(0x7FFF);
current_pin <<= 1;
if(current_pin >= 0x80)
{
current_pin = 2;
}
}
}
Ну и сам результат
С другими узлами, все выглядит примерно также, за исключением архитектуры контроллеров и порядка выполнения команд. Безопасно ли оставлять такие лазейки в автомобильном оборудовании? Видимо да, раз производитель так делает. Зачем я этим занимался? Просто было интересно, ну и ассемблер меня давно интересовал, познакомился с ним, так сказать.
Подопытный — панель приборов Mitsubishi 8100B197, общение по CAN шине велось адаптером Tactrix OpenPort 2.0, софт на компьютере собственной разработки.