Практический реверс-инжиниринг. Часть 4 – Полная выгрузка информации из флеш-памяти

Практический реверс-инжиниринг. Часть 4 – Полная выгрузка информации из флеш-памяти

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

Автор: Juan Carlos Jiménez

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

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

Нюансы при выгрузке памяти

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


Рисунок 1: Назначение некоторых пинов у внешней флеш-памяти

Кроме того, мы ознакомились с набором инструкций и можем взаимодействовать с микросхемой при помощи любого устройства, которое умеет «общаться» с шиной SPI.

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

Нужен демонтаж флеш-памяти? (теория)

Самый лучший способ остановить коммуникацию между Ralink и флеш-памятью – демонтаж и  полная изоляция микросхемы от остальной цепи. Данный метод позволяет нам полностью управлять ситуацией и исключает все возможные источники конфликтов, но, к сожалению, требует дополнительное оборудование, опыт и время. Мы попробуем пойти более простым путем. 

Второй способ – перевод Ralink в неактивный режим, в то время как все остальные элементы вокруг находятся в режиме ожидания. Зачастую у микроконтроллеров есть пин Reset, который, при подаче логического нуля, перезагружает микросхему без отключения питания на плате. Но у нас нет полной версии даташита (возможно, эта документация распространяется только среди покупателей на основе соглашения о неразглашении информации). Внешний вид микросхемы и сложность окружающей цепи серьезно затрудняю задачу по самостоятельному поиску нужного пина.

Третий способ – подача напряжения только на внешнюю флеш-память вместо включения всей цепи. Подача питания на плату недокументированным способом может привести к конфликтам. Мы могли бы изучить цепь питания, но поскольку роутер дешевый и распространенный, попробуем обойтись без дополнительных исследований. Согласно даташиту необходимое напряжение – 3 В. Я буду запитывать внешнюю флеш-память напрямую.


Рисунок 2: Схема прямого запитывания внешней-флеш памяти

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

Светоизлучающие диоды и другие статические компоненты не могут взаимодействовать с флеш-памятью и, соответственно, не могут стать причиной проблем, если мы подаем достаточный ток. Я использую ступенчатый источник питания, где предусмотрена подача различных токов на все случаи жизни. Если у вас нет подобного источника питания, можете попробовать воспользоваться системой питания внешнего (ведущего) устройства или каким-либо USB-адаптером питания в случае, если необходимы токи большего размера.

Теперь пришло время подключить внешнее устройство к шине SPI.

Соединение с флеш-памятью

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

Теперь, когда мы знаем назначения пинов флеш-памяти и переходника USB-SPI, пробуем подсоединиться.


Рисунок 3: Схема подключение переходника к флеш-памяти

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

Выгрузка информации

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

Flashrom – старое и нестабильное приложение, однако поддерживает модель FT232H (переходник) в качестве ведущего устройства, и модель FL064PIF (флеш-память) – в качестве ведомого. У меня возникли проблемы при работе в OSX и виртуальной машине с Ubuntu, но в конечном итоге все заработало в системе Raspberry Pi (ОС Raspbian):


Рисунок 4: Выгрузка данных из флеш-памяти при помощи flashrom

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

Разделение бинарного файла

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

Примечание: binwalk – очень полезная утилита для анализа бинарных файлов, созданная командой /dev/ttyS0;  Если вы занимаетесь исследованиями аппаратной части, вам определенно нужно познакомиться с ребятами из этой группы.


Рисунок 5: Базовая информация о бинарном файле, полученная при помощи утилиты bindwalk

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


Рисунок 6: Карта флеш-памяти

При наличии нужных адресов мы можем разделить бинарный файл на 4 базовых сегмента. Утилита dd принимает на входе 3 параметра в десятичном формате: размер блока (bs, в байтах), смещение (skip, количество блоков) и размер (count, количество блоков); Для преобразования из шестнадцатеричной системы в десятичную мы можем воспользоваться либо калькулятором, либо средствами шелла и командой  $(()):

$ dd if=spidump.bin of=bootloader.bin bs=1 count=$((0x020000))
131072+0 records in
131072+0 records out
131072 bytes transferred in 0.215768 secs (607467 bytes/sec)
$ dd if=spidump.bin of=mainkernel.bin bs=1 count=$((0x13D000-0x020000)) skip=$((0x020000))
1167360+0 records in
1167360+0 records out
1167360 bytes transferred in 1.900925 secs (614101 bytes/sec)
$ dd if=spidump.bin of=mainrootfs.bin bs=1 count=$((0x660000-0x13D000)) skip=$((0x13D000))
5386240+0 records in
5386240+0 records out
5386240 bytes transferred in 9.163635 secs (587784 bytes/sec)
$ dd if=spidump.bin of=protect.bin bs=1 count=$((0x800000-0x660000)) skip=$((0x660000))
1703936+0 records in
1703936+0 records out
1703936 bytes transferred in 2.743594 secs (621060 bytes/sec)

