Исследуем саундбар Yamaha YAS-109

в 14:14, , рубрики: nand, XOR, саундбары, шифрование файлов
Исследуем саундбар Yamaha YAS-109 - 1

Всем привет!

Краткое предисловие: я счастливый обладатель замечательного саундбара YAS-109 от Yamaha, на момент написания пользуюсь им уже целый год, и всё в целом хорошо. Но однажды я решил узнать: не подслушивает ли меня мой музыкальный друг? Ведь у него есть встроенная поддержка Alexa, а ещё Bluetooth, WiFi, Ethernet и другие прелести… Так и начинается история моего ресёрча.

Пробуем простой подход

Обычно самый простой способ изучить поведение железки — это посмотреть на файлы обновлений. Уже по ним можно сделать некоторые выводы о безопасности устройства, о подходе производителя к этой самой безопасности. Так я и сделал: полез в поиск, вбил название устройства, добавил слово firmware и первой же ссылкой получил страницу с файлами обновления:

Качаем прошивку

Качаем прошивку

В архиве меня ждёт следующий набор файлов:

Содержимое файла обновления

Содержимое файла обновления

На скрине видим четыре файла: mcu.enc, update.enc, yamaha_usb_upgrade.enc и yamaha_ver.txt. Конечно, это были никакие не файлы Wireshark, как рисует мне «Проводник», просто расширение файла совпадает. Это же расширение говорило мне и о том, что сами обновления, скорее всего, чем-то пошифрованы. Проверим? Суём файлы в hex-редактор и видим следующую картину:

Что-то на за

Что-то на зашифрованном

В самом начале можно заметить не очень приятное слово Salted__, что означает: смотри, мол, я зашифрован OpenSSL с использованием «соли». Чтобы забрутить это дело, могут уйти десятилетия, поэтому подход пришлось признать неудачным.

Пробуем сложный, но самый эффективный метод

Я с детства любил ковыряться в различных железках: тамагочи, гоночных машинках, старых магнитофонах. Разобрать такое, потом не собрать, получить за это по шапке от бати — вот моё счастливое детство. Прошли годы, и теперь «ковыряться в железках» — моя профессия. Конечно, сейчас на работе желательно собирать их обратно, но поскольку дают обычно два экземпляра, один идет под разбор.

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

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

При разъединении двух половинок корпуса действуем аккуратно: сенсорная панель саундбара соединяется с основной платой управления гибким шлейфом плюс двумя проводами: заземлением и питанием.

Переходим к основной плате управления. Часть её уходит под пластиковый корпус с перфорацией, поэтому снимаем его и любуемся платкой целиком. А встречает нас тут непонятный ABOV A33G526RLN. Поиск выдаёт информацию о каком-то 32-bit-Cortex-M3-based-микроконтроллере. Смотрим распиновку:

Pinout
Pinout

Сразу отмечаем для себя наличие ног JTAG: nTRST (49), TDI (50), TMS (51), TCK (52), TDO (57), nRESET (58). Так как корпус контроллера у нас не BGA-шный, каждый из указанных выводов можно прозвонить и найти, куда именно они выведены (если выведены). Достаточно быстро удалось выяснить, что ноги JTAG приходят на такой вот разъём:

Подозрительно нераспаянный разъём

Подозрительно нераспаянный разъём

Этот разъём практически идентичен соседнему с ним — чёрному шлейфовому разъёму. Такого коннектора у меня, конечно же, не было, как и шлейфов нужной ширины, поэтому я просто подпаялся к необходимым ногам:

Подозрительно подпаянный разъём

Подозрительно подпаянный разъём

Затем я припаял к МГТФ-проводам проводки с разъёмом «мама» и подключил их к JLink, согласно распиновке. Без особой надежды на успех (мало ли, отключен JTAG) я запустил железку. И, о чудо, JLink Commander встретил меня сообщением об успешно найденном Cortex-M3 контроллере:

Cortex-M3 identified

Cortex-M3 identified

Смотрим Memory Map в даташите:

Memory Map

Memory Map

Видим, что основной код лежит в «нуле», а загрузчик — в 0x1FFF_0000. Окей, пробуем сдампить. К сожалению, если читать память во время работы, в «нуле» нас будут ждать нули же. Поэтому делаем reset и читаем снова:

Дамп прошивки ARM получен

Дамп прошивки ARM получен

Как и ожидалось, первой прочиталась обычная таблица векторов ARM-контроллера. Обрадовавшись, я полез изучать полученный код, но, к сожалению, не нашёл ничего о расшифровке обновлений. Значит, с этими обновлениями работает кто-то другой…

Попробуем снова посмотреть на плату управления, вдруг можно сдампить что-то ещё.

Оказывается, можно. Нашлась флешка в корпусе SOIC8 с маркировкой BoyaMicro 25Q32BSSIG:

Маленькая флешка

Маленькая флешка

Засовываем флешку в программатор и видим что-то, чего увидеть совсем не ожидаем, а именно:

Дамп с маленькой флешки

Дамп с маленькой флешки

Первое, что бросается в глаза: хедер MAPX и никакого внятного содержимого внутри — возможно, здесь хранятся какие-то конфиги. Ладно, идём дальше, здесь ловить нечего.

