Благодарности


Коллективными усилиями участников тематического tg чата особенно UnameOne, M, Karim, Nicky F. с форума 4pda, удалось разобраться в запутанных хитросплетениях исходного кода прошивок, архитектуре смартбоксов, чипа MT7621, технологиях пайки микросхем. Все эти модные софт скилы "командная работа", "межличностное взаимодействие", "креативность", "проактивность", "самостоятельность", "дисциплина" все еще слабоваты, но что-то точно улучшилось. Сотни часов с дымящимся паяльником и перед монитором, но я не бросил. Спасибо за это товарищам!


Спасибо Ватрушкину из Израиля, который натолкнул на идею сборки собственного программатора чипов NAND из грязи и палок. Про это возможно напишу отдельную статью, если будет настроение.


Хочу отдельно отметить человека по имени Андрей он же inflabz, который начал заниматься темой смартбоксов очень давно. Сейчас он отошел от темы, с ним очень сложно разговаривать. Люди, кто знал его раньше говорят: "Очень плохая музыка! Раньше лучше было". Тем не менее его вклад первопроходца нельзя не отметить. Кажется, что он чем-то тяжело и неизлечимо болеет. Мужайся, не раскисай, держись! Общение с ним недавно закончилось выпиливанием меня из телеграма до 1 февраля по чьей-то жалобе. Сразу вспомнились офисные интриги, сплетни, сенсации, которые я уже за время пока не работаю успел забыть. В общем если это он меня выпилил, я не держу зла. А если кто-то другой, то мне нет дела до этой жалкой ничтожной личности. :-)


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


Тем, кого незаслуженно забыл, простите и не обессудьте.


Введение



Я занимаюсь прошивками маршрутизаторов Билайн Смартбокс Флеш и Гига на базе чипа Mediatek MT7621A уже больше полугода. За это время чего только не было. Я заказывал программаторы чипов NAND с Али, возвращал их обратно, купил кучу флюсов, припоя, 3 паяльника, паяльную станцию, коврик для пайки, кучу проводков и raspberry pi. Накоплены бесценные знания и опыт, которые пришлось доставать по крупицам из сообщений форумов, китайских мануалов на просторах сети, репозиториев github тоже в основном китайских, исходных кодов программ.


Пришлось это все собрать, проанализировать, просеять, снова проанализировать, потом структурировать и обобщить. У меня короткая память, поэтому если так случится и через месяц или год я снова займусь чем-то подобным, то эта статья будет отличным мануалом. Надеюсь, что кому-то она понравится. Людям JS скрипта вряд ли будет интересно, но статья для настоящих пусть и начинающих инженеров/программистов, кто любит не только нажимать педали, но и залезть под капот.


Зачем понадобилось менять загрузчик, если он и так работал?


Резонный вопрос. Ответ очень прост. Лень. Дело в том, что скомпилированный автором загрузчик брид недоступен в исходных кодах, не уверен, что автор знает, что в России есть устройство, которое используется десятками (если не сотнями) тысяч людей по всей стране. Автор китаец поэтому логично, что он сделал загрузчик для китайского роутера Xiaomi mi router 3g. Этот роутер хоть и построен на том же чипе MT7621A, но все же отличается. В роутере Xiaomi кнопка reset подключена к 18 выводу GPIO, а в билайновских смартбоксах кнопки reset на всех моделях подключены к другим выводам. В smartbox flash reset на GPIO 3, а в smartbox giga на GPIO 4. Если установить на smartbox flash или giga загрузчик breed для Xiaomi mi router 3g, то кнопка reset не работает.


Кнопка reset в breed используется для прерывания загрузки, чтобы если в нем настроен автозапуск прошивки, этот запуск не производился, а брид продолжал бы работать. Если у вас настроенный роутер, который сразу запускает прошивку, то может быть довольно сложно прервать этот процесс без работающей кнопки reset. Если ты делаешь прошивки, или испытываешь чужие, то прерывать загрузку приходится очень часто. Автор брид предусмотрел еще 2 альтернативных способа прерывания загрузки. Всего мне известно 4 способа прерывания загрузки breed, 3 штатных и 1 нештатный.


