Главная » Хабрахабр » Реверс-инжиниринг прошивки устройства на примере мигающего «носорога». Часть 1

Реверс-инжиниринг прошивки устройства на примере мигающего «носорога». Часть 1

26 апреля 2018 года компания ИНФОРИОН провела конференцию для студентов МГТУ им. Баумана SMARTRHINO-2018. Специально для конференции было подготовлено небольшое устройство на базе микроконтроллера STM32F042.

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

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

Осторожно, под катом мигающий носорог и его прошивка!

Легенда

Участникам семинара была предложена следующая легенда.

Вам досталось устройство и небольшая инструкция к нему.

Прибор осветительный «Носорог»

Инструкция по эксплуатации

Прибор совмещает в себе стильный компактный дизайн, яркие светодиоды с низким потреблением тока и USB-интерфейс для подключения питания. Прибор осветительный «Носорог» предназначен для освещения помещений небольшой площади.

Предоставляются широкие возможности для контроля освещенности, позволяющие задавать оттенок и насыщенность для каждого светодиода в отдельности. Устройство оснащено Bluetooth-модулем для удаленного управления.

Управление устройством осуществляется через специальное программное обеспечение «Синезубик».

Приятного использования!

Упомянутого программного обеспечения для управления устройством у вас нет и необходимо написать его с нуля. Помимо этого необходимо удостовериться в безопасности использования данного устройства.

Если есть устройство, то можно попытаться получить его прошивку, вычитав её из flash-накопителя микроконтроллера. То есть всё, что есть у исследователя — устройство, которое можно включить. Этот этап был пропущен для упрощения и ускорения мастер-класса — участники получили готовый образ прошивки в виде бинарного файла rhino_fw42k6.bin (как если бы они получили прошивку, например, из обновлений).

Заинтересованный читатель также может скачать прошивку для самостоятельного исследования.

Для участников было доступно 4 рабочих «Носорога». Мастер-класс проходил в интерактивном режиме – с возможностью спрашивать, предлагать свои пути решения.

Внешний осмотр

Кратко: на этом этапе производится внешний осмотр устройства с целью поиска маркировок, доступных разъемов.

В начале семинара был сделан акцент на то, чтобы сначала внешне изучить устройство, потом уже приступать к реверсу прошивки.

В первую очередь, интересует микроконтроллер, потом периферийные устройства и разъёмы.

Внешний осмотр устройства позволил установить следующее:

  • Микроконтроллер STM32F042 – тут сразу стоит обратиться к документации на микроконтроллер (если такая есть), откуда можно узнать архитектуру, разрядность микроконтроллера и много чего полезного (для нашего случая – 32 разрядный микроконтроллер на архитектуре ARM);

  • На тыльной стороне имеется разъем без обозначений – те, кто работал с микроконтроллерами, могут сделать верное предположение, что это разъем для прошивки устройства (во-первых, он не промаркирован; во-вторых, он имеет 5 контактов, что соответствует необходимому количеству контактов для перешивки микроконтроллера);

  • Контакты GND, TX;
  • USB-разъем для питания устройства (об этом говорится и в «Инструкции»);
  • Неизвестный разъем XP2 на лицевой стороне устройства;

  • Непонятная желтая блямба на ноге носорога – вероятно, сенсорная кнопка.

Самые шустрые участники сразу же подключили питание устройствам и увидели следующее:

Также было обнаружено, что появились доступные Bluetooth-устройства c именами RHINOCEROS-220x, при подключении к которым в системе создаётся виртуальный COM-порт. Оказалось удобным подключаться к устройству по Bluetooth со смартфона и взаимодействовать через мобильное приложение «Serial Bluetooth Terminal» или аналогичное.

Было установлено, что при отправке в COM-порт произвольного текста устройство возвращает ответ Unknown command.

Начальное исследование прошивки

Кратко: на этом этапе выполняется предварительный анализ прошивки. Просмотр строк. Загрузка прошивки в IDA Pro.

