Как взломать Harley Davidson. Часть 1

в 17:02, , рубрики: AURIX, can шина, Ghidra, tricore, winidea, микроконтроллеры, мотоциклы, реверс-инжиниринг

В декабре 2021 года была опубликована статья по взлому[1] фирменного диагностического комплекса. С тех пор появилось новое поколение мотоциклов, которые никого не оставляют равнодушными. Давайте покопаемся у них прямо в мозгах.

Глава 1. Программист не может не программировать

Harley Davidson Fat Bob

Harley Davidson Fat Bob

Все началось со спонтанной покупки мотоцикла Harley Davidson Fat Bob 2020. Вещь шикарная, спустя восемь месяцев я все так же радуюсь этой покупке, как и в первый день. Мотоцикл можно завести без ключа, имея при себе метку или же введя код, ручка газа электронная. Не прошло и двух недель, как я подключил логгер к диагностическому разъему шины CAN.

В интернете уже кто-то немного ковырял протокол, так что спустя еще пару недель у меня была простенькая консольная программа на Python которая, теперь уже через Wi-Fi адаптер, показывала обороты, пробег, скорость, поворотники и т.п. Эту программку я гонял и на компьютере, и на iPhone в среде разработки Pythonista. Так что какое-то время я ездил, сверяя данные своей программы и приборов мотоцикла, вылавливал что-то новое и подумывал: может быть, сделать какую-то программку с симпатичным дашбордом?

У большинства двигателей разбирать протокол CAN несложно: не так много данных меняется ежесекундно, по характеру изменений понятно, то ли это счетчик сообщений, то ли контрольная сумма, то ли плавно меняющаяся величина. Так что, после первичного анализа, остается не так много данных для обработки. Тут подойдет любой софт, который работает с вашим адаптером, и даже Microsoft Excel – это грозное оружие в руках CAN hacker.

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

Как пояснили знающие люди, новые мотоциклы пока взломать не удалось. Это, вероятно, не совсем правда: чешская компания diag4bike продает программно-аппаратный комплекс совместимый с последним поколением Harley Davidson, требуя (помимо адаптера и лицензии на софт) покупки отдельной лицензии на каждый мотоцикл[2] за 220 евро, и при этом, похоже, с Harley Davidson у них нет никаких официальных отношений.

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

Глава 2. В дебрях Aurix TriCore

ECM Delphi MT21M, HD part number 41000677
ECM Delphi MT21M, HD part number 41000677

Мотоциклом управляет блок ECM Delphi MT21M на базе микроконтроллера Infineon Aurix TC265, и имеет фирменный номер 41000677 в каталоге запчастей.

Мне кажется, зря ополчились все на инфоцыган. Если долго посылать сигнал во вселенную, она услышит, и пошлет тебе прошивку. Есть в этом что-то. Снятые с блока управления дампы всплыли на популярном ресурсе MHH Auto[3], и добрые люди с Otomotiv Forum не только помогли их скачать, но и от себя еще дамп добавили. Все они сейчас доступны для загрузки в ветке обсуждения мотоцикла на Otomotiv Forum[4].

Ковырять прошивки обычно несложно. Что нужно сделать, чтобы отправить сообщение CAN? В один регистр загрузить идентификатор сообщения, во второй первые четыре байта данных, в третий последние четыре байта данных, а в четвертом регистре поставить флажок, что сообщение готово для отправки. И все.

На самом деле, все может быть капельку сложнее. 29-битный идентификатор может быть выровнен по старшему биту регистра, микроконтроллер иметь несколько FIFO, но все это ожидаемо и довольно быстро вытягивается из документации, если был практический опыт программирования.

В случае Aurix мой опыт мне ничем не помог.

Конечно, я первым делом загрузил документацию по микроконтроллеру. Бегло пролистал 5705 страниц TC26x B-Step User’s Manual. Вздохнул. Это только описание архитектуры чипа и регистров. Описание CAN, а точнее MultiCAN+, начинается на странице 2536 и заканчивается на странице 2716. Описание, как и во всех подобных документах, довольно лаконичное, за подробностями и примерами добро пожаловать в Application Notes. За набором инструкций микроконтроллера также в отдельный документ. И так далее. Хотите все знать, еще 5000 страниц наберется.