Первый способ прерывания загрузки breed


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


Второй способ прерывания загрузки breed


Если у вас есть подключение к роутеру через интерфейс UART, то брид готов остановится, если получит любые данные через интерфейс. Проблема тут в том, что для подключения по UART требуется последовательный порт COM или USB устройство, которое будет эмулировать наличие такого порта. Нужно разобрать роутер и распаять контакты для UART подключения. Конечно распаянный UART вещь в хозяйстве полезная. Но разбирать и паять только для прерывания загрузки, как стрелять из пушки по воробьям.


Третий и лучший способ прерывания загрузки breed


Прерывание загрузки breed с помощью кнопки reset — самый простой и надежный способ. Только именно он и не работает на роутерах smartbox без модификаций загрузчика. Чтобы починить кнопку и понадобилось изменять загрузчик. В сообществе на форуме 4pda уже были люди, которые проделали трюк с изменением загрузчика. Но ни описаний, ни скриптов они не выложили, поэтому старые версии breed с переключенной кнопкой для smartbox, которые имелись в наличии, не решали проблему. До того, как полез в дебри реверс-инжинеринга я пытался достучаться до этих людей.


velomas


Первым выложил измененный брид с переводом интерфейса с китайского на английский. Он за пару недель не ответил ни на одно сообщение на форуме. Поскольку он время от времени появлялся на форуме, я ждал, что именно он что-то мне скажет. Скажи он, что именно нужно делать, результат был бы тот же, но удовольствия было бы гораздо меньше.


inflabz


Второго человека, который делал изменения кнопок (inflabz) на форуме уже давно не было, от дел он кажется уже отошел и пропал с горизонта. Он в свое время довольно сильно продвинулся в адаптации прошивки openwrt для smartbox, выкладывал все в паблик. Вел свой чат по смартбоксам. Он делал изменение кнопок. Я смог на него выйти через другого человека из прошивочной тусовки. Уважаемый inflabz, говорил как препод в шараге, отвечал вопросом на вопрос, но скриптов так и не дал, сам менять брид отказался. Кое-что полезное он таки сказал, а главное не дал моему интересу к теме угаснуть.


Sedy


Довольно одиозная личность в узких кругах. У него огромный опыт работы с различными моделями устройств, какое-то чумовое кол-во сообщений и ачивка супермодератор на gsm-forum. Он изгнан с форума 4pda за ведение коммерческой деятельности, но в нашем чате он состоит. Поскольку он сделал прошивки своим заработком, бесплатно делится знаниями он не настроен. По имеющимся данным он является обладателем нужных скриптов для модификации, который получил по блату от velomas. В чате пишет часто, но очень осторожно, больше читает. Его я даже не спрашивал.


Четвертый (нештатный) способ прерывания загрузки breed


Опытным путем был открыт этот способ, который пару раз выручал меня, когда первые три не работали.


Перед моментом запуска прошивки, когда брид уже передал управление следующей программе есть примерно 2 секунды, в которые брид уже загружен и работает, но еще не успел запустить прошивку. Если отключить DHCP и вручную установить IP адрес компьютера и шлюза (192.168.1.1), то за эти 2 секунды можно подключиться к брид через telnet и передать команды на отключение автозагрузки. Вот эти:


env unset autoboot.command; env save

Успеть за это время довольно сложно, поэтому получается через раз. Но на безрыбье и рак — рыба.


Описание структуры загрузчика breed


Для истории и тех, кто захочет поковыряться самостоятельно.
Структура загрузчика breed для xiaomi mi3g:
Размер заголовка 64 байта. Вот как выглядит этот участок памяти в редакторе.



