MKS Robin. Часть вторая - укрощение строптивой прошивки.

Подписаться на 3Dtoday
jmz
Идет загрузка
Загрузка
13.12.18
5319
28
печатает на FLSUN 3D Metal Frame Prusa i3 DIY KIT
Техничка
72
PREVIEW
Написание альтернативной прошивки или экстремальное мигание светодиодом.

Штурм и натиск

Не удовлетворившись диагностикой по фотографии, проведенной в первой части я заказал себе MKS Robin для более близкого знакомства.
Для обновления прошивки на MKS Robin используется предустановленный загрузчик, который MKS никогда и никому не дает. Чтение памяти микроконтроллера внешним отладчиком заблокировано, а разблокировка вызывает очистку всей флеш-памяти, необратимо превращая MKS Robin в тыкву дорогую отладочную плату. Таким образом, единственный вариант добраться до загрузчика - это изнутри самой прошивки. Ограничения на чтение памяти не распространяются на программу внутри микроконтроллера.

Тренировка на кошках, роль которых выполняла отладочная плата на STM32F103ZET6, дала свои результаты и к моменту приезда MKS Robin у меня уже была пара загрузчик/прошивка, которые я собирался использовать для насильственного вывода Robin из сумрака.

Разумеется ничего из запланированного не заработало. Загрузчик охотно принимал альтернативную прошивку, но после обновления микроконтроллер наглухо зависал. Перепробовав несколько вариантов альтернативных прошивок и не сумев даже зажечь светодиод (пин PB2) я был вынужден расписаться в собственной криворукости и отказаться от идеи штурма в лоб.

В процессе работы прошивка MKS Rоbin пишет диагностическую информацию в COM порт (UART3), поэтому альтернативным вариантом проникновения на охраняемую территорию стала модификация оригинальной прошивки. Анализ и модификация адресов текстовых строк подтвердил правильность изначальной оценки расположения прошивки во флеш памяти, что позволило загрузить код в online disassembler.

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

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

После отбраковки нескольких вариантов была найдена функция достаточно большая, чтобы в нее можно было поместить полезную нагрузку. Для проверки работоспособности троянского метода мне потребовался внешний светодиод, подключенный к линии DIR драйвера E0. Эта нога микроконтроллера гарантированно использовалась в нужном режиме (GPIO Output).

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

Полезная нагрузка в виде переносимый код для форматированного вывода содержимого флеш памяти в шестнадцатеричном виде на UART3:
    volatile uint32_t i;
    uint8_t data;
    uint8_t hex;
    uint32_t pointer;
    GPIOA->BSRR = (uint32_t)GPIO_PIN_15 << 16U;
    for (pointer = 0x08000000 ; pointer  < 0x08080000; pointer ++) {
     if ((pointer & 0x0000000f) == 0) {
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >> 28) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >> 24) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >> 20) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >> 16) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >> 12) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >>  8) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)((pointer >>  4) & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        hex = ((uint8_t)( pointer     & 0x0F)) + 48; if (hex > 57) hex += 7;
        USART3->DR = (uint16_t) hex;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        USART3->DR = 58;
        while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
        USART3->DR = 32;
     }
     data = *((uint8_t *)pointer);
     while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
     hex = (data >> 4) + 48;     if (hex > 57) hex += 7;
     USART3->DR = (uint16_t) hex;
     while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
     hex = (data & 0x0f) + 48;     if (hex > 57) hex += 7;
     USART3->DR = (uint16_t) hex;
     while ((USART3->SR & UART_FLAG_TXE) != UART_FLAG_TXE) {}
     if ((pointer & 0x0000000f) == 0x0000000f) {
        USART3->DR = 10;
     } else {
        USART3->DR = 32;
     }
    }
    for (;;) {
    GPIOA->BSRR = GPIO_PIN_15;
    for (i = 0; i < 3000000; i++) __NOP();
    GPIOA->BSRR = (uint32_t)GPIO_PIN_15 << 16U;
    for (i = 0; i < 3000000; i++) __NOP();
    }


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

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

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

Шифровка из китая

Трофейный загрузчик прекрасно разместился на отладочной плате, не высказывая никакого неудовольствия от отсутствия привычных LCD экрана и SPI флеш-памяти. Наличие подключенного ST-Link так же не вызвало у загрузчика никаких возражений, поэтому разблокировка микроконтроллера MKS Rоbin была отложена на неопределенный срок и вся работа велась на отладочной плате.

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