Впрочем, для начала мне нужна была только карта памяти, чтобы понять, с какого базового адреса загружать прошивку в дизассемблер Ghidra. К счастью, у них есть поддержка TC29x. Нам бы, конечно, TC26x, но вроде бы по регистрам и набору инструкций мы совпадаем, и на том спасибо.

Но есть нюанс. Если мы посмотрим файл tc29x.pspec из поставки Ghidra, то мы не увидим в нем регистров CAN. Я не могу упрекнуть разработчиков, они добавили 3715 других символов. Видимо, им не хватило сил на MultiCAN. В файле tc172x.pspec есть регистры CAN, но не вздумайте скопировать, там совершенно другие адреса.

И вот настало время рассказать, почему не просто CAN, а MultiCAN.

В микроконтроллере есть 5 узлов CAN (на каждый приходится по 15 регистров), которые могут быть подключены как ко внешним шинам CAN, так и ко внутренней шине. В случае внешней шины, для каждого узла доступно 5 вариантов назначения выводов. Для приема и отправки используется 256 объектов сообщений (на каждый приходится по 10 регистров). Сообщения связываются с узлами через списки, их всего 16.

В общем, MultiCAN состоит из почти 3000 регистров. Если серьезно подходить, стоит сразу написать скрипт, генерирующий дополнения к спецификации Ghidra, чтобы не пришлось потом эти регистры по одному именовать руками внутри программы.

Такая архитектура задает иной подход к программированию CAN. Предположим, ECM отправляет 30 сообщений. Вместе с инициализацией CAN, сразу инициализируется 30 объектов сообщений вместе с идентификатором сообщения и, возможно, данными по умолчанию. Чтобы отправить сообщение, мы можем обновить данные в объекте и инициировать его отправку. Код отправки сообщения будет оперировать только номером сообщения.

Но это еще половина беды. Допустим, мы уже разобрали весь код инициализации CAN и знаем, что нужное нам сообщение – самое первое. Восемь байт его данных хранятся в регистрах CAN_MODATAL0 и CAN_MODATAH0 с адресами 0xf0019010 и 0xf0019014 соответственно.

Было бы логично поискать эти адреса в прошивке, но вы их не найдете.

91 20 00 2f       movh.a       a2,#0xf002	   ; 0xf0020000
d9 22 00 08       lea          a2,[a2]-0x8000  ; 0xf0018000
		          ...
d9 22 00 01       lea          a2,[a2]0x1000   ; 0xf0019000
		          ...
19 20 10 00       ld.w         d0,[a2]0x10	   ; 0xf0019010 CAN_MODATAL0
19 21 14 00       ld.w         d1,[a2]0x14 	   ; 0xf0019014 CAN_MODATAH0

В приведенном выше коде мы вначале загружаем слово в верхнюю часть регистра, потом даем отрицательное смещение, далее я опустил часть кода, которая что-то делает с адресом 0xf0018000, далее мы добавляем положительное смещение, и наконец грузим данные сообщения CAN в регистры DO:D1 пользуясь коротким смещением в инструкциях загрузки.

Если обратить внимание на байт-код, то там даже близко нет адресов, которыми мы оперируем. Жонглируя байтами 00 2F и 00 08 мы получили в первых строках 0xf0018000. На первый взгляд кажется, что никакой беды в этом нет.

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

А декомплятор, конечно, не разберет весь код. Он оставит вам огромные массивы, которые вы будете вручную превращать в указатели на функции. В этих функциях тоже будут вызовы (методов классов) по таблицам, которые декомпилятор не всегда сможет определить по объективным причинам.