/* uboot image header 64B */
typedef struct image_header {
    uint32_t        ih_magic;       /* 4B Image Header Magic Number */
    uint32_t        ih_hcrc;        /* 4B Image Header CRC Checksum */
    uint32_t        ih_time;        /* 4B Image Creation Timestamp  */
    uint32_t        ih_size;        /* 4B Image Data Size           */
    uint32_t        ih_load;        /* 4B Data   Load  Address      */
    uint32_t        ih_ep;          /* 4B Entry Point Address       */
    uint32_t        ih_dcrc;        /* 4B Image Data CRC Checksum   */
    uint8_t         ih_os;          /* 1B Operating System          */
    uint8_t         ih_arch;        /* 1B CPU architecture          */
    uint8_t         ih_type;        /* 1B Image Type                */
    uint8_t         ih_comp;        /* 1B Compression Type          */
    uint8_t         ih_name[12];    /* 12B Image Name               */
    nand_header_t   ih_nand;        /* 20B Этого куска нет в обычном заголовке uboot image, image name занимает не 12, а все 32 байта */
} image_header_t;

Применительно к MT7621, который установлен в smartbox flash, giga, turbo nand_header_t устроен так:


typedef struct nand_header {
    uint32_t                nand_ac_timing;             /* 4B */
    uint32_t                ih_stage_offset;            /* 4B stage1 offset */
    uint32_t                ih_bootloader_offset;       /* 4B bootloader offset */
    uint32_t                nand_info_1_data;           /* 4B */
    uint32_t                crc;                        /* 4B Хитрый crc, который поддавался дольше остальных */
} nand_header_t;

После заголовка идет предзагрузчик, который настраивает чип, распаковывает сжатый lzma загрузчик, загружает его и передает ему управление. Место, где начинается основной код загрузчика содержит заголовок с минимальным числом параметров. Для поиска заголовка загрузчика используется уникальный идентификатор Breed. 32 битное число 0x37540178. Затем идет 4 байта размера тела загрузчика, потом 4 байта адреса памяти, куда предзагрузчик записывает загрузчик, дальше 4 байта адреса точки входа, откуда будет запущен загрузчик.


Это участок программы, где с волшебного числа 0x37540178 начинается заголовок breed.



#define BOOTSTRAP_HDR_MAGIC     0x37540178
/* breed bootstrap kernel header */
typedef struct bootstrap_kernel_header_type {
    uint32_t magic; /* 0x37540178 */
    uint32_t length;
    uint32_t load_address;
    uint32_t entry_point;
} bootstrap_kernel_header_t;

Сразу после заголовка Breed идет заголовок lzma длиной 13 байт.


typedef struct lzma_header_type { 
 uint8_t lzma_props;                    /* 1B параметры сжатия (lc, lp, pb) */
 uint32_t dictSize;                     /* 4B размер словаря */
 uint64_t uncompressedSize;             /* 8B распакованный размер данных */
} lzma_header_t;

Значение байта с параметрами сжатия вычисляются по формуле: pb * 5 + lp * 9 + lc. Обратно значения параметров вычисляются так:


lc = lzma_props % 9; /* остаток от деления на 9 */
lzma_props /= 9;     /* Заменяем исходное число на полученное целое число при делениии на 9 */
pb = lzma_props / 5; /* целое число при делении на 5 */
lp = lzma_props % 5; /* остаток от деления на 5 */

В самом начале запуска чипа, после того как контроллер памяти MT7621A в состоянии считывать данные NAND памяти, он умеет работать только с uimage заголовком. Т.е. он умеет проверять только 1 контрольную сумму заголовка, копировать себя в нужный участок оперативной памяти, а потом начать выполнять код предзагрузчика из заданной в заголовке точки входа (ep). Предзагрузчик становится умнее, понимает lzma. Распаковывает из архива код breed и уступает ему место. Все это довольно очевидно. Для человека, который знаком с работой подобных устройств, все становится понятно сразу при виде знакомых идентификаторов. Я не такой, поэтому очень здорово, что Nicky F. посмотрел и любезно согласился все объяснить мне. В этом плане в нашей мини-тусовке есть критическая масса людей, которые готовы начать объяснять "от печки" или чуть дальше алфавита. Новичков натыкающихся на бетонную стену профессионалов легко развернуть в направлении выхода. А ведь как тяжело бывает этим профессионалам опускаться на уровень новичка. Все компьютерщики, которым приходилось побывать в шкуре мастера по настройке исчезнувшего курсора мышки, хорошо поймут о чем я толкую.


Тайну всех контрольных сумм разбирали несколько дней, я старался без устали, но все понял только когда меня ткнули носом в нужный участок исходного кода другого загрузчика mt7621. Поскольку для запуска загрузчика вторая контрольная сумма nand crc не проверяется, то успешно, если как-то самостоятельно записать даже такой неполноценный загрузчик, то работать он будет не хуже нормального. На этом можно было бы и остановиться. Как говорится: "Работает — не трогай!" Но очень хотелось пройти этот квест, поэтому никто не бросил. Прошло еще несколько дней прежде, чем сначала обнаружилось присутствие этой спрятанной контрольной суммы, а потом нашелся алгоритм подсчета этой контрольной суммы. Кто-то может сказать: "Ну что тут такого? Открыл исходники и все увидел". Таким я хочу напомнить, что загрузчик breed это программа с закрытым исходным кодом, автор китаец, который на вопросы и просьбы через гитхаб не отвечает.


Как я сказал, вторая контрольная сумма, которая не предусмотрена каноническим заголовком uimage спрятана в том участке, в котором записывается имя образа (image name). 32 байта для имени образа сократили до 12, а оставшиеся 20 байт пошли под еще 1 заголовок с еще 1 контрольной суммой. Вторая контрольная сумма в отличие от первой подсчитывается не по алгоритму crc32, а по старому алгоритму crc стандарта POSIX.


Вот как это работает.


  1. В заголовке Breed проверяется длина lzma архива.
  2. В заголовке uimage:
    2.1. image size (preloader и сам bin файл брида без 64 байт заголовка)
    2.2. image data crc (crc32 checksum preloader + bin)
    2.3. nand crc (crc POSIX 1003.2 checksum uimage заголовка, перед вычислением уже должен быть выполнен п. 2.1, а значение контрольных сумм nand crc и header crc должны быть установлены нулями)
    2.4. header crc (crc32 uimage заголовка, перед вычислением должен быть выполнен п. 2.3)
    Если все чексуммы сходятся, брид готов записать такой загрузчик. Спасибо за внимание, пишите, как говорится, письма. Буду рад ответить на вопросы.

Как все было


Пару недель назад, не теряя надежды модифицировать брид, я забрел в соседний чат специлизирующийся на прошивке PADAVAN для SMARTBOX. Слово за слово и откликнулся человек %username%, который на 4pda Nicky F. Он согласился помочь в разборе breed, хотя у него даже нет тех моделей роутеров, для которых я хотел делать модификацию. Видимо увидел в чате приятные сердцу слова про реверс и дизасм. В чатах довольно много людей что-то постоянно просят, но в основном приходят те, у кого не работает модем или что-то не так с прошивкой. Nicky F. рассказал что и куда нажимать. Я отвечал впопад, поэтому дело заладилось. Так я в первый раз дизасембировал код программы. Я не могу назвать себя совсем нубом в вопросе, так как уже к тому времени приходилось использовать отладчик, делать какие-то вставки в программы на языке ассемблера. Я видел ассемблер, но это были либо вставки в собственные простейшие программы или полученный во время компиляции ассемблерный код, который производит компилятор.


Переводить именно машинный код в ассемблер мне не приходилось ни разу. Чистый ассемблер полученный без оптимизаций компилятора, перемежающийся с исходным кодом и оптимизированный до неузнаваемости машинный код, который преобразован дизассемблером — это, как говорят в Одессе, 2 большие разницы. Страшно сказать, но первый моим дизассемблером стала Ghidra, та самая, которая родилась в недрах АНБ, но с недавних пор выложена в публичный доступ.


Через пару дней мытарств и безуспешных поисков обращений к участкам памяти, которые чип MT7621 использует в качестве интерфейса взаимодействия и управления GPIO. У нас только вчера появился MT7621 ProgrammingGuide на английском, где все четко и понятно написано. А тогда у нас был исходный код ядра линукс с драйверами для MT7621 и какие-то обрывки данных с китайских сайтов на китайском языке. Это был поиск чего-то примерно такого, выполняющее что-то примерно этакое. Но удача любит смелых, поэтому она мне улыбнулась. Nicky F. написал, что нашел нужную функцию. Китаец hackpascal (Weijie Gao — автор breed) заботливо написал функцию, которая прямо первым аргументом брала в десятичном виде номер нужного GPIO. А ведь управление осуществляется установкой нужных битов, поэтому вполне могло быть что-то типа такого в исходном коде:


#define GPIO_HIGH(i,g)  (g) < 32 ? (*(reg32_t *)(i + GPSET0) = 1<<(g)) : (*(reg32_t *)(i + GPSET1) = 1<<(g))  

Тут еще, поморщив брови, можно разобраться. Но чем это станет на ассемблере? Ведь тут код в виде MACRO, который компилятору попадает уже в виде просто чисел.


Для наглядности покажу то место в коде breed.



Я быстро сделал версии для обоих роутеров. Чик, чик и чудо случилось. Не пришлось даже сильно рисковать. Так как брид позволяет запустить себя без физической записи на чип памяти. Можно просто загрузить код в оперативную память и указать точку входа для запуска. Брид с работающей кнопкой сброса заработал. Затем я прошил его на место старого загрузчика и скрестив пальцы выключил и включил питание. Роутер включился, но к тому времени я уже несколько раз портил загрузчик и успешно перепаивал микросхему памяти. Паяльная станция стояла рядом, поэтому я переживал не сильно.


Заключение


Денег мне за это не платят, в значительной степени я занимаюсь всем этим ради таких моментов. Когда своими руками и головой удается заставить что-то работать это непередаваемое чувство. И почти неважно что именно ты создаешь этими руками. Это может быть сделанная стяжка пола в квартире, замена дисплея на разбитом айфоне сестры. Упорный труд плюс успешный результат дает это чувство. Я много лет работал в офисе, там этого сильно меньше. Офисный планктон с 9 до 18 перекатывают ручку с одного конца стола на другой, потом покупает форд фокус или даже бмв и квартиру в ипотеку, а потом едет в ГОА искать смысл жизни. А я с китайской паяльной станцией ковыряюсь с микросхемой памяти в общем не сильно нужного мне роутера, но я делаю и от этого счастлив, чего и вам желаю.

Комментарии (4)


  1. dec123
    01.02.2022 20:18
    +2

    Класс! Хороший реверс-инжиниринг. Осталось разобраться только с JTAG подключением и можно ничего не паять. :-)


  1. Dark_Purple
    02.02.2022 09:35
    +1

    Можно было бы картинку роутера-маршрутизатора запостить, а то что за он...


    1. johovich Автор
      02.02.2022 09:40
      +1

      Да, красивую надо найти. Или плату сфоткаю.


  1. gritsan
    02.02.2022 15:23

    Как же знакомо, такое счастье. Особенно когда почти утратил все надежды, а на устройстве появляется заставка.

    Может быть ниженаписанное будет здесь неуместно и неприлично.

    Попал сюда в поисках как заставить устройство на MTK 8127 увидеть полный объем замененного чипа Ram (1Gb) . На том же Gsm форуме пишут нужно править Preloader или аналогично не отвечают на вопросы. Так что просто оставлю это здесь)