Недавно мое внимание привлек факт, что в обновлениях прошивок для Linksys WRT120N используют какую-то обфускацию. Мне показалось, что будет интересно порыться в ней, и я решил взглянуть.
Последняя версия прошивки не выглядит как прошивка, с которой можно сразу работать
Как вы можете видеть, есть небольшой блок данных, сжатых LZMA — это просто HTML-файлы для веб-интерфейса роутера. Большая часть прошивки состоит из каких-то странных, случайных данных. Т.к. мне больше ничего с ней не сделать, а любопытство все сильнее пыталось одолеть меня, я купил эту модель роутера себе (как они стоимость Amazon Prime-то взвинтили!).
Анализ железа
При первом же взгляде на железо стало видно, что WRT120N работает на Atheros AR7240 SoC, имеет 2MB SPI флеша, 32MB RAM и что-то похожее на Serial и JTAG-разводку:
Для того, чтобы поглубже взглянуть на процесс загрузки, я решил начать с последовательного порта.
Я уже ранее рассказывал про последовательные порты, поэтому не буду заострять внимание на методы нахождения пинов и скорости в этой статье. Было легко найти выводы порта с использованием мультиметра и визуального осмотра платы:
Pin 2 – RX
Pin 3 – TX
Pin 5 – Ground
Порт работает на скорости 115200 бод и выдает интересную загрузочную информацию:
$ sudo miniterm.py /dev/ttyUSB0 115200
--- Miniterm on /dev/ttyUSB0: 115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
=======================================================================
Wireless Router WG7005G11-LF-88 Loader v0.03 build Feb 5 2009 15:59:08
Arcadyan Technology Corporation
=======================================================================
flash MX25L1605D found.
Copying boot params.....DONE
Press Space Bar 3 times to enter command mode ...
Flash Checking Passed.
Unzipping firmware at 0x80002000 ... [ZIP 3] [ZIP 1] done
In c_entry() function ...
install_exception
install exception handler ...
install interrupt handler ...
ulVal: 0x484fb
Set GPIO #11 to OUTPUT
Set GPIO #1 to OUTPUT
Set GPIO #0 to OUTPUT
Set GPIO #6 to INPUT
Set GPIO #12 to INPUT
Timer 0 is requested
##### _ftext = 0x80002000
##### _fdata = 0x80447420
##### __bss_start = 0x804D5B04
##### end = 0x81869518
##### Backup Data from 0x80447420 to 0x81871518~0x818FFBFC len 583396
##### Backup Data completed
##### Backup Data verified
[INIT] HardwareStartup ..
[INIT] System Log Pool startup ...
[INIT] MTinitialize ..
CPU Clock 350000000 Hz
init_US_counter : time1 = 270713 , time2 = 40272580, diff 40001867
US_counter = 70
cnt1 41254774 cnt2 41256561, diff 1787
Runtime code version: v1.0.04
System startup...
[INIT] Memory COLOR 0, 1600000 bytes ..
[INIT] Memory COLOR 1, 1048576 bytes ..
[INIT] Memory COLOR 2, 2089200 bytes ..
[INIT] tcpip_startup ..
Data size: 1248266
e89754967e337d9f35e8290e231c9f92
Set flash memory layout to Boot Parameters found !!!
Bootcode version: v0.03
Serial number: JUT00L602233
Hardware version: 01A
...
Похоже, прошивка сделана компанией Arcadyan, особенно интересно было сообщение ‘Unzipping firmware…’, быстрый гуглёж привел меня к посту про деобфускацию прошивок от Arcadyan, но тут, похоже, применяется немного другой метод.
Через последовательный порт можно только использовать меню загрузчика. Во время загрузки можно в него залезть, если нажать три раза пробел, и выполнить некоторые действия, такие как очистка флеша и установка настроек платы:
Press Space Bar 3 times to enter command mode ...123
Yes, Enter command mode ...
[WG7005G11-LF-88 Boot]:?
======================
[U] Upload to Flash
[E] Erase Flash
[G] Run Runtime Code
[A] Set MAC Address
[#] Set Serial Number
[V] Set Board Version
[H] Set Options
[P] Print Boot Params
[I] Load ART From TFTP
[1] Set SKU Number
[2] Set PIN Number
======================
К сожалению, загрузчик не позволял сдампить содержимое RAM или флеша. Хоть на плате и есть JTAG-разводка, я решил сдампить флеш напрямую, т.к. процесс дампа через JTAG, как правило, не быстрый, а подключение SPI очень простое.
Наверное любое устройство, которое поддерживает протокол SPI, может читать флеш. Я использовал кабель FTDI C232HM и программку spiflash.py из состава libmpsse
$ sudo spiflash --read=flash.bin --size=$((0x200000)) --verify
FT232H Future Technology Devices International, Ltd initialized at 15000000 hertz
Reading 2097152 bytes starting at address 0x0...saved to flash.bin.
Verifying...success.
Флеш состоит из трех LZMA-блоков и небольшого количества MIPS-кода, но сама прошивка все еще нехорошая:
Два первых LZMA-блока — часть recovery image, а MIPS-код это сам загрузчик. Все остальное место занимает обфусцированный файл прошивки, кроме нулей и каких-то данных в конце.
Анализ загрузчика
Загрузчик, помимо того, что он расшифровывает прошивку и загружает ее по адресу в память, содержит некоторые интересные штучки. Я пропущу скучные вещи, вроде того как я искал адрес загрузки загрузчика, вручную определял функции стандартной библиотеки C, находил таблицу смещения JUMP-ов и т.д., а сразу перейду к интересному.
Сначала, на раннем этапе загрузки, загрузчик проверяет, не нажата ли кнопка reset. Если она нажата, то он загружает образ “Tiny_ETCPIP_Kernel” — маленький recovery image, с веб-интерфейсом.
Это хорошая новость. Теперь мы знаем, что если что-то пойдет не так в процессе обновления прошивки, то можно будет нажать reset и оживить устройство.
А есть еще и скрытый режим администратора в меню загрузчика:
Нажатие "!" приведет к включению режима администратора, который разблокирует некоторые опции, включая чтение и запись в память.
[WG7005G11-LF-88 Boot]:!
Enter Administrator Mode !
======================
[U] Upload to Flash
[E] Erase Flash
[G] Run Runtime Code
[M] Upload to Memory
[R] Read from Memory
[W] Write to Memory
[Y] Go to Memory
[A] Set MAC Address
[#] Set Serial Number
[V] Set Board Version
[H] Set Options
[P] Print Boot Params
[I] Load ART From TFTP
[1] Set SKU Number
[2] Set PIN Number
======================
[WG7005G11-LF-88 Boot]:
Самая интересная часть загрузчика, конечно же, та, которая загружает обфусцированную прошивку в память.
Анализ обфускации
Деобфускация выполняется в функции load_os, которой передается указатель на обфусцированный образ и адрес, куда следует поместить распакованный образ:
Сама распаковка не особо сложная:
В общем, если прошивка начинается с 04 01 09 20 (а наша именно с этих байт и начинается), то выполняется алгоритм расшифровки, который:
- Переставляет два 32-байтных блока данных по адресам 0×04 и 0×68
- Переставляет 4 бита у первых 32 байт, начиная с адреса 0×04
- Побайтно переставляет смежные 32 байта, начиная с 0×04
После этого всего, данные по адресу 0×04 содержат верный LZMA-заголовок, которые потом разжимаются.
Реализовать утилиту деобфускации было просто, и прошивка WRT120N теперь может быть распакована и разжата.
$ ./wrt120n ./firmware/FW_WRT120N_1.0.07.002_US.bin ./deobfuscated.bin
Doing block swap...
Doing nibble-swap...
Doing byte-swap...
Saving data to ./deobfuscated.bin...
Done!
Анализ распакованной, но не разжатой прошивки
Если кому-то интересно, можете скачать утилиту для распаковки.
Автор: ValdikSS