Pull to refresh

Как отобрать у микроконтроллера QN902x его прошивку

Reading time7 min
Views17K

Введение

Когда у меня появился умный чайник Xiaomi Mi Smart Kettle Pro - у меня возник тот же вопрос, что и у многих его пользователей: почему его нельзя включить удаленно? Чайник позволяет подключиться к нему через блютуз, задать температуру которую он будет поддерживать и еще пару незначительных параметров, но его невозможно удаленно включить, что нивелируют его ценность как компонента умного дома.

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

Аппаратная часть

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

Вокруг микроконтроллера нет ни капли силикона и виднеется окись
Вокруг микроконтроллера нет ни капли силикона и виднеется окись

Блютуз-модуль должен быть намертво залит силиконом, но китайцы часто экономят и на плате можно встретить всего пару капель силикона, из-за чего модули довольно быстро коррозируют при активном использовании чайника. После нескольких циклов отмачивания в растворителе и грубой чистке щеткой с платы снимаются хлопья силикона, и она становится пригодна для анализа. Платка компактная, состоит из трех основных компонентов (МК, флешка и предположительно микроконтроллер сенсорных кнопок). На платке достаточно много пятачков для доступа к ключевым цепям для тестирования и прошивки. Микроконтроллер достаточно большой, и, слава богу, не BGA, что позволяет отследить дорожки на плате. Вооружившись скальпелем и мультиметром можно выяснить какие ноги микроконтроллера к каким точкам подключены (добрые китайцы оставили на плате шелкографию и у точек есть имена для их идентификации). После прозвона выходит примерно следующая картина:

Часть схемы с точками и разьемами
Часть схемы с точками и разьемами
Расположение точек (CAD MS Paint)
Расположение точек (CAD MS Paint)

На самой плате не так и много свободного места, к тому же мне попадались платы как минимум двух видов (различия незначительные), было необходимо как-то зарисовать расположение точек, а заодно и назначение разъемов. Всего два разъема, каждый с тремя проводами, все разных цветов. Назначение всех проводов кроме одного удалось выяснить сняв маску со второй, силовой платы, однако назначение одного из контактов так и осталось для меня загадкой. На одной версии силовой платы он не подключен вовсе, на другой через резистор посажен на землю. Вероятно, он был нужен когда-то раньше, а затем от него отказались, но для совместимости решили форм-фактор плат не изменять. Назначение U3 для меня остается не совсем ясным, но похоже что это контроллер сенсорных кнопок. U1 это явно SPI-флешка, но никакой документации по нанесенным на корпус кодам я найти не смог.

QN9020

У использованной тут модели чипа (QN9022) память для программ не интегрирована внутрь самого чипа, но используется внешняя флеш-память с интерфейсом SPI. Казалось бы - бери и читай, но зачастую в таких случаях используется шифрование данных на флешке, с ключем уникальным для конкретного микроконтроллера. И этот чип оказался не исключением, в даташите явно сказано:

Защита флеши
Защита флеши

Стоит обратить внимание на вторую заметку: код, выполняющийся внутри микроконтроллера, всегда может вычитывать из флеши данные в их декодированном виде. И даже SWD блокируется после прошивки. У этого чипа есть два брата-близнеца (QN9020 и QN9021) с важным отличием от QN9022: у них флешка интегрирована внутрь чипа. Однако, структура и способ доступа к ней никак не отличается, это все еще отдельный кристалл с интерфейсом SPI.

Тем не менее, никто не гарантирует что процесс прошивки был выполнен в соответствии с рекомендациями и интерфейс SWD действительно отключен, так что стоит проверить этот вариант в первую очередь. Китайцы оставили на плате точки для подключения, видимо использовавшиеся для отладки, однако SWCLK соединено с линией питания резистором с нулевым сопротивлением R4. Зачем это может быть сделано? У этого микроконтроллера, как и у многих других, одни и те же ноги могут быть сконфигурированы для разных целей, и нога P0_7 может быть как SWCLK, так и AIN3.

Мультиплексирование ног
Мультиплексирование ног

Зачем нам тут AIN3 к подключать к питанию? На это нам ответит схема устройства АЦП.

Схема АЦП
Схема АЦП

AIN3 может быть сконфигурирован как опорное напряжение для АЦП. Учитывая, что у нас аналоговый температурный сенсор, это ожидаемый ход. Значит, для работы SWD надо выпаять R4, и подпаявшись к пятачкам T9-T12 можно попробовать подключиться к микроконтроллеру, для чего я попытался использовать J-Link. Однако, результат немного предсказуем, контроллер не отвечает и наиболее простой путь оказался недоступен.

Загрузчик

Значит, нужно копать дальше, и в первую очередь изучить как происходит процесс прошивки. А происходит он через загрузчик, что хороший знак: чем больше кода, тем больше шансов встретить баг сюрприз-фичу. Загрузчик может работать через UART или SPI и имеет простой командный интерфейс. В нашем случае, необходимые для прошивки пины через UART вынесены на точки T1-T3, и остается подпаять их к USB-UART преобразователю.

Блютуз-модуль спаянный с USB-UART преобразователем
Блютуз-модуль спаянный с USB-UART преобразователем

Рассмотрим команды загрузчика:

CMD Code

UART Commands

Functions

0x33

B_C_CMD

Build connection with bootloader.

0x34

SET_BR_CMD

Set UART baud rate used in ISP mode.

0x35

SET_FLASH_CLK_CMD

Set clock frequency used by QN902x’s flash.

0x36

RD_BL_VER_CMD

Read bootloader version.

0x37

RD_CHIP_ID_CMD

Read the chip number of QN902x.

0x38

RD_FLASH_ID_CMD

Read flash ID of QN902x.

0x39

SET_APP_LOC_CMD

Set application routine download location, internal SRAM or Flash.

0x3A

SETUP_FLASH_CMD

Set the flash operation commands.

0x3B

SET_ST_ADDR_CMD

Set the start address of Read, Program, Erase and Verify commands.

0x3C

SET_APP_SIZE_CMD

Set the application size.

0x3E

SET_APP_CRC_CMD

Set the CRC result of verifying application.

0x40

SET_APP_IN_FLASH_ADDR_CMD

Set the starting address of application storage location.

0x42

SE_FLASH_CMD

Sector erase flash

0x43

BE_FLASH_CMD

Block erase flash

0x44

CE_FLASH_CMD

Chip erase flash

0x45

PROGRAM_CMD

Download.

0x46

RD_CMD

Read NVDS.

0x47

VERIFY_CMD

Verify the application.

0x48

PROTECT_CMD

Enter into protect mode.

0x49

RUN_APP_CMD

Run application.

0x4A

REBOOT_CMD

Reboot system. (software reset)

0x4B

WR_RANDOM_DATA_CMD

Write a random number to Bootloader.

0x4C

SET_APP_IN_RAM_ADDR_CMD

Set the starting address of application location in the SRAM.

0x4D

SET_APP_RESET_ADDR_CMD

Set the address of application entry point.

И там же, рядом, карта разметки флеши:

Карта разметки флеши
Карта разметки флеши

В глаза бросается команда чтения RD_CMD, которая позволяет прочитать флешку по указанному адресу. Казалось бы - задача решена, но на практике область действия RD_CMD ограничена первыми 0x1000 байтами, в которых располагается NVDS (non-volatile data storage, набор хранимых параметров в виде словаря).

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

Диаграмма загрузки программы в ОЗУ
Диаграмма загрузки программы в ОЗУ

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

Странные данные
Странные данные

Явно виднелась какая-то последовательность, повторяющаяся каждые 0x100 байт, но это отдельная тема. А вот плата перестала запускаться, хотя до этого исправно мигала диодами и сообщала о себе через блютуз. Чтож, видимо придется копать глубже. Посмотрим на карту памяти микроконтроллера:

Карта памяти
Карта памяти

Нас интересуют области помеченные как "BootLoader" и "ROM". Хотя прошивка и погибла в процессе считывания, код загрузчика никуда из области ПЗУ деться не мог, а значит был доступен для чтения. Анализ, как ожидалось, мог открыть какие-то ошибки реализации командного интерфейса, или проявить скрытые команды (например, показать куда делись команды с пропавшими из списка команд кодами, например 0x3C, вдруг там окажется UNPROTECT_CMD). Дальше уже ничего сложного: загружаем в ОЗУ свой код, через него вычитываем загрузчик, сохраняем его и анализируем дизассемблером. Анализ кода показал следующее:

  1. При загрузке программы (первая команда PROGRAM_CMD) загрузчик стирает флешку. Разумный ход, хотя в документации об этом не было ни слова.

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

  3. Обнаружилась довольно неожиданная реализация switch-case, поначалу поставившая меня в ступор: вычисление адреса команды выполнялось в подфункции, которая вместо возврата выполняла переход на вычисленный адрес исходя из переданного кода, адреса возврата и массива констант сохраненных по адресу возврата.

После этих новостей у меня появилась новая идея: закоротить флешке ноги MOSI/MISO (или другим способам аппаратно защитить ее от стирания) на время выполнения первой команды PROGRAM_CMD, однако мне не нравился этот способ своей неэлегантностью и неприменимостью к чипам QN9020/QN9021, и я отложил его на случай если ничего другое не сработает. Продолжив ковырять загрузчик просто из интереса, внезапно мне бросились в глаза команды WR_RANDOM_DATA_CMD и SETUP_FLASH_CMD. Про WR_RANDOM_DATA_CMD в документации сказано кратко: The procedure of writing a 32bit random number to bootloader. Зачем, куда? Не ясно, особенно много вопросов вызывает тот факт, что реально загрузчик пишет не 4 байта, а 12 байт присланные с этой командой, однако это снова отдельная тема. Вторая команда куда интереснее:

Описание SETUP_FLASH_CMD
Описание SETUP_FLASH_CMD

SPI-флешки имеют командный интерфейс, и хотя формат команд у разных флешек почти всегда одинаковый, то их код может сильно отличаться. Разработчики чипов QN902x приняли славное решение, позволив пользователю самому задавать код команд и тем самым сделав QN9022 почти универсальным чипом в плане совместимости с разными SPI-флешками. В том числе, внимание, код команды для стирания прошивки. И несмотря на слова When the internal Flash is not existent, очень похоже что у чипов с интегрированной флешой этот процесс ничем не отличается. А значит нужно просто-напросто отправить набор команд, в котором коды стирания будут подменена на что-то безобидное, вроде кода чтения или кода выведения флеши из режима сна.

Конец

И... Это работает!

Достаточно после установления связи с загрузчиком отправить первой командой альтернативный набор команд для работы с флешью, затем загрузить в ОЗУ прошивку с реализацией моста-считывателя, вернуть на законное место оригинальный набор команд и запустить прошивку из ОЗУ. После этого можно сдампить флешку, а после перезагрузки плата вернется в рабочее состояние. Разработчики не предусмотрели банальную верификацию очистки флеши, что заняло бы едва ли десяток инструкций и защитило от подобного рода атаки.

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

Мою реализацию вышеописанного метода можете посмотреть тут: https://github.com/a-sakharov/qn902x-dump

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 98: ↑98 and ↓0+98
Comments52

Articles