Мы создали 4 бинарных файла:

  1. bootloader.bin: загрузчик U-boot. Файл несжатый, поскольку микроконтроллер Ralink не умеет распаковывать эту информацию.
  2. mainkernel.bin: ядро в Linux. Базовая прошивка, управляющая системой bare metal. Упакована алгоритмом lzma.
  3. mainrootfs.bin: Файловая система. Содержит все важные бинарные и конфигурационные файлы. Упакована как squashfs при помощи алгоритма lzma.
  4. protect.bin: различные данные, упомянутые в третьей статье. Упаковка отсутствует.

Анализ отдельных частей бинарного файла

После разделения бинарного файла на 4 части рассмотрим подробнее каждый из этих сегментов.

Загрузчик


Рисунок 7: Информация о бинарном файле, имеющем отношение к загрузчику

Утилита binwalk нашла и декодировала заголовок uImage. Загрузчик U-Boot использует данные заголовки для идентификации важных областей памяти. Это та же самая информация, которая отображается командой file в случае с полным дампом памяти, поскольку  вначале идет тот же самый заголовок.

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

Ядро


Рисунок 8: Информация о бинарном файле, имеющем отношение к ядру

Перед извлечением полезной информации необходима распаковка. После анализа через binwalk мы еще раз убедились (как и во второй статье), что ядро упаковано алгоритмом lzma, который является очень популярным среди встроенных систем. Быстрая проверка при помощи команды strings mainkernel.bin | less подтверждает, что информации, пригодной для анализа, в файле нет.

Существует множество утилит для распаковки алгоритма lzma, например, 7z или xz, однако ни одна из этих утилит не смогла распаковать файл mainkernel.bin:

$ xz --decompress mainkernel.bin
xz: mainkernel.bin: File format not recognized

Скорее всего, дело в заголовке uImage, который нужно вычленить из файла. Мы знаем, что информация, упакованная lzma, начинается с байта 0x40. То есть нам необходимо скопировать все, кроме первых 64 байт.


Рисунок 9: Копирование части файла при помощи утилиты dd

Пробуем распаковать еще раз:

$ xz --decompress mainkernel_noheader.lzma
xz: mainkernel_noheader.lzma: Compressed data is corrupt

Утилите xz удалось распознать алгоритм lzma, но не запакованные данные. Мы пытаемся распаковать весь файл mainkernel, имеющий отношение к флеш-памяти, однако хранимая информация навряд ли занимается 100% сегмента. Попробуем удалить неиспользуемую память в конце файла и распаковать вновь:


Рисунок 10: Удаление ненужной части файла и повторная распаковка

Как видно из рисунка выше, распаковка завершилась удачно, что можно проверить при помощи команды strings, которая ищет строки в формате ASCII в бинарных файлах. Попробуем поискать что-нибудь полезное:


Рисунок 11: Поиск полезной информации в распакованном файле при помощи команды strings

Строка Wi-Fi Easy and Secure Key Derivation выглядит многообещающе, но впоследствии выяснилось, что данная строка определена в спецификации Wi-Fi Protected Setup spec. Ничего интересного, имеющего отношения к алгоритму генерации паролей.

С другой стороны, мы смогли распаковать информацию и можем двигаться дальше.

Файловая система


Рисунок 12: Информация о бинарном файле, имеющем отношение к файловой системе

Сегмент памяти mainrootfs не имеет заголовка uImage поскольку относится к ядру, а не к загрузчику U-Boot.

Файловая система SquashFS – очень популярна среди встроенных систем. Существует множество версий и вариаций, и иногда разработчики используют нестандартные сигнатуры, чтобы затруднить обнаружение данных внутри бинарного файла. В образовательных целях мы могли бы поиграться с различными версиями unsquashfs и/или модифицировать сигнатуры.


Рисунок 13: Пример сигнатуры файловой системы

Поскольку данная файловая система популярна, и поиск нужной конфигурации – довольно скучное и утомительное занятие, кто-то уже должен был написать скрипт, упрощающий решение этой задачи. Я нашел версию Firmware Modification Kit заточенную под операционную систему OSX, которая содержит скомпилированные версии unsquashfs и включает в себя скрипт unsquashfs_all.sh.


Рисунок 14: Поиск и распаковка нужной версии файловой системы

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


Рисунок 15: Некоторые элементы дерева файловой системы

В полной версии дерева можно найти все остальные файлы, используемые системой (а не только из папки /var/).

Теперь, используя наработки из предыдущих статей, мы можем приступить к поиску потенциально интересных бинарных файлов.


Рисунок 16: Некоторые бинарные файлы, представляющие для нас интерес

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

Защищенная область


Рисунок 17: Информация о бинарном файле, имеющем отношение к защищенной области памяти

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


Рисунок 18: Содержимое части файла protect.bin

Содержимое файла protect.bin, скорее всего, совпадает с файлом curcfg.xml: логи и некоторые строки, показанные на рисунке выше. Эту информацию мы уже проанализировали в третьей части и не нашли ничего нового.

Что дальше

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

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

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

Спасибо за внимание. 

Цифровые следы - ваша слабость, и хакеры это знают.

Подпишитесь и узнайте, как их замести!