Comments 52
Со стороны компьютера — исполняемый файл с прошивками (разными для разных вариантов устройств), содержащимися в ресурсах. Программа обнаруживает подключенное устройство, запрашивает у него данные о нем и выбирает соответствующую прошивку.
Этой же программой передается и содержимое EPROM-памяти (которая отдельным чипом в устройстве) — настройки, калибровка и т.д., это уже идет без шифрования :)
Протокол предусматривает запрос от устройства серийного номера, уникального идентификатора микроконтроллера, версий загрузчика и прошивки, задание нового серийного номера, обновление и считывание EPROM, обновление прошивки.
Единственный минус — девайс подключается по USB как CDC (COM-порт), что требует драйверов, поэтому сейчас перевожу все это дело на HID :)
64 Кб — для примера и для ровного счета. Хотя, почему нет? Может, там в процессе обновления вывод на экран какой хитрый нужен, с картинками и шрифтами.
А вывод на экран лучше всё ж таки не делать. Захочется завтра экран поменять, или косяк какой вылезет — а загрузчик уже есть, и безболезненной процедуры апгрейда загрузчика нету (которую может сделать пользователь со 100% гарантией получения живого устройства). Минимум функционала (получил прошивку — залил куда следует, и хватит).
Обеспечить шифрование прошивки для исключения клонирования устройства.
Я правильно понял, что под этим понимается шифрование файла с прошивкой на SD-карте, а не шифрование прошивки в устройстве?
habr.com/post/432966/#comment_19491212
Другое дело, что есть специальные люди, которые жизнь потратили на то, чтоб извлекать прошивки(и загрузчики) с устройств с взведенными битами защиты — так что именно для этой архитектуры, это лишь вопрос желания, гугления, и $$$$.
Здесь сначала идет код проверки контрольной суммы, выполняющийся при окончании чтения файла, а потом само чтение. Возможно, так писать не следует, напишите в комментариях что вы об этом думаете.
Вы считали прошивку и проверили CRC, потом вы ещё раз её считываете для записи во флеш. Но никто не может гарантировать, что второй раз вы считали с карточки то же, что и в первый раз, а контрольную сумму вы уже не проверяете. Таким образом мы имеет точно правильную прошивку на SD-карте, а что по факту попало во флеш — неизвестно.
Проблема 2:
Вы храните CRC от расшифрованной прошивки. Строго говоря, это не безопасно. Более безопасно хранить CRC от зашифрованной прошивки. Злоумышленник не должен иметь ни единой крупицы дополнительной информации о зашифрованных данных. Открытый CRC косвенно указывает на версию прошивки как минимум.
Проблема 3:
Целостность прошивки с криптографической точки зрения не обеспечивается вообще.
Можно манипулировать битиками зашифрованной прошивки, пересчитывать CRC, зашивать и смотреть
как на это реагирует ваш девайс. Зная структуру прошивки, где, например, таблицы прерываний, инициализированные данные и т.д. можно много чего наворотить, если очень захотеть.
Можно включить проверку CRC в драйвере SD-карты, у карт это реализовано аппаратно. Или добавить ещё одну проверку.
Модифицировать алгоритм для хранения зашифрованной контрольной суммы не сложно. В этой статье я хотел показать суть, как сделать загрузчик. Толком ничего не нашел, собирал информацию по крупицам пока писал этот код, вот решил скомпоновать все.
Можно включить проверку CRC в драйвере SD-карты
Можно, но вы уже внедрили (уровнем выше) в свой формат CRC. А раз так, то стоит это делать правильно, а иначе зачем ваш код?
… для хранения зашифрованной контрольной суммы не сложноНет смысла зашифровывать CRC. Проще держать CRC от зашифрованных данных. Это даже работать будет быстрее, потому, что не нужно будет ничего расшифровывать на фазе проверки.
P.S. 3 пункт дописал с задержкой, обратите внимание.
А по шифрованию да, уже согласился в предыдущем комментарии, что имеет смысл считать контрольную сумму зашифрованных данных. И изменить алгоритм никакой сложности не представляет, по сути, поменять несколько строк местами.
По 3 пункту — это вы вот сейчас знаете, что контрольная сумма в конце и она не зашифрована, потому, что видите исходник с комментариями. В реальной ситуации никто не знает о том, что контрольная сумма в конце. Она может быть в начале, в середине, после каждой страницы памяти, и вообще в любом месте или в нескольких местах сразу и по разным алгоритмам, всё зависит от фантазии разработчика. Эта статья ведь не руководство к действию как получить 100% защиту, а просто описание метода, который тоже можно доработать.
это вы вот сейчас знаете, что контрольная сумма в конце и она не зашифрована, потому, что видите исходник с комментариямиSecurity through obscurity не работает. Это плохая практика.
В реальной ситуации никто не знает о том, что контрольная сумма в конце.Если известно, что в бинарнике есть контрольная сумма, то поиск её не такая уж и сложная задача. Тем более, что она очень хорошо параллелится.
Она может быть в начале, в середине, после каждой страницы памятиДа пожалуйста, т.е. где-то среди 2048+4 байт есть контрольная сумма. Найти её не так сложно, даже при условии, что полином заранее неизвестен.
Вы пишете о том, что любую защиту можно взломать. Я с вами согласен, даже очень дорогая и запутанная защита компьютерных игр часто взламывается в первые дни после релиза, но вопрос о наличии специалистов и стоимости взлома тут на первом месте.
Но никто не может гарантировать, что второй раз вы считали с карточки то же, что и в первый раз
Считайте это защитой не от взлома, а от дурака-пользователя, который скопировал только половину файла.
Открытый CRC косвенно указывает на версию прошивки как минимум
Тут есть бОльшая проблема — вектор инициализации прибит гвоздём в загрузчике. И, в итоге, на версию прошивки явно указывает её содержимое.
Но вообще, по-хорошему, версию надо на видном месте писать — хоть текстом в зарезервированной (и не загружаемой в устройство) области. Это, возможно, слегка облегчит жизнь взломщику, но гарантированно уберёт кучу проблем у обычного пользователя.
Считайте это защитой не от взлома, а от дурака-пользователя, который скопировал только половину файла.Зачем оправдывать неправильную схему, когда можно сделать нормально, обеспечив корректность файла и на карточке, и в памяти мк без особых усилий? Тем более если конечная цель — гарантированно залить нормальную прошивку в мк.
Тут есть бОльшая проблема — вектор инициализации прибит гвоздём в загрузчике. И, в итоге, на версию прошивки явно указывает её содержимое.В данном конкретном примере это так. Но в теории можно было гвоздём прибитый вектор инициализации криптографически замешать с серийным номером.
Он правда под L4, но я и под L1 переделал для себя, полет нормальный, только медленнее конечно чем на L4. Скоро на остальные платформы напишут.
А на F1 или F0 или не напишут вообще, или он отъест треть памяти сразу. А это тоже хорошие контроллеры, для которых есть применение.
Загрузчик пишет со скоростью 82 KB за 1:24 мин.
Сначала стираются все страницы, далее передаются блоки по 256 байт (страница), после записи и отправки подтверждения передается следующая страница. Снимается блокировка страницы один раз перед записью 256 байт и блокируется в конце. Вся задержка на 306 строке, если ее закоментировать, задержки нет. Эта одна команда (запись 32 bit во flash) блокирует программу на 233,7 ms / (256/4) = 3,7 ms.
«STM32F405: прошить 400кб за 10 секунд или быстрый UART-загрузчик заточенный под USB-UART, размером менее 4 килобайт»
m.habr.com/post/305800
Я не понимаю, как можно достичь такую скорость загрузки при аппаратном ограничении записи во flash.
1. компилятор не понимает что стек изменился (gcc не понимал несколько лет назад)
2. новый стек находится в последнем верхнем слове доступной памяти и после него памяти физически не существует и её чтение ведёт к исключению (а такое часто бывает: начать стек с конца доступной ОЗУ обычная практика)
3. компилятор реализовал void ExecMainFW() с сохранением nonvolatile регистров в стеке при входе в неё и при вызове Jump_To_Application(); их попытается восстановить из стека, т.к. не знает о Jump_To_Application(); ничего.
при этом в пункте 3 произойдёт выход стека за пределы блока памяти.
решение которое делал я в своих загрузчиках для стм32Ф4 и Ф7 и H7 такое:
github.com/Mirn/Boot_F4_fast_uart/blob/master/src/sfu_commands.c#L229
см функция jump_main: я объявил «голую» функцию, т.е. без обработки стека и тд и в неё уже перешёл в основную программу с установкой стека.
Видится такая возможность атаки: записать вместо обновляемого приложения код, выдающий содержимое памяти наружу любым доступным методом, например через тот же USB. В коде загрузчика ключ шифрования хранится в явном виде, что должно позволить расшифровывать файлы обновлений прошивок.
Код, заливаемый во Flash, расшифровывается. Если подсунуть свой код, то во Flash Будет записан мусор и контроллер не запустится.
К тому же я указывал, что эта статья не является руководством к действию для получения 100% защиты, а лишь поясняет суть. Можно добавлять различные проверки и запутывающее поведение, что серьезно усложнит взлом.
А записать штатным образом (через этот загрузчик) какой-то осмысленный код представляется затруднительным…
При RDP = 0хА5 (Level 1) можно записывать в те страницы, которые не закрыты через WRP0..WRP3
поправьте если неправ
RDP=0xA5 — это Level0, защиты нет. Level1 — это RDP != 0xA5.
При установке Level1 «снаружи» (JTAG/SWD/штатный Boot/код в RAM) можно только сделать Mass Erase. «Изнутри» (из своего кода) можно стирать/записывать страницы, на которые не стоит write protect (странная особенность F10x — автоматически ставит защиту от записи на первые страницы флеша при установке зашиты от чтения. Не отключается).
Первоисточник — PM0075 Programming manual STM32F10xxx Flash memory microcontrollers и reference manual.
Все это очень полезно, но, к сожалению, нынче заклонировать почти любой микроконтроллер можно за сумму порядка 2-5к$. Знаю не по наслышке, нечистоплотные китайские заказчики быстро с этим справились, обломав зубы только на цифровой подписи FPGA-чипа
— Единственное, что вспомнилось — OBDII адаптер ELM327, но там, на сколько я помню, сами разработчики забыли включить защиту от чтения памяти.
Какой пример? Мой девайс содержит LPC1343, в котором юник айди и блочное шифрование, и ПЛИС Lattice, подписанная AES-128 ключем. Когда китайцы решили больше нам не платить, они быстренько склонировали микроконтроллер, обрадовались, произвели первую партию изделий и тут их ждал облом, потому что OTP-пассворд в ПЛИС ломать еще не научились… я провел небольшое исследование, и нашел несколько контор, клонирующих любые МК с флэш-памятью. Средний ценник 3к
Сомнение вызывает слово «любые»
Например NEC UPD76F0012GD мне никто и ни за какие деньги не предложил считать (и камень, и изделие на его основе стоят в приборе 20-летней давности)
Я не понял только, для чего и в загрузчике и в основной программе NVIC_SetVectorTable() с одинаковыми параметрами. Если, как написано «Сразу после запуска startup файл все переинициализировал», то в загрузчике этот вызов бесполезен, получается, он же прямо перед передачей управления, фактически, этому startup.s.
Планируется ли перешивать сам загрузчик? У меня такая возможность предполагается и я придумал два варианта: 1) выделение дополнительного места, куда будет записываться временная программа перешивающая загрузчик (как вариант — вместо основной, с последующей её перепрошивкой назад), 2) махинации со скриптом линковщика и расположением функции копирования в RAM через атрибуты (и функций ею используемых). Реализую второй вариант, попутно разбираясь в этих скриптах (тоже не сходу находится полезная информация), вроде получается. Может есть ещё какие-то нормальные способы? Как люди делают?
По перешивке загрузчика — это может сделать и непосредственно основная программа точно таким же способом. Но при отключении питания в процессе прошивки устройство закирпичится совсем. Я бы не стал это делать не установив хотя бы аккумулятор и не удостоверившись в том, что его заряда достаточно.
для чего и в загрузчике и в основной программе NVIC_SetVectorTable()
Потому что стандартный ST'шный SystemInit() перезаписывает NVIC->VTOR на стандартное 0x08000000. Для правильной работы надо подправить адрес или попросту выкинуть оттуда эту строку за ненадобностью — в загрузчике это делать идеологически правильнее.
Загрузчик с шифрованием для STM32