Тут могут быть разные подходы, в простом случае достаточно воспользоваться утилитой strings, чтобы получить строки бинарного файла (приводятся в сокращении): Перед разбором кода прошивки имеет смысл проверить, не упакован ли код.

../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_cortex.c
../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_dma.c

Hardware init done... Starting FreeRTOS
sendMsg error %s
TSC %d
SET AUTH %d
cmd[%d] %s
UART task
Bluetooth task
AT+AB ShowConnection

AT-AB -BypassMode-
state bypass
ERROR: Wrong header length
cmd: %s
led idx %d hue %d sat %d val %d
msg %s
addr=%x, size=%x
User auth pass %s
Congrats amigo!
Wrong won't give up!
ERROR: Unk cmd
I've got a super power and now I'm seeing invisible tactical combatant nano-ants everywhere

uartRxTask
watchdogTask
sensorTask
bluetoothTask
ledsTask

Строк нашлось много – можно сделать предположение, что прошивка не сжата и не зашифрована. Уже на этом этапе можно обратить внимание на некоторые примечательные строки, например, форматные строки, строки с описанием ошибок и указанием операционной системы (а вы их увидели?). Наличие осмысленных строк, по большому счёту, можно считать половиной успешного реверса.

Будем использовать IDA версии 6. Что ж, попробуем загрузить прошивку в самый популярный дизассемблер. 9 для 32-битного кода (так как микроконтроллер 32-разрядный).

При открытии файла прошивки IDA не может автоматически определить архитектуру и точку входа – необходимо ей помочь.

На этом этапе необходимо снова обратиться к документации на микроконтроллер STM32F042x4 STM32F042x6 и посмотреть раздел 5 «Memory mapping»:

В качестве Processor Type выбираем ARM Little endian, ставим галку Manual load, нажимаем OK:

В окне «Do you want to change the processor type» нажимаем Yes, после этого IDA предлагает нам создать сегменты ОЗУ (RAM) и ПЗУ (ROM), ставим галку ROM.

На схеме нужно смотреть секцию Flash – это адреса 0x08000000 – 0x08008000. Теперь мы должны указать адрес начала ROM. Также укажем, что именно в этот же адрес мы хотим загрузить файл прошивки: Loading address = 0x08000000.

В окне "ARM and Thumb mode switching instructions" нажимаем OK.

Нажимаем OK. Далее IDA говорит, что ничего не знает о произвольных двоичных файлах и точку входа – функцию main — вы должны определить самостоятельно.

Можно изучать прошивку. Загрузка произведена.

Можно обратить внимание на то, что не все строки совпадают с результатами из утилиты strings – IDA распознала не всё, к сожалению. Откроем окно строк (Shift + F12). Немного погодя мы ей поможем…

Примечание для начинающих

  • Любая программа/прошивка представляет собой набор двоичных данных. IDA Pro может по-разному интерпретировать эти данные исходного файла (представлять данные в виде команд или данных в том или ином формате). При этом тут нет кнопки «Назад» (Ctrl+Z), чтобы отменить выбранное отображение — нужно знать, как переключаться между разными режимами отображения. (Шпаргалка по горячим клавишам IDA Pro)
  • Реверс-инженер из кажущегося хаоса двоичных данных восстанавливает логику, структуру и читаемость.
  • Строки – важная информация при реверсе! Так как, по сути, среди всего набора двоичных данных являются наиболее просто и быстро воспринимаются человеком. Строки позволяют делать выводы о назначении функций, переменных и блоков кода.
  • Именуй просмотренные функции! По умолчанию, IDA даёт функциям имена по их стартовым адресам. При анализе держать в голове эти адреса весьма сложно, гораздо проще пользоваться осмысленными именами. Для того, чтобы поименовать функцию достаточно хотя бы её беглого анализа – это уже будет важным подспорьем для дальнейшего анализа.
  • Именуй распознанные переменные! Для того чтобы эффективнее проводить анализ блоков кода и функций, имеет смысл именовать переменные, которые распознала IDA, в соответствии с их назначением (всё, как в лучших практиках программирования).
  • Оставляй комментарии, чтобы не забыть важное. По аналогии с программированием, комментарии при реверсе позволяют дополнительно пояснять логику работы программы или отдельных её участков.
  • По возможности создавай структуры! IDA в своём арсенале имеет средство работы со структурами, имеет смысл освоить это средство и применять его при необходимости. При наличии структур исследуемый код станет еще проще для восприятия.

