Объект исследования — блок SRS от Audi A4 с номером 8W0959655J версии 107. После попытки обновления программного обеспечения блок изменил версию ПО на 103, а также поменял одну букву в серийном номере, превратившись в 8W0959655H. Подобная ситуация уже рассматривалась в одной из наших предыдущих статей, однако в данном случае блок остался на связи, но с существенным отличием: в нём зафиксировалась внутренняя ошибка B2000-00. Эта ошибка, которую невозможно устранить стандартными методами, блокирует любые манипуляции с ЭБУ и указывает на необходимость его замены. Дополнительная сложность заключается в том, что на плате установлен закрытый на чтение процессор, и это значительно усложняет процесс восстановления.

Подключение к ЭБУ SRS
Подключение к ЭБУ SRS

Одна из особенностей данного блока управления заключается в том, что он работает по протоколу FlexRay, который, напрямую, диагностических средств не имеет, то есть вы не можете подключиться к FlexRay-шине и что-то диагностировать, вы подключаетесь к ЭБУ шлюза по CAN-шине, а дальше уже шлюз соединяется по FlexRay со всей архитектурой сети. FlexRay - это тоже шина, тоже двухпроводная, во многом похожая на CAN, но работает на более высоких скоростях. Ее история, в общем-то, незавидна. Был создан концерн, в него вошли NXP, BMW, VAG, Bosch, Daimler-Chrysler и GM. Он разработал этот стандарт, производители стали использовать его в автомобилях собственных марок, однако в итоге, в 2009 концерн распался, оставив стандарты ISO с номерами от 17458-1 до 17458-5. В настоящее время автомобильные компании переходят на протокол CAN-FD, имеющий более дешевую реализацию, более привычную организацию, а, самое главное, обратную совместимость с обычной высокоскоростной шиной CAN.

Диаграмма подключения блоков по шине FlexRay в Audi A4
Диаграмма подключения блоков по шине FlexRay в Audi A4

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

Результат диагностики блока SRS "на столе", без подключенной периферии
Результат диагностики блока SRS "на столе", без подключенной периферии

В блоке находится процессор Renesas серии V850E2 с номером D70F3508GJA2. Для поиска документации используем документ с перечнем процессоров на официальном сайте компании Renesas Electronics:

Линейка процессоров V850E2
Линейка процессоров V850E2
Чтение информации о CPU программатором Orange 5
Чтение информации о CPU программатором Orange 5

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

Фотография процессора Renesas V850E2
Фотография процессора Renesas V850E2

Как обычно, сначала сохраняем первоначальный образ дампа. Затем пытаемся сделать понижение версии данных в EEPROM, заливая "доноров" с разных версий из нашей базы данных. К сожалению, подобрать такую версию не удалось. Скорее всего, это было уже невозможно, так как дамп EEPROM был испорчен.

Блок по-прежнему показывал всего две ошибки. Исходя из этого, было принято решение работать с процессором.

Как видно из скрина программатора Orange 5, при подключении процессор определяется и сообщает некоторые данные о себе, например, объём Сode-flash, объем Data-flash, идентификатор чипа. А значит, часть протокола можно проверить в работе даже на закрытом процессоре.

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

Было установлено, что загрузчик процессора (программа, применяемая для чтения и записи Flash памяти процессора) работает по UART. Протокол, в данном случае, организован по одному проводу в режиме UART. То есть, это полудуплексный вариант UART. Это представляло определенную проблему, потому что готовых решений для полудуплексных протоколов на базе UART обнаружить не удалось.

Вторая проблема заключалась в отсутствии у нас документации на этот протокол обмена.

Третьей особенностью является то, что процессор поддерживает два питания, 3.3 вольта и 1.2 вольта. Соответственно, мы должны не просто подать 3.3 вольт, но еще и встроить дополнительный регулятор питания, который подаст отдельное питание для ядра процессора. Отдельно от 3.3 вольт запитывается внутренняя Flash-память процессора (FVDD). Эта особенность открывает нам возможность управлять питанием микросхемы Flash-памяти.