Потратив пару ночей в Ghidra, я нашел много всего интересного, и пришел к выводу, что одной декомпиляцией не обойтись. Во-первых, 2.5 мегабайтный дамп это действительно много. Это 20 тысяч листов ассемблерного кода. Во-вторых, часть кода копировалась в оперативную память и исполнялась оттуда. При этом относительная адресация (смещение от текущего адреса) позволяла восстановить часть логики кода, дизассемблируя его по месту хранения в ПЗУ, но как только начиналась абсолютная адресация (используемая в таблицах методов классов), то разматывать эти цепочки становилось совершенно невозможно. В-третьих, я совершенно не хотел ставить эксперименты на своем мотоцикле. А было понятно, что если я найду что-то интересное, то я пожертвую и мотоциклом.

Обстановка накалялась, нужно было что-то срочно решать.

Глава 3. Никогда бы не поверил

Aurix TC275 Lite Kit подключенный к шине CAN

Aurix TC275 Lite Kit подключенный к шине CAN

Не так уж и много вариантов предлагал AliExpress. И я их тоже понимал. Ни один человек в здравом уме не захочет программировать TriCore. С другой стороны, среди хороших программистов очень много людей специфических. Мы меняем, в конце концов, кайф гонять на Харлее на кайф копаться в его мозгах. Без какой-то конкретной цели. Просто потому, что это показалось забавным. И непростым. А по междурядью любой наваливать может.

За сходные деньги удалось купить Aurix TC275 Lite Kit. Подключаемый по USB к компьютеру программатор уже интегрирован на плату. Также не плате есть трансивер CAN, а второй посадили на макетную платку с разъемом для шины CAN.

Можно было начинать.

Загрузил бесплатную среду Aurix Development Studio. Буквально пара дней, и я заставил работать примеры с сайта Infineon. Помню, сутки убил на то, что на плате трансивер CAN нужно включать, иначе он работал только на прием. У трансивера ножка STANDBY подключена к микроконтроллеру, надо низкий уровень подать. В документации на плату это есть, а она всего-то 25 страниц. Чего это я, после 5000 страниц-то?

В общем, с примерами разобрался, научилась плата принимать и отправлять сообщения. Загрузил на нее прошивку от мотоцикла. Проработала она буквально мгновение, чего и следовало ожидать. Так как ножки, используемые для CAN в ECM, на плате были заняты чем-то другим, от чего отказаться было никак нельзя. Выход был один, патчить прошивку ECM, чтобы после инициализации узлов CAN, немного похозяйничал мой код, и перенастроил узлы на нужные мне ножки микроконтроллера, которые есть на плате.

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

Поэкспериментировав на плате, отыскал свободное место в прошивке ECМ по адресу 0x80010000 и приготовил вот такой код прямо внутри моего тестового проекта в Aurix Development Studio:

void my_init(void) __at( 0x80010000 )
{
    int disabled = (((&MODULE_CAN)->N[IfxMultican_NodeId_0]).CR.B.CCE == 0U);
    if (disabled) ((&MODULE_CAN)->N[IfxMultican_NodeId_0]).CR.B.CCE = 1U;

    IfxPort_setPinModeOutput2(&MODULE_P20, 8, IfxPort_OutputMode_pushPull,IfxPort_OutputIdx_alt5);
    IfxPort_setPinPadDriver2(&MODULE_P20, 8, IfxPort_PadDriver_cmosAutomotiveSpeed2);
    IfxPort_setPinModeInput2(&MODULE_P20, 7, IfxPort_InputMode_pullUp);
    IfxPort_setPinPadDriver2(&MODULE_P20, 7, IfxPort_PadDriver_cmosAutomotiveSpeed2);
    ((&MODULE_CAN)->N[IfxMultican_NodeId_0]).PCR.B.RXSEL = Ifx_RxSel_b;

    if (disabled) ((&MODULE_CAN)->N[IfxMultican_NodeId_0]).CR.B.CCE = 0U;

    disabled = (((&MODULE_CAN)->N[IfxMultican_NodeId_1]).CR.B.CCE == 0U);
    if (disabled) ((&MODULE_CAN)->N[IfxMultican_NodeId_1]).CR.B.CCE = 1U;

    IfxPort_setPinModeOutput2(&MODULE_P00, 0, IfxPort_OutputMode_pushPull,IfxPort_OutputIdx_alt5);
    IfxPort_setPinPadDriver2(&MODULE_P00, 0, IfxPort_PadDriver_cmosAutomotiveSpeed2);
    IfxPort_setPinModeInput2(&MODULE_P00, 1, IfxPort_InputMode_pullUp);
    IfxPort_setPinPadDriver2(&MODULE_P00, 1, IfxPort_PadDriver_cmosAutomotiveSpeed2);
    ((&MODULE_CAN)->N[IfxMultican_NodeId_1]).PCR.B.RXSEL = Ifx_RxSel_d;

    if (disabled) ((&MODULE_CAN)->N[IfxMultican_NodeId_0]).CR.B.CCE = 1U;

    IfxPort_setPinModeOutput2(&MODULE_P20, 6, IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinState(&MODULE_P20, 6, IfxPort_State_low);

    ECU_Data_Init();
}

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

void ECU_Data_Init(void) __at( 0x800f05b8 )
{
    ;
}

Теперь дело осталось за малым:

  1. Из скомпилированного кода примера перенести тело функции my_init() в дамп прошивки.

  2. Вызов функции ECU_Data_Init() по адресу 0x800e2d66 заменить на вызов my_init().

  3. А my_init(), сделав свои дела, вызовет настоящий ECU_Data_Init(), для этого я добавил в код своего проекта функцию-пустышку.

Для отладки я загрузил отладчик winIDEA. Он прямо неплох, но имеет какую-то странную модель лицензирования, включающую бесплатную лицензию. Воспользоваться мне ей удалось, только настроив использование winIDEA в Aurix Development Studio. Запуская свой проект на отладку из нее, плата прошивалась тестовым проектом и я попадал в winIDEA, где перепрошивал плату назад пропатченной прошивкой ECM и дальше можно было спокойно работать, пока winIDEA не вылетала по ошибке. Тогда начинай сначала.

Что удивительно, код мой практически сразу заработал. Выяснилось, правда, что на плате ECM микроконтроллер работает на вдвое большей частоте, но никаких серьезных проблем это не вызывало, только пришлось поправить делитель в инициализации всех узлов CAN (регистр CAN_NBTRx.BRP).

Ну и ECM, конечно, хотел инициализировать еще много чего, в том числе то, что инициализировать было нельзя. Так как плата совсем другая и заканчивалось это перезагрузкой. Поковырявшись в отладчике, я нашел переменную по адресу 0x70006724. Если ее в нужный момент обнулить, до вызова функции по адресу 0x800e2bd4, то код инициализации ненужной периферии не вызывался. Для этого я добавил в код еще одну заплаточку и….

О чудо, прошивка завелась! Полетели сообщения CAN! Вероятность этого чуда я оценивал крайне низко.

И теперь я мог. Отлаживать свой мотоцикл. Прямо на столе. Не боясь ничего. Это действительно было чудо.

Продолжение следует

В следующей части нас ждут Unified Diagnostic Services, CUDA и фиаско. Но не переживайте, следующая часть - не последняя!

[1] Hacking a Harley Tuner https://therealunicornsecurity.github.io/Powervision-1/

[2] Harley Davidson 114 EU5 Delphi MT21M Decat/Lambda OFF https://mhhauto.com/Thread-Harley-Davidson-114-EU5-Delphi-MT21M-Decat-Lambda-OFF

[3] Harley Davidson 2021+ Softail ECM https://otomotiv-forum.com/threads/harley-davidson-2021-softail-ecm.27544/#post-286793

[4] Tuning License for Harley-Davidson https://www.custom-chrome-europe.com/en/Motorcycle-Parts/Intake-Fuel-Systems/EFI-Tuning-Accessories/EFI-Tuning-Accessories/Tuning-License-for-Harley-Davidson.html

Автор: agorlach77

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js