А дальше — больше. Под припаянным экраном с поролонкой я нахожу интересный контроллер с маркировкой CSR A67175:

Что под шилдом

Что под шилдом?

Поиск по данному названию приводит меня на сайт Qualcomm с чем бы вы думали… с упоминанием технологии MAPX! Конечно же, публично доступных даташитов на данную приблуду не нашлось, скачать что-либо можно только при наличии полноценного подтверждённого аккаунта на сайте вендора, притом на обычного человека оформить его нельзя. Ну что ж… Понадеявшись, что всю основную работу с обновлениями делает не этот монстр без документации, я пошёл изучать плату дальше.

Помните тот модуль, который втыкается в штырьевой разъём на основной плате, с наклейками с указанием сетевых MAC-адресов, экраном и коннектором для антенны WiFi?

Такая вот странная плата

Такая вот странная плата

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

На обратной стороне я обнаружил крайне интересный объект:

Ура, NAND-флеша!

Ура, NAND-флеша!

Это была NAND флеш-память Kioxia TC58NVG1S3HTA00. К сожалению, на тот момент для моего программатора ChipProg-48 не было необходимого адаптера, а с обычной универсальной для TSOP48 колодкой флешку читать он отказывался. Пришлось обращаться к «дяде» по объявлению.

Заполучив дамп на USB-флешку, дома я первым делом полез смотреть, что же там. А ждал меня полнейший сюрприз:

Что-то на зашифрованном, опять

Что-то на зашифрованном, опять

Как видите, полнейший мусор. Расстроившись, я даже успел позвонить «дяде» и высказать всё, что о нём думаю, а именно: что за ерунду он мне сдампил? Наверняка программатор кривой, или контакт плохой или колодка не та. На что мужик мне ответил, мол, приходи завтра, попробуем сдампить снова и хотя бы сравнить дампы. Если читается мусор, тогда они не совпадут. Согласившись с этим утверждением, я немного успокоился. Но дожидаться завтрашнего дня не стал, а решил пристальнее рассмотреть содержимое дампа.

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

Я открыл даташит на флешу, отметил для себя организацию ячеек памяти — блоков и страниц: (2048 + 128) bytes × 64 pages × 2048 blocks. Далее я сделал предположение, что шифрование может применяться постранично или по блокам. Разбив дамп на блоки, я решил поксорить их между собой и выделить те, где оказалось бы наибольшее количество нулей, попутно сохраняя такие результаты. И, что интересно, этот подход дал свои плоды.

В некоторых результатах ксора появились более-менее читаемые строки:

Почти расшифровали

Почти расшифровали

Продолжив играться дальше, я выяснил, что первые 0x440 байт, поксоренные на третьи 0x440 байт, дают наиболее правдоподобный результат. Возможно, если поксорить второй такой блок с четвёртым, я тоже получу что-то вменяемое? Оказалось, что так оно и есть. Чуть позднее я выяснил, что первый блок можно также успешно поксорить и на 256-й блок. Написав скриптец для автоматизации сего действия, я принялся изучать полученные результаты. Но они меня не обрадовали.

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

Расшифровалось чуть лучше

Расшифровалось чуть лучше

Я загрузил этот код в IDA Pro (архитектура ARM64) и принялся его изучать. Кое-как подобрав ImageBase (он оказался равен 0x400), я бегло осмотрел распознанные функции и данные, которые в них используются. Конечно же, я наткнулся на список с маркировками NAND флеш-памяти, в том числе и имеющейся у меня:

Хмм, похоже на список флешек

Хмм, похоже на список флешек

Но ссылок на этот список IDA не нашла. Тем не менее прямо над ним нашлась крайне подозрительная табличка:

Какие-то сиды

Какие-то сиды

Что первым делом делает реверс-инженер, когда встречает незнакомые константы? Правильно, идёт с ними в поисковик. И, что забавно, константы нашлись на GitHub! Как раз в контексте NAND, а рядом упоминались такие слова, как randomizer и scrambling. Изучив тот код, я пришёл к выводу, что эти константы являются начальными значениями для генератора псевдослучайных данных (ключа) для шифрования данных на флеше. К сожалению, сама логика генератора с ходу не нашлась, но надежда на то, что я смогу побороть шифрование данных в дампе, появилась, и достаточно высокая.

Продолжив искать данные константы, я наткнулся на другой интересный репозиторий от ValveSoftware с названием steamlink-sdk. В этом репозитории данные константы использовались уже не как значения, которые отправляются в контроллер NAND, а тот их непонятно как использует. Здесь обнаружился… сам код генератора. Файл с именем prbs15.c содержал и саму таблицу сидов с названием seed_table, и такую функцию, как prbs15_gen, в которой эта таблица использовалась. Я незамедлительно переписал себе код функции на Python, запустил скриптец с крайне большой надеждой на положительный результат.

И… генерируемый скриптом ключ таки совпал с тем, что мне удалось получить, ксоря блоки между собой! Я ликовал!