И еще один момент, о котором уже говорилось выше, но хотелось бы остановиться на нём подробнее. Защита на чтение проявляется только при команде чтения, но не при командах соединения или получения информации об идентификаторе чипа. Проще говоря, когда мы сообщаем загрузчику, что хотим прочитать, какую-то область памяти, получаем конкретный ответ: "процессор закрыт на чтение" (Protect error). Это тоже является послаблением защиты, так как на конкретный запрос есть конкретный результат.

На официальном сайте удалось найти документацию на протокол похожего процессора V850 ES/Jx3-L. Этот протокол оказался очень похож, за рядом исключений, которые легко вычислялись.

Команды процессора в режиме загрузчика
Команды процессора в режиме загрузчика

Система команд очень простая. Нас интересует команда 50H - прочесть определённую область памяти. Проверить работу протокола можно посредством команд, доступных сразу после handshake. Например, C0H - это чтение модификации процессора, возвращает сигнатуру процессора.

Статусы ответов и ошибок, возвращаемые процессором
Статусы ответов и ошибок, возвращаемые процессором

В этом смысле протокол загрузчика весьма удобный. Подсоединился, послал команду C0H, он сразу вернул характеристики процессора. Помимо этого, он еще возвращает адреса своих флешей! Не надо искать datasheet или какие-то характеристики, всё возвращает программа загрузчика.

Для работы с UART использовался обычный USB2UART адаптер, и скрипт на python, однако, выяснилось, что буквально все адаптеры обладают внутренним буфером, и применение таких мер, как, например, команда serial.reset_input_buffer() особо не помогает. Работа по одному проводу означает, что все данные, которые вы посылаете возвращаются к вам обратно. Поэтому решено это было на уровне кода:

 def read(self, len, breset=True):
        b = b""
        try:
            if breset:
                self.ser.reset_input_buffer()
            b = self.ser.read(self.m_sent + len)
            b = b[self.m_sent :]

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

До чтения мы дошли, а чтение мы сделать не можем, потому что возвращается ошибка Protected Error. Для обхода данной защиты применялась технология voltage glitch. Суть её заключается в том, чтобы менять питание процессора в определённый момент, внося сбои в выполнение кода загрузчика. И здесь нам как раз очень пригодился пин управления питанием FVDD.

Cхема подключения к процессору для осуществления glitch-атаки
Cхема подключения к процессору для осуществления glitch-атаки

В качестве glitch-машины применялась плата Raspberry Pi Pico. Наша задача - сделать просадку питания в нужный момент, на 1-2 микросекунды, пропустив выполнение некоторых команд программы загрузчика. За начало отсчёта (триггера) брался Reset, перезагружавший процессор. Из программы на python он управлялся с помощью пина DTR, а на Pico принимался посредством чтения состояния GP3. Параметры для ВПО glitch-машины передавались через usb2com порт Raspberry Pi Pico.

        for delay in GLITCH_EXT_OFFS:
            for width in GLITCH_WIDTHS:
                for counts in GLITCH_COUNTS:
                    tries = 0
                    while tries in GLITCH_RETRIES:
                        tries += 1
                        if pi:
                                pi.write_giltch_params(delay, width, counts)
                        conn.setBaudrate(RH850_SERIAL_BAUDRATE_STAGE0)
                        # perform Reset
                        conn.resetCPU(True)
                        time.sleep(RESET_PAUSE)
                        conn.resetCPU(False)  # Release reset line
                        time.sleep(0.7)  # little pause for some CPUs
                        # initializing ...
                        for i in range(3):
                            conn.write(b"\x00")
                            time.sleep(0.02)
                        # Run 00 cmd
                        print("Init...")

Множественные циклы скрипта занимались перебором параметров для glitch-атаки. Например, параметр width определял "ширину" пропуска питания в микросекундах. В ВПО Pico это выглядело так.

Ожидание триггера (используется pico-sdk):

  while (true) {
      watchdog_update();
      int j = gpio_get(TRIGGER_PIN);
      if (0 == j) {
        trigger_down = true;
        bReset = 3;
      }
      if (j && trigger_down) {
        bReset = 1;
        break;
      }      
      if (time_us_64() - ctime > RESET_WAIT_TIMEOUT) {
        bReset = 2; // timeout
        break;
      }
    } // trigger wait

