Найти в Дзене
mamaich

Снова про интерактивный u-boot на Яндекс ТВ станции

Ранее я уже писал про то, как можно заставить u-boot Яндекс ТВ Станции запускать наши скрипты с USB с помощью пайки. Есть и полностью программный способ, но я его пока не публикую, так как он очень не стабилен (например, вчера у меня процесс занял 2.5 часа). В отличие от Яндекс Станции Макс и Яндекс Модуля, на ТВ Станции изменения в u-boot вносятся регулярно, причем изменения довольно заметные, что приводит к перемещению функции cli_loop в памяти, из-за чего требуется повторно дампить расшифрованный u-boot и искать в нем нужные мне места. В один момент мне это надоело, и я решил, что неплохо бы автоматизировать поиск. На Яндекс ТВ Станции в u-boot нехорошие люди отключили команду "go", зато добавили возможность загружать файлы с файловой системы (команды fatload/ext4load/imgread) и использовать команду bootelf для запуска специальным образом написанных программ. Далее я опишу, как сделать программу, которая будет запускаться по bootelf, искать в памяти начало cli_loop по сигнатуре и п

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

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

На Яндекс ТВ Станции в u-boot нехорошие люди отключили команду "go", зато добавили возможность загружать файлы с файловой системы (команды fatload/ext4load/imgread) и использовать команду bootelf для запуска специальным образом написанных программ. Далее я опишу, как сделать программу, которая будет запускаться по bootelf, искать в памяти начало cli_loop по сигнатуре и передавать на нее управление.

Я сравнил дампы u-boot для Яндекс Модуля 2, Яндекс Станции Макс и Яндекс ТВ станции, и везде функция cli_loop выглядит следующим образом:

зачеркнуты байты, отличающиеся в разных версиях u-boot
зачеркнуты байты, отличающиеся в разных версиях u-boot

То есть, мы можем сделать поиск такой последовательности:

FD 7B BF A9 FD 03 00 91 ? ? ? 97 00 00 00 14

(вопросительный знак = байт может иметь любое значение).
Данная последовательность встречается в коде u-boot только один раз. Но если сканировать всю память - она может попадаться дважды, так как, например, в Максе, в начале памяти лежит кусок от u-boot, использовавшийся в процессе расшифровки.

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

В начале кода я определяю приблизительный адрес u-boot в памяти по значению регистра LR. Адрес округляю вниз до границы в 16 мб. U-boot у нас меньше 16 мб, так что адрес захватывает адресное пространство до его начала, но это не критично (там обычно нули). Определение адреса по значению LR я сделал для универсальности - возможно, буду использовать данный ELF не только на ТВ Станции. Также это помогает исключить временные копии u-boot (как в Станции Макс).
Далее в коде идет поиск сигнатуры и передача управления по найденному адресу.

Компиляция кода производится так:

aarch64-linux-gnu-gcc -Os -nostdlib -ffreestanding -fno-stack-protector -c ./cli_loop.c -o ./cli_loop.o
aarch64-linux-gnu-ld -Ttext=0x02000000 --entry=_start --no-dynamic-linker -nostdlib --gc-sections -o cli_loop.elf cli_loop.o

Ключи в первой команде говорят, чтобы GCC не рассчитывал на наличие стандартной библиотеки, не добавлял защиту от переполнения стека (иначе будут ошибки "undefined reference to __stack_chk_guard".

Вторая команда линкует итоговый файл. Так как u-boot не умеет делать релокацию, то загружаемый файл должен быть привязан к абсолютным адресам. Я выбрал адрес 0x02000000 для начала кода (секция .text). Остальные секции будут расположены вслед за ней.

Загрузка файла в память производится командой u-boot:

fatload usb 0 0x01ff0000 cli_loop.elf

Загрузка файла будет с первого FAT/FAT32 раздела флешки, вставленной в верхний разъём.

Почему адрес загрузки 0x01ff0000, а не 0x02000000? Потому что в начале файла находится заголовок ELF, и если мы загрузим файл по адресу 0x02000000, то там окажется заголовок, реальное содержимое секции .text сдвинется дальше, и наша программа просто упадёт. Размер заголовка в моем случае был 64 кб (0x10000), так что я ELF загружаю на эти 64кб ниже.

Далее стартуем файл на выполнение:

bootelf -p 0x01ff0000

После добавления этих команд в aml_autoscript получаем интерактивную консоль:

обратите внимание, что тут мы видим правильный адрес 0x2000000
обратите внимание, что тут мы видим правильный адрес 0x2000000

И этот способ работает на всех версиях u-boot, которые я проверил.