Недавно в моём распоряжении оказалась партия умных часов с поддержкой геолокации, которые после тестирования были отправлены в резерв. Мне было поручено найти применение для этих девайсов, с чего и началась история их реверс-инжиниринга, о которой я поведаю в этой статье.
Начну я с рассмотрения их внешних особенностей и схемы, после чего подробно расскажу о том, как перепрограммировал эти часы, в том числе пропатчив их под иные задачи.
▍ Первичный осмотр
В своём штатном состоянии часы не имели никаких фишек и сопровождались всего одной страницей с инструкциями о том, как их заряжать и использовать. Каждый комплект состоял из часов и зарядки к ним. Не было никаких README, сайтов или порталов разработчиков. На поверхности часов присутствовал всего один ёмкостный датчик, включавший в себя дисплей, на котором пользователь, переключая экраны, мог видеть время, отслеживать сердечный ритм и получать некоторую отладочную информацию.
Экраны часов
Отладочный экран предоставлял полезные сведения для идентификации часов. Я заметил, что указанный на нём IP-адрес был одинаков для всех часов одной модели. Изначально я подумал, что это IP-адрес сервера, с которым часы взаимодействуют.
Зарядка от часов
Часы имели степень защиты IP67. Это означало, что они были водонепроницаемы, и я, располагая ограниченным набором инструментов, никак не мог получить доступ к их внутренностям без нанесения повреждений. В духе первооткрывательства я решил попытаться вскрыть часы с помощью плоскогубцев. Было нелегко, но спустя час старательного ковыряния мне удалось «снять» верхнюю крышку корпуса.
Вскрытие устройства
Внутренности
Внутри оказалась печатная плата с LiPo-батареей. Также в корпус были встроены две антенны, подключённые к коннекторам u.FL на плате.
▍ Реверс-инжиниринг платы
Часы управляются микросхемой nRF52832 с поддержкой Bluetooth и содержат ещё две основных: Wi-Fi модуль ESP8285 и сотовый чип от SIMCOM. Поначалу меня сбило с толку присутствие Wi-Fi микроконтроллера, поскольку ничто не говорило о наличии у часов поддержки Wi-Fi. Но позднее я выяснил, что он используется для триангуляции в городской черте.
Так как точность GPS в городских условиях достаточно низкая, приблизительное местоположение часов можно определить при наличии точки доступа Wi-Fi, используя данные сотовой сети и данные GPS. Судя по схеме, nRF52832 обменивался данными с чипом SIMCOM по UART и отправлял команды для взаимодействия с мобильной сетью. Зная это, я сосредоточился на поиске UART или контактов для программирования на микросхеме nRF52832, поскольку она была основной, и именно эти подключения обычно используются для взаимодействия с микроконтроллером.
На плате присутствовало довольно много контактов, принадлежащих UART и предназначенных для программирования. Одна из интересных дорожек пролегала от двух позолоченных контактов на правой стороне платы к SWDIO и SWCLK на микросхеме nRF52832, а также линии, которая, как я ожидал, использовалась для зарядки часов. SWDIO и SWCLK — это JTAG-контакты для программирования микросхемы. На нижней стороне лицевой крышки часов также присутствовали подпружиненные контакты, которые замыкались с площадками SWDIO и SWCLK, когда часы были закрыты.
Причём эти контакты также были подключены к медным выводам на лицевой стороне часов, предоставляя внешний доступ к возможности программирования. Наличие внешнего доступа к программированию было немного странным, поскольку обычно прошивка устройства заливалась на плату с завода и впоследствии обновлялась через Wi-Fi или Bluetooth. Как правило, в доступности вывода для программирования нужды нет. Тем не менее эта особенность оказалась крайне полезной позднее, поскольку, благодаря ей, для доступа к прошивке мне не требовалось вскрывать все остальные часы. Необходимость жёсткого вскрытия каждого устройства сделала бы бессмысленной мою задачу изменить их функциональность, так как обратно собрать часы после такого будет весьма непросто.
Более интересно же было то, что на зарядке подпружиненные контакты, подключённые к SWCLK и SWDIO, были подсоединены к контактам D+ и D- порта microUSB, которые обычно используются для передачи данных. Это означало, что для перепрограммирования часов мне не придётся собирать кастомную платформу — достаточно просто распустить кабель microUSB, чтобы подключать линию программирования к любым часам. Приятный нюанс.
Контакты
Вскрытие кабеля
После того как я подключил часы к своему отладчику JLink через зарядку, первым делом я заметил, что они производят отладочный вывод с помощью приложения JLink RTT Viewer. Отлично! И, хотя возможность наблюдать отладочный вывод была очень полезна, из-за отсутствия ввода, настроенного для RTT-модуля, не было возможности отправлять часам команды. Тем не менее полученный вывод подтвердил мои предположения о внутренней схеме подключения часов.
UART-взаимодействие микросхемы nRF52832 с модулем SIMCOM
После нескольких экспериментальных попыток отправить команды через JLink я решил взглянуть на прошивку. Подключив Jlink, я смог сделать её дамп с помощью команды nrfjprog
, сопроводив её флагами --readcode
и --readram
.
Дамп штатной прошивки
Прошивка оказалась не защищена от чтения или записи, поэтому я смог получить её дамп целиком. В этот момент у меня было два варианта для перенастройки функциональности часов:
- Перепрограммировать их полностью, залив новую прошивку.
- Пропатчить IP-адрес и порт, чтобы часы отправляли данные на сервер, которым управляю я.
Поскольку перепрограммирование часов предполагало весьма масштабный процесс, я решил пойти путём патча.
▍ Переходим в Ghidra
Чтобы определить соответствующие функции и переменные в Ghidra, мне нужно было сделать дамп RAM и флэш-памяти. Поскольку это голый код с устройства Cortex M0+, мне пришлось декомпилировать прошивку в формат ARM с прямым порядком байтов, после чего Ghidra смогла сгенерировать получитаемый псевдокод.
На этой стадии мне нужно было определить расположение IP-адреса, который, как я считал, был указан где-то в коде. Часы по каналу сотовой связи отправляют данные на сервер, адрес которого показывался на их отладочном экране. Если я смогу обновить IP-адрес, то смогу и перенаправить передаваемые часами данные на подконтрольный мне сервер.
Однако мои первые попытки найти строку с IP-адресом, который я видел на экране часов, не увенчались успехом.
Моей следующей попыткой был поиск ближайшей строки из отладочного вывода, который я наблюдал в RTT Viewer. Ей оказалась AT+CIPOPEN=0
, представляющая последовательную команду, отправляемую с nRF52832 на модуль SIMCOM с инструкцией открыть соединение с IP-адресом, указанным в виде аргумента. Отыскав эту строку, использующую IP-адрес сервера, с которым связываются часы, далее я мог бы найти область памяти, где хранится этот адрес.
Строка, обнаруженная в памяти
Более успешным оказался поиск строки формата. В найденном совпадении присутствовал паттерн строки, похожий на паттерн IP-адреса. Он вызывался в функции, подозрительно похожей на sprintf
.
Ссылка на строку, вызываемую в функции sprintf
, вызывающую четыре других переменных
Функция sptintf
ссылалась на массив, хранящийся в DAT_20000887
, который, судя по карте памяти микросхемы nRF52832 ниже, соответствует разделу данных в RAM.
Карта памяти nRF52832
Перейдя в область размещения RAM, я выяснил, что в него пишут две функции. Интересна же только первая часть этой области, поскольку именно в ней находился IP-адрес. Вторая часть позволяет выполнять последующее обновление этого адреса по воздуху.
Перепрыгнув к первой функции, я обнаружил эти жёстко прописанные шестнадцатеричные переменные, записанные в массив в рамках основной функции. Эти переменные как раз соответствовали IP-адресу, который я видел на отладочном экране часов.
Область памяти, которую нужно пропатчить
Эти области памяти представляли шесть байт данных прошивки, которые мне нужно было пропатчить для изменения IP-адреса и порта.
Всего надо пропатчить 6 байт, 4 для IP и 2 для порта
Здесь возникла небольшая заминка: в качестве номера порта выступало значение 38899, сохранённое в виде двух байтов — 0x0c
и 0x68
. В скомпилированной программе использовалась инструкция movn
, которая применяла логическую операцию NOT
к жёстко прописанным значениям до их помещения в RAM. Чисто технически, можно было пропатчить эту инструкцию, удалив операцию NOT
. Но я пытался минимизировать количество изменяемых байтов, поэтому добавил дополнительную операцию при преобразовании номера порта в подходящие шестнадцатеричные значения.
▍ Перепрограммирование часов
На основе этого понимания я смог написать простой скрипт для пропатчивания прошивки на любой IP-адрес и порт. Этот скрипт также обновлял контрольную сумму каждой изменяемой строки, чтобы прошивка соответствовала ожидаемому формату. После завершения патчинга я залил прошивку на часы с помощью утилиты Programmer от компании Nordic Semiconductor.
Скрипт патчинга прошивки
Заливка прошивки
И рад сообщить, что всё прошло прекрасно! Обновив прошивку для взаимодействия с сервером, я смог получать данные с телефона и обрабатывать их.
Пропатченная прошивка
На этом этап реверс-инжиниринга завершается. В ходе процесса я многому научился, поскольку имел мало опыта по перепрограммированию устройств на базе ARM. Тем не менее у меня было хорошее понимание того, как бы я разработал устройство, и какими возможностями оно обладало, что помогло значительно сузить диапазон поиска.
Интересной новостью стало то, как интерфейс программирования был подключён к USB-порту, чего в других аналогичных часах я не встречал. Отсутствие какой-либо защиты от чтения/записи прошивки также является нетипичным, поскольку другие IoT-устройства обычно получают такую защиту при выводе в продакшен с целью предотвратить возможное клонирование.
Как бы то ни было, процесс оказался очень интересным, и возможность найти новое применение электронике, которая в противном случае осталась бы надолго пылиться на складе или была бы выброшена, придала проекту дополнительный смысл.
Автор: Дмитрий Брайт