Анализ строк

Кратко: Анализ строк может помочь составить примерный план исследования двоичного файла.

Итак, строки.

Hardware init done... Starting FreeRTOS
sendMsg error %s

cmd[%d] %s
rsp[%d] %s
UART task
Bluetooth task

AT+AB SPPDisconnect
AT+AB DefaultLocalName RHINOCEROS-2205

Лишь только на основании строк уже можно получить много информации:

  • Операционная система – FreeRTOS;
  • Наличие форматных строк – скорее всего используются printf-подобные функции, можно будет установить назначение регистров/переменных;
  • Названия задач (тасков) – можно предположить назначение этих самых тасков и связанных с ними функций;
  • Использование AT-команд – предположительно так строится взаимодействие микроконтроллера и Bluetooth-модуля.

Далеко не всегда всё так радужно при анализе прошивок – строк и debug-информации может не быть совсем или они малоинформативны, но при создании прошивки мы намеренно не стали усложнять процесс обратной разработки.

Идентификация стандартных функций

Кратко: на данном этапе необходимо удостовериться, что строки действительно распознались, после чего предстоит идентифицировать некоторые стандартные функции языка C.

После загрузки прошивки и автоматического анализа IDA распознала тела функций (не всех, кстати), но среди имён функций нет ни одного «нормального» (только автоматические имена от IDA), что может быть небольшой сложностью в сравнении с реверсом ELF- или PE-файла.

Таким образом, в ходе исследования нужно определить назначение не только специфических функций конкретной прошивки, но и идентифицировать стандартные C-функции. Может возникнуть разумный вопрос — где гарантия, что такие функции есть в прошивке и что они стандартные? Здесь стоит сказать, что обычно при создании программного обеспечения (в том числе прошивок), в 9 случаях из 10 не заморачиваются созданием своей уникальной libc-библиотеки, а пользуются тем, что уже написано и проверено временем. Именно поэтому в 90% случаев можно выдвигать предположение о наличии стандартных С-функций.

Стоит обратить внимание, что наличие декомпилированного листинга не отменяет необходимости понимать ассемблер, тем более, декомпил существует далеко не для всех платформ. Поскольку Hex-Rays Decompiler умеет превращать ARM-ассемблер в С-код, воспользуемся этой приятной возможностью.

Откроем окно строк в IDA (Shift+F12).

Выберем строку sendMsg error %s, откроем ссылки на эту строку (клавиша X – Xrefs — Cross References) — IDA распознала ссылки на строку, это хорошо:

При этом некоторые строки явно распознаны не полностью. Однако среди строк, выделенных зелёным в дизассемблере, есть просто байты, выделенные красным. Таким же образом можно пройтись по всем строко-подобным данным и превратить их в строки (или, например, написать Python-скрипт, который пробежится по указанному диапазону адресов и создаст строки). Так, например, если установить курсор на адрес 0x080074E6 и нажать клавишу A (потом согласиться с предложением «Directly convert to string?»), то получится строка «No device connected».

Попробуйте пройтись по строкам, нажимая клавишу X. Следующее препятствие, которое может возникнуть – нераспознанные ссылки на строки (даже если строка распозналась). Ссылка на объект может быть не найдена по двум очевидным причинам: Так, например, в моем случае не найдена ссылка на строку «recvMsg error».

  • нет кода, который ссылается на текущий объект;
  • IDA не распознала ссылку.