Как оказалось, каждый сид использовался для генерации ключа размером 0x440 байт, который применялся на два блока такого же размера подряд, после чего брался следующий сид. Так расшифровывалась целая страница размером 0x22000 байт, после чего сиды снова брались с нулевого элемента. Итого получается, что реально использовалась лишь первая половина сидов из таблицы (64 элемента).

Наверняка к этому моменту у вас мог возникнуть вопрос: а почему вообще приём с ксором блоков мне помог? Дело в том, что в расшифрованном дампе оказалось очень большое количество блоков размером 0x440 байт, заполненных одними лишь нулями, либо FF. Если бы в дампе не нашлось таких блоков, данный подход бы не сработал. Пришлось бы что-то придумывать, например сравнивать дампы с флеши после каждого включения саундбара.

Особенности NAND-памяти

Вы наверняка уже и забыли о «дяде» и его предложении перечитать флешку. А я не забыл. К сожалению, вспомнить о нём мне пришлось, когда в некоторых местах расшифрованного дампа стали обнаруживаться не очень хорошие артефакты:

Особенности NAND

Особенности NAND

Как можно заметить, в двух разных блоках, данные в которых должны быть одинаковыми, некоторые строки оказались битыми. Так как в правильности ключа я был уверен, оставалось одно — местами битый дамп. Когда я спросил об этом товарища, он сказал, что это нормально и, возможно, стоит поиграться с изменением подаваемого при чтении флешки напряжения. Именно такие действия применяют умные ребята из Rusolut в своём видео об особенностях дампинга NAND.

На тот момент у меня по-прежнему не было нужной колодки для программатора, поэтому я снова созвонился с «дядей» и попросил его наделать для меня дампов при других подаваемых напряжениях. Получив в распоряжение 10 новых дампов, я принялся их сравнивать. Вы будете смеяться, но все были разными, при том биты в каждом менялись на абсолютно разных позициях! Полнейший треш…

С полученным осознанием безысходности и расшифрованным битым дампом, я принялся его изучать, первым делом натравив на него binwalk. Среди результатов поиска обнаружились сигнатуры UBIFS, скомпилированные device-tree блобы и много чего ещё. Я решил начать с device-tree.

Не буду сейчас вдаваться в структуру данного формата данных. Скажу лишь, что для каждого раздела, хранимого в структуре, хранится и SHA256-хеш, по которому можно определить целостность содержимого. После извлечения всех device-tree блобов и разделов из них, я стал их сравнивать. Так мне удалось обнаружить несколько разделов в разных блобах, отличающихся всего на некоторое количество бит. Найдя те разделы, у которых реальные хеши совпали с указанными в блобах, я взял их за основу при сравнении с теми, чьи хеши отличались. Таким образом мне удалось исправить биты в кривых разделах и немного починить оригинальный дамп.

Пришла пора переходить к UBIFS. На просторах интернета я нашёл скрипт ubidump.py и натравил его на расшифрованный дамп, чтобы тот попытался извлечь всё, что можно было. Но не тут-то было. В структуре блоков UBI имеется поле с контрольной суммой CRC32, и если она не совпадает, то дампер такой блок пропускает. К сожалению, таких блоков было очень много и нужно было что-то придумать.

А придумал я очень простую, но действенную вундервафлю:

Просто, но действенно

Просто, но действенно

Тут всё просто: если контрольная сумма не совпала, то «переключаем» один бит и снова считаем CRC32. Конечно, это не будет работать, если в данных, для которых идёт подсчёт, таких неправильных битов больше одного, но такого при сравнении дампов NAND я не обнаружил. Так, потихоньку, бит за битом, мне удалось исправить большое количество кривых байт!

Тем не менее, проблемы с UBIFS не закончились. Если вкратце, то в некоторых местах, где должны были быть блоки UBI, хранился какой-то мусор, который раньше я принимал за упакованные данные (файловая система позволяет хранить данные либо в виде потока ZLIB, либо сжатые LZO). Что это не сжатые данные, я понял, когда заметил цикличность в появлении мусора: начинались они со смещения 0xBB00 и повторялись каждые 0x22000 байт.

Неужто генератор ключа работает неправильно? Перепроверил — вроде всё нормально. Тогда в чём может быть проблема? Ответ оказался полнейшим выносом мозга! Дело в том, что контроллер NAND от MediaTek, прошивки от которого у меня не было, решил, что ему мало просто постранично что-то там скремблить. Надо ещё немного извернуться…

Вредина MT8516

Вредина MT8516

Так вот, умные ребята-разработчики прошивки этого контроллера решили, что нужно брать 22-й блок в каждой странице и шифровать его по-особому. А именно: брать 63-й сид из таблицы сидов, генерировать по нему ключ, но использовать байты получившегося ключа со смещения 0x118 (SIC!!!). Не спрашивайте, как я к этому пришёл. Но в итоге дамп стал получаться нормальным!

А вот что именно там обнаружилось, я расскажу в другой раз. Но для затравки: код там такой, что за голову хватаешься… Вот вам для подогрева интереса:

Login successful

Login successful

P.S. А ещё саундбар работает с сетью даже тогда, когда выключен (но воткнут в розетку).

Автор: Владимир

Источник

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


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