Непосредственное управление питанием:

busy_wait_at_least_cycles(t_delay); // задержка, сконвертированная в тики CPU
gpio_put(GLITCH_PIN, GLITCH_ON); // выключение питания
busy_wait_at_least_cycles(t_width); // пауза
gpio_put(GLITCH_PIN, GLITCH_OFF); // включение питания

Как показала практика, собирать такие штуки лучше всего на готовом docker с pico-sdk.

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

Потребление на пине FVDD в момент работы ВПО загрузчика
Потребление на пине FVDD в момент работы ВПО загрузчика

Для glitch-атаки важна точность попадания. В данном случае, процессор не очень быстрый, речь идет о микросекундах, не о наносекундах. Однако даже тут было "плавание". Осциллограф показывал, что не на всех итерациях отступ от Reset соответствует заданному.

FVDD и RESET на осциллограмме
FVDD и RESET на осциллограмме

Поэтому был сделан оверклокинг. Оверклокинг до 250MHZ повысил точность, достаточно для наших целей. Касаемо разгона Pico, в открытых источниках есть информация о разгоне до 436 МГЦ.

Точность попадания стала удовлетворительной
Точность попадания стала удовлетворительной

На картинке выше показано насколько стабильной стала точность. Зелёным цветом показан glitch, снизу - отчёт работающего скрипта.

Таким образом, мы смогли дойти до команды чтения. Как только произошёл успешный glitch, мы смогли получить ответ на эту команду, и начали с ней разбираться.

Постепенно удалось написать работающий код команды чтения. Были небольшие нюансы с таймаутом последовательного порта, так как объемы данных значительно увеличились, но, в целом, ничего сложного. После этих манипуляций удалось вычитать Data-flash и Сode-flash нашего процессора.

Фрагмент кода для чтения флеш:

                              # Read flash
                            conn.setTimeout(0.3)
                            # open file to write binary
                            filename_unique = "V850E2_%04X-%04X_%s.bin" % (
                                flash_addr[0],
                                flash_addr[1],
                                time.strftime("%Y%m%d-%H%M%S"),
                            )
                            f = open(filename_unique, "wb")
                            logger.info("writing file")
                            read_bytes = 0
                            while read_bytes < flash_size:
                                # Get size of the rest of the packet
                                length_answer = conn.read(0x403)
                                if (len(length_answer)<4):
                                    print("strange answer")
                                    exit()
                                length = struct.unpack("!H", length_answer[1:3])[0]
                                resp = length_answer[3:]
                                while len(resp) < length:
                                    rest = conn.read(0x400)                               
                                    resp += rest
                                    if len(rest) < 1:
                                        print("bad len")
                                        exit()                               

Итак, мы получили данные, которые находятся внутри процессора. Но что делать дальше, ведь нам надо восстановить блок? Ранее мы восстановили блок ABS. Тогда была похожая ситуация, связанная с обновлением прошивки блока системой ODIS. Поэтому мы взяли этот скрипт, взяли два ODX файла: один, которым блок был "обновлён" и другой, который должен был быть в нём на самом деле. С помощью скрипта восстановили её и записали в процессор.

Но это не сработало, потому что EEPROM блока был всё еще в непонятном состоянии. Пришлось взять рабочий "донорский" EEPROM, который нашёлся только для 104 версии firmware блока. Соответственно, прошивку в блок также залили 104, используя всё тот же скрипт. Только тогда блок поднялся. Взял надлежаещее питание (при включении блок потребляет около 1A, затем после зарядки конденсаторов это значение падает) и внутренняя ошибка пропала.

Фрагмент диагностики после ремонта
Фрагмент диагностики после ремонта

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

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

Комментарии (2)


  1. datacompboy
    13.02.2025 11:08

    Power Glitch как универсальная отмычка... А нигде нет базы коллективной с задержкой и шириной гличей под разные процессоры?


    1. ecurep Автор
      13.02.2025 11:08

      не слыхал о такой базе )) к тому же даже на этих процессорах есть ньюансы по паузам и оффсетам