Постараемся исключить первую из них, выполнив двоичный поиск по прошивке. Открываем окно двоичного поиска (Alt + B), вводим адрес строки, не забываем поставить галочку «Find all occurrences»:

Получили одно вхождение:

Перейдём к нему (адрес 0x0800506С):

Появилась ссылка на строку: Превратим DWORD-число в offset, нажатием клавиши O.

Почему созданы двойные ссылки на строки?

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

Установим курсор чуть выше — внутри функции sub_8005070 (диапазон 0x08005070-0x08005092). Переключимся к декомпилированному листингу нажатием Tab:

Если вернуться к строке «sendMsg error %s», то можно увидеть, что она также передается в функцию sub_8006690. Обратим внимание на функцию sub_8006690. Пусть сейчас на уровне предположения это будет printf (даже если наше предположение окажется неверным, то оно всё равно позволит нам продвинуться в исследовании). Именно строки с символами форматирования могут привести к предположению о том, что функция sub_8006690 – это стандартный printf.

Префикс «x_» добавим для удобства (от слова «eXecutable») – так можно будет отличить переименованные нами функции от функций, имена которым дала IDA автоматически. Поставим курсор на имя sub_8006690, нажмём клавишу N, введём новое имя x_printf.

Выйти на него можно опять же через строки. Можно считать выполненной подготовительную часть, теперь перейдём к анализу таска, отвечающего за обработку Bluetooth-соединения. Так, можно сразу выбрать строки со словом «bluetooth»: Во многих окнах IDA можно выполнять поиск по Ctrl+F.

Что такое таск?

Таск (task, задача) – понятие из мира операционных систем реального времени (RTOS). Если по-простому, то таск можно представлять как отдельный процесс. Подробнее можно почитать в цикле статей о FreeRTOS

Bluetooth-таск

Кратко: идентифицировать и проанализировать функцию обработки команд, передающихся по Bluetooth. Необходимо будет создать дополнительный сегмент памяти в IDA.

Строка «Bluetooth task\r\n» не имеет кросс-ссылок — воспользуемся снова двоичным поиском, получим адрес, где она используется – 0x080058A0, перейдём туда и увидим список частично распознанных ссылок:

Создадим из них полноценные ссылки (проклацав клавишу O, или написав Python-скрипт для IDA).

Возможно, не везде создадутся ссылки (адреса, выделенные зелёным):

Исправляем — помогаем Иде. Перейдя по ссылкам, выделенным зеленым, увидим, что там не созданы строки.

Теперь в коде по адресу 0x08005556 появилась ссылка на эту строку: Вернёмся к строке «Bluetooth task\r\n».

Не забудем также дать говорящее название текущей функции «sub_8005554», например, «x_bluetooth_task». Здесь же видим, что эта строка передается аргументом в уже просмотренную нами функцию x_printf.

Обратим внимание на строку 132, где в функцию x_printf передаётся некое число. Переключимся в декомпил и просмотрим функцию полностью. Если изменить отображение числа с десятичной системы счисления на шестнадцатеричную (клавиша H), то увидим число 0x8007651, которое очень похоже на адрес.

Помогаем ей, правда, для этого нужно переключиться из декомпила в дизассемблер (клавиша Tab): делаем offset, переходим по нему, создаём строку. Уже знакомая ситуация – IDA не распознала ссылку. Переходим обратно в декомпил, нажимаем F5 (обновить).

Радуемся улучшению кода:

Явно, помимо форматной строки в x_printf должен передаваться еще список аргументов переменной длины (va_list), IDA этого не распознала… Ну вы поняли, да? Снова обратим внимание на строку 132. Поможем ей.

Впишем правильный прототип функции printf: Установим курсор на имя функции x_printf, нажмём Y – откроется окно изменения прототипа объекта.

int x_printf( const char *format, ... )

Эмм, простите, у вас ошибка в прототипе printf...

