Я пытаюсь написать доказательство концепции на C, которое демонстрирует выполнение кода из буфера памяти в стеке на ARM Cortex-M3. Это будет полезно для демонстрации того, что правильное использование ARM MPU может предотвратить такую атаку. Я подумал, что быстрый и грязный способ поместить код в стек - это скопировать его из обычной функции, а затем использовать goto, чтобы перейти к нему следующим образом:
static void loopit(void)
{
printf("loopit\n");
while (1);
}
void attack(void)
{
uint8_t buffer[64] __attribute__((aligned(4)));
memcpy(buffer, loopit, sizeof(buffer));
goto *((void *) (int) buffer);
}
Я ожидал, что когда я вызову функцию атаки, она скопирует код в стек, перейдет к нему, распечатает сообщение и войдет в бесконечный цикл. Однако вместо этого я получаю исключение со следующими значениями в регистрах ошибок:
HFSR = 0x40000000
CFSR = 0x00020000
PSR = 0x60000000
Похоже, что это бит INVSTATE в UFSR, который указывает на «незаконное использование EPSR», который, как я прочитал, обычно связан с попыткой инструкции BX перейти на адрес с LSB, установленным на 0, который процессор интерпретирует как функцию с в нем код не-Thumb, но процессоры Cortex-M допускают только код Thumb. Я вижу, что memcpy получает нечетный адрес для функции loopit
, поскольку я предполагаю, что компилятор выполняет операцию ИЛИ реальный адрес памяти с 1
. Итак, исправление, которое я думаю, заключалось бы в том, чтобы переписать мою функцию атаки следующим образом:
void attack(void)
{
uint8_t buffer[64] __attribute__((aligned(4)));
memcpy(buffer, ((int) loopit) & ~1, sizeof(buffer));
goto *((void *) ((int) buffer) | 1);
}
Однако после этого я получаю другое исключение с регистрами ошибок:
HFSR = 0x40000000
CFSR = 0x00080000
PSR = 0x81000000
В этом нет никакого смысла: установленный бит 3 UFSR означает, что «процессор попытался получить доступ к сопроцессору». Глядя на ПК, на этот раз кажется, что прыжок прошел успешно, и это здорово, но затем что-то пошло не так, и ЦП, похоже, выполняет странные инструкции и не входит в бесконечный цикл. Я попытался отключить прерывания перед goto и также закомментировать printf, но безуспешно. Есть какие-нибудь подсказки, что происходит не так и как заставить это работать?
(int)
в(void *) ((int) buffer)
, в зависимости от размераint
и размера указателей, этоint
преобразование может изменить адрес. Будет лиgoto *((void *) buffer);
работать лучше? - person Breaking not so bad   schedule 11.11.2017buffer
вообще не используется! - person A.K.   schedule 11.11.2017