Поворотным моментом стала попытка запустить альтернативную прошивку на MKS Robin. Загрузчик отрапортовал об успешном обновлении прошивки, после чего микроконтроллер наглухо завис, в точности повторив свое поведение на ранней стадии знакомства. Причина столь разного поведения абсолютно одинаковых микроконтроллеров нашлась очень быстро, ей оказалось отличие методов заливки прошивки. MKS Robin обновлялась с SD карты с использованием штатного загрузчика, в то время как отладочная плата, для ускорения процесса, прошивалась через ST-Link. Прошивка отладочной платы с SD карты полностью воспроизвела поведение MKS Robin, а подключенный отладчик тактично намекнул, что микроконтроллер поймал HardFault exception.

Сравнение содержимого памяти микроконтроллера и файла с прошивкой показало, что совпадают только первые 320 байт. Заливка оригинальной прошивки дала еще более интересную картину - первые 320 байт заливаются без изменений, следующие 30 килобайт меняются, а начиная с 31041-го байта все опять заливается как есть. Таким образом MKS Robin использует частичное шифрование прошивки (30КБ из ~430КБ). Все мои альтернативные прошивки были короче 30 килобайт и полностью убивались в процессе заливки. Модифицированная же прошивка имела полезную нагрузку в нешифрованной области, что и позволило сделать дамп содержимого флеш памяти. Причиной повышенной криворукости и многодневной фрустрации оказался альтернативно-одаренный подход китайских разработчиков к шифрованию прошивки.

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

Секретный китайский ключ
A3BDAD0D4111BB8DDC802DD0D2C49B1E26EBE3334A15E40AB3B13C93BBAFF73E

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

Дальше - больше. К мигающему светодиоду добавились:
  • поддержка COM порта для вывода отладочной информации
  • поддержка SD карты, для создания резервной копии загрузчика
  • PlatformIO проект со скриптами для автоматического шифрования прошивки в ‘Robin.bin’ пригодный для обновления с SD карты штатного загрузчика
В результате получился полный набор инструментов, позволяющий запустить на MKS Robin произвольный код (Marlin) используя штатные средства обновления прошивки. Код традиционно доступен на GitHub
Подписаться на 3Dtoday
72
Комментарии к статье

Комментарии

13.12.18 в 17:10
6
Нет слов. Браво!

Всегда доставляет читать статьи такого рода.
13.12.18 в 17:19
1
Здорово! Интересно читать. :)
13.12.18 в 17:55
0
молодец. ИДА довольно сносно потрошит кортексы.
28кб загрузчика - это конечно крупно.
13.12.18 в 17:59
0
Сильно.
13.12.18 в 18:26
0
Восторг! Супер! И чего, будем марлин 2 шить теперь?
13.12.18 в 20:41
3
Планы именно такие, но сначала я хочу сделать переходник с разъема TFT экрана на традиционные EXP1/EXP2
Это даст возможность подключить поддерживаемые Marlin'ом экран, энкодер и SD карту и получить полнофункциональный набор электроники.
13.12.18 в 18:28
1
Крррасота. Следующий челлендж — порвать Lerdge?
13.12.18 в 20:30
4
Насколько мне известно, это уже сделано
13.12.18 в 21:34
1
О, круто. Не интересовался.
14.12.18 в 12:00
0
Автор так и не смог марлин там запустить, насколько я понял.
14.12.18 в 12:15
0
Автор (xC0000005) пишет, что поддержка F4 в ST Arduino оставляет желать лучшего.
Arduino код на Lerdge-K запускается, Marlin - пока нет.
https://github.com/MarlinFirmware/Marlin/issues/12624
13.12.18 в 19:25
0
Надо стянуть загрузчик с Гитхаба пока китайцы не удалили, а то у вас там пароль легко подбираемый (Мао Цзэдун) :)
Если серьезно, снимаю шляпу!
13.12.18 в 19:34
1
ВАУ, субер, браво, отлично.....
Секретный китайский ключ
A3BDAD0D4111BB8DDC802DD0D2C49B1E26EBE3334A15E40AB3B13C93BBAFF73E
После этого сообщения , каждая российская домохозяйка сможет не только выбирать президента в США, но и председателя ЦК КНР :) :)

Предположу, этот секретный ключ подойдет и к версии Робин Лайт.
13.12.18 в 20:51
0
Спасибо!
Отличная новость!
13.12.18 в 23:10
2
Вот ведь. Абсолютно не интересная для меня тема, в которой я еще и мало что понимаю, но прочитал на одном дыхании и даже понял почти все.
Браво! И даже не столько за саму проделанную работу, сколько за стиль и качество изложения.
Спасибо.
14.12.18 в 10:56
1
СНИМАЮ ШЛЯПУ!
А чем плоха оригинальная прошивка? Или все это ради спортивного интереса?
14.12.18 в 12:19
4
В первую очередь тем, что закрытая, и пользоваться приходится тем, что дал производитель.
Я не верю, что при портировании Repetier Firmware на STM32 китайцы стали менять 8-ми битную математику.

Функционал у оригинальной прошивки тоже не блешет возможностями. Печатать она может, но не более того.
Простейший пример - управление вентиляторами ограничено одним-единственным вентилятором обдува модели.
Вентиляторы обдува термобарьера и драйверов шаговых двигателей предлагается подключать напрямую в 12в и шуметь они будут постоянно.
14.12.18 в 14:09
0
Будем ждать прошивку )
14.12.18 в 20:53
0
Браво!!!
А с daVinci 1.1 так можно?
А то родная мама хороша - а вот ее прошивка проприетарное УГ...
14.12.18 в 21:17
0
Смысла нет - в свободной продаже плата отсутствует, открытой прошивки для этого микроконтроллера нет.
14.12.18 в 22:07
0
Понимаю, но так хотелось..))
Репитер на похожую маму (daVinci 1.0) какая то добрая душа таки вкорячила.
А если судить по дизайну - это таки одна и та же мама но с разными компонентами - на моей есть посадочные места для драйвера второго экструдера (daVinci DUO) и под SD-карту (daVinci 1.0).
Вот только в daVinci 1.0 на борту SD-карта с которой идет прошивка а моя мама рулится по UART с "экрана" на Андроиде.
Вобщем, Малина уже приехала и Октопринт на нее уже накинут. Жду MKS GEN L - буду лепить Франкенштейна с блекджеком и шлюхами.
За одно и то недоразумение которое там за директ стоит на Титан с V6 поменяю.

А вот потом попробую устроить перехват UART - может выйдет подружить ее с Октопринтом через GPIO Малины.
15.12.18 в 00:30
0
Офигенно! Когда марлин или клиппер будет? Есть плата, готов поучавствовать в тестах.
16.12.18 в 02:23
1
Ларчик то просто открывался. Защита совсем детская, но нервов погрызла. Я с подобной "защитой" сталкивался еще на древнем PDP11. Не далеко же ушли китайцы ))) Формально все готово чтобы портировать марлин насколько понимаю - есть обратный реверсинженеринг что куда распаяно, есть как надо отхорить прошивку для того чтобы ее обратно "испортил" родной загрузчик. Проблема наверное в основном в sdio режиме работы SD карты...Доделаете проект? Сейчас как раз кручу в руках это чудо
17.12.18 в 14:14
0
http://3dtoday.ru/blogs/jmz/marlin-robin-two-nozzle/
Marlin работает, но без SDIO и экрана.
20.12.18 в 16:04
0
Шикарно! Отличная работа!
17.01.19 в 17:04
0
Аплодирую стоя,Снимаю Шляпу ну т.д )))
17.01.19 в 17:13
0
Уважаемый JMZ ! Возможно не открою для тебя Америки? Есть на Гит Хабе один проектик который Автор забросил. Думаю ты расберешься ,что с Этим делать )

Для написания комментариев, пожалуйста, авторизуйтесь.

Читайте в блогах

Градус Флюкса в двух решениях

Арифмометр Curta. Часть 2

На фестивале 3Dtoday Fest покажут новейшие 3D-принтеры и расходные материалы

Почти спонтанный стрим с Артёмом Соломниковым #2

Технологии 3D-печати помогли итальянскому инвалиду стать пилотом

Безграничные возможности 3D-печати на Top 3D Expo