Согласен, правильно будет

void x_printf( const char *format, ... )

. И чуть позже мы это исправим.

IDA отобразит аргументы для форматной строки:

Пришло время установить назначение (имена) переменных (снова строки нам помогают):

  • x_printf("recv %s state %d\r\n", v0, v25);x_printf("recv %s state %d\r\n", recv_data, state);
  • x_printf("cmd: %s\r\n", v24);x_printf("cmd: %s\r\n", cmd);
  • x_printf("addr=%x, size=%x\r\n", v14, v15);x_printf("addr=%x, size=%x\r\n", addr, size);

Другие имена уже не так очевидны, но и не супер-сложны для понимания.

Например, обратим внимание на участок кода:

Логично переименовать: Переменная v3 сравнивается с числом 3, потом фигурирует сообщение о неправильной длине заголовка.

  • переменную v3 в header_len;
  • функцию sub_80006C8 в x_strlen (можно зайти в эту функцию и проверить наше предположение).

Далее обращаем внимание на следующий блок кода:

Внутри она выглядит так: Функция sub_80006B4 используется несколько раз.

Узнали её?

Переименовываем. strcmp. Создаём из хаоса и разрозненности стройный читаемый код.

Теперь обратим внимание на переменные v20000624, v20000344, v20000348. IDA выделила их красным цветом. Всё потому, что они ссылаются на адреса, которые отсутствуют в текущей базе дизассемблера. Если снова обратиться к документации на микроконтроллер, то можно увидеть, что диапазон адресов 0x20000000-0x20001800 относится к RAM.

Почему 0x20001800?

0x1800 — это 6Kb RAM, а это указано в документации

Если переменная ссылается на несуществующую область памяти, для нее будут недоступны xref’ы – исследование будет вызывать дискомфорт… Для удобства и производительности имеет смысл создать дополнительный сегмент памяти. Открываем окно сегментов (Shift + F7), добавляем RAM-сегмент:

Обращаем внимание на переменную unk_20000344: Обновляем декомпил.

Так и запишем, то есть назовем эту переменную. Очень похоже, что это некий auth_flag (флаг авторизации). В моем случае кросс-ссылок не нашлось – используем двоичный поиск и создаём ссылки.

Проверка на устройстве

Кратко: проверим отдельные предположения на работающем устройстве

Тут тоже простор для творчества, но если не усложнять, то самое простое – подключиться к устройству по Bluetooth, отправить какую-то команду и посмотреть на результат. Статический анализ – классная штука, но еще лучше, если есть возможность исследовать код в динамике.

Таким образом, функцию sub_8005234 можно переименовать в x_bluetooth_send. Так, например, при отправке строки «ZZZ» устройство ответит строкой ERROR: Wrong header length\r\n, при отправке «MEOW» (эта строка есть в исследуемом коде, передаётся в функцию strcmp) увидим mur-mur (>._.<)\r\n, а при отправке «ZZZZ» — ERROR: Unk cmd.

Вот что получилось: Составлю список команд, которые возможно поддерживаются устройством, и сразу проверю их.

  • “ECH1” – возвращает «ОК», включает эхо-режим – команда дублируется отправителю;
  • “ECH0” – выключает эхо-режим;
  • “MEOW” – возвращает «mur-mur (>._.<)\r\n » — то ли пасхалка, то ли отладочная команда;
  • “LED “ — выключает один из ярких светодиодов;
  • “UART” – возвращает «ОК»;
  • “BLE “ — однократно мигает красным светодиодом;
  • “READ” – возвращает «ERROR: Not auth!»
  • “WRIT” – возвращает «ERROR: Not auth!»
  • “AUTP” – возвращает «ERROR: auth error!»
  • “SETP” – возвращает «ERROR: Not auth!»
  • “VIP “ — возвращает «Wrong won’t give up!»

Промежуточные выводы относительно протокола:

  • команда состоит минимум из 4 символов;
  • есть довольно странные команды, как-то связанные с авторизацией (зачем авторизация на осветительном приборе?).

Улучшение кода. Создание структуры

Кратко: при возможности имеет смысл создавать структуры данных — большое подспорье для анализа.

Задача минимум для нас – научиться управлять светодиодами. Идём дальше.

Посмотрим, что находится в этой ветке: Эксперимент показал, что с большими светодиодами связана команда «LED » — по крайней мере, она позволила выключить один из четырех больших светодиодов.

Здесь можно было бы переименовать переменные, смущают только конструкции вроде

*(_WORD *)(v6 + 4) = sub_8005338(v4);

В большинстве случаев переменная v6 является указателем на структуру. Для удобства также создадим эту структуру. Контекстное меню для переменной v6 – выбираем пункт «Create new struct type».

IDA предлагает следующее определение для структуры:

Здесь доверимся автоматике относительно типов полей структуры, но установим читабельные имена, основываясь на данных из форматной строки:

struct struct_LED
{
_DWORD idx;
_WORD hue;
_BYTE sat;
_BYTE val;
};

После создания структуры код стал ещё приятнее:

Дополнительные переменные v7 и v8 для удобства также были переименованы. Переменная v6 по ходу дела была переименована в led. Пусть Вас не смущает появление дополнительных переменных – компилятору виднее.

Для перевода цвета из RGB можно воспользоваться таблицей. По сведениям из форматной строки можно сделать вывод, что цвет задаётся в формате HSV (Hue, Saturation, Value).

Про переменную v4 пока сложно что-то сказать наверняка, кроме того, что она является структурой и создается в функции sub_8005298:

Давайте так и назовём: Можно предположить, что переменная v4 представляет собой аргументы команды, пришедшие по Bluetooth.

  • v4 — bt_args
  • sub_8005298 — x_get_bt_args

В декомпиле возможна потеря ранее распознанной информации

При манипулировании именами и типами данных в декомпиле могут пропадать или появляться аргументы функций. В этом случае нужно для таких функций явно указывать их прототип (клавиша Y на заголовке функции). Из-за того, что в ARM’е первые 4 аргумента передаются через регистры, IDA при декомпиле может эти аргументы «терять», в этом случае… спешим на помощь ИДЕ. Если по декомпилу непонятно, какие аргументы передаются в функцию, идём в дизассемблерный листинг и смотрим на регистры R0-R3 – не заносятся ли в них какие-то значения перед обращением к интересуемой функции. Если заносятся, то в 90% случаев – это аргументы функции, и нужно прописать эти аргументы в прототипе.

Команда “LED ”

Кратко: исследование LED-команды, продолжаем переименовывать функции и переменные.

Сделаем еще несколько переименований для удобства восприятия:

  • sub_8003B6E – x_create_struct
  • sub_800532C – x_get_value_1
  • sub_8005338 – x_get_value_2

Зайдём в функцию x_get_value_1:

А теперь сравним функции x_get_value_1 и x_get_value_2: Переименуем sub_800530C в x_get_value_3.


При этом x_get_value_1 возвращает 1-байтовое число, а x_get_value_2 – 2-байтовое. В них используется одна и та же функция x_get_value_3, но с отличающимся вторым аргументом (2 и 4).

Анализируем работу с x_get_value_3:

  • работа ведётся со строкой bt_args (или структурой, содержащей строку);
  • когда на вход подается число 2, на выходе – число размером 1 байт;
  • когда на вход подается число 4, на выходе – число размером 2 байта.

Сопоставив эти факты, можно выдвинуть предположение, что функция x_get_value_3 формирует число из hex-строки указанного размера.

Выполним переименования:

  • x_get_value_1 — x_get_byte;
  • x_get_value_2 — x_get_word;
  • x_get_value_3 — x_unhexlify.

Проверим, используется ли функция x_unhexlify где-то ещё.

Функция sub_8005344 выглядит так: Используется.

Можно переименовать её в x_get_dword.

Заинтересованный читатель может погрузиться в статический анализ функции x_unhexlify и структуры bt_args — наверняка это будет увлекательно.

На данный момент мы можем сформировать команду для управления светодиодами:

Остаётся вопрос – нужны ли разделители между отдельными полями?

Пользуясь преимуществом наличия устройства, я проверю 2 варианта:

  • пробелы в качестве разделителей;
  • без разделителей.

Для включения нулевого светодиода в красный цвет (следуя таблице преобразования) необходимо задать следующие значения:

  • Индекс светодиода (idx) = 0x00;
  • Оттенок (hue) = 0x00;
  • Насыщенность (saturation) = 0xFF;
  • Значение (value) = 0xFF.

Команда с пробелами: "LED 00 0000 FF FF" — светодиод зажегся ярким светло-голубым цветом.

Команда без пробелов: "LED 000000FFFF" (пробел после символов «LED» нужен по формату команды) – светодиод зажегся красным цветом.

И здесь же можно выстроить предположение (которое могут подтвердить те товарищи, кто провёл полный статический анализ функции x_unhexlify), что функция x_unhexlify служит для потокового вычитывания информации с указанием размера из некого базового буфера. Таким образом, можно сделать вывод, что параметры команды должны передаваться без пробелов.

  • Включить первый светодиод зелёным: "LED 010078FF80"
  • Включить второй светодиод синим: "LED 0200F0FFFF"
  • Включить третий светодиод фиолетовым: "LED 03012СFF80"

В LED-ветке осталась неизученной функция sub_8003B7C. Она принимает на вход некую переменную dword_20000624. Посмотрим, где используется эта переменная – на всякий случай сразу воспользуемся бинарным поиском (Alt + B):

Хотела спрятаться! Обратите внимание на адреса 0x08004FF0, 0x08005D40. Помогаем Иде – создаём ссылки.

Посмотрим теперь, куда ведут ссылки off_8004FF0 и off_8005D40:

  • функция sub_8004D84 – явно стартовая функция прошивки, так как внутри используется строка «\r\nHardware init done… Starting FreeRTOS\r\n» — переименуем эту функцию в x_main;
  • функция sub_8005A08 – в самом начале использует строку «LED task\r\n» — переименуем эту функцию в x_leds_task.

Таким образом, переменная dword_20000624 используется:

  • ближе к концу функции main;
  • после получения данных по Bluetooth в x_bluetooth_task;
  • в начале цикла функции x_leds_task.

Те, кто программировал потоки в обычной ОС или же работал с тасками RTOS, увидят в этой переменной указатель на очередь для обмена данными между тасками – и правильно сделают. Выполним еще несколько переименований:

  • dword_20000624 — leds_queue;
  • sub_8003BD0 — x_queue_recv;
  • sub_8003B7C — x_queue_send.

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

Переименуем:

  • sub_800501C — x_sendMsg;
  • sub_8005044 — x_recvMsg.

Теперь, чтобы убедиться, что мы полностью умеем управлять светодиодами, исследуем функцию x_leds_task.

На этом мы немного прервёмся, выпьем чай с шоколадкой и продолжим во второй части статьи.

Результаты первого этапа

  • Проведён внешний осмотр устройства.
  • Прошивка загружена в дизассемблер.
  • Найдены полезные для исследования строки.
  • Было установлено, что «носорог» управляется через Bluetooth по простому текстовому протоколу.
  • Частично исследован таск обработки команд протокола обмена по Bluetooth.

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Наша книжная полка С#-программиста. А что у вас?

Привет! Обычно мы рекомендуем несколько источников, сопровождая их своими комментариями, почему именно они будут полезны. Будущие студенты Veeam Academy часто спрашивают нас о книгах, которые были бы полезны при подготовке к поступлению на наш курс по программированию на С#. Поэтому ...

[Из песочницы] Компрессия больших массивов простых чисел

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