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

Введение

ARM - это доминирующая архитектура для чипов смартфонов (Apple, Samsung, Qualcomm выпускают чипы на ARM-е), поэтому сегодня рассматривается она.

Про ARM Limited

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

Вместе с преимуществами архитектуры чипы получают и ее уязвимости. Распространенный пример - ROP, или return-oriented programming. Смысл атаки заключается в получении злоумышленником контроля над стеком программы (call stack), чтобы перехватить поток управления (control flow) и заставить исполняться определенную последовательность машинных команд, которая уже присутствует в памяти устройства. Например, можно активировать NFC устройства и похитить деньги с виртуальной карты владельца.

Отсылка к stackoverflow

ROP - продвинутая версия атаки под названием SS - stack smashing (разрушение, разбивка стека). В SS, получая доступ к стеку программы, злоумышленник записывает последовательность байтов так, чтобы затереть или изменить адрес возврата (return address) функции. По сути - разновидность stack overflow =)

Как же защититься от подобного вмешательства? Сущетсвует несколько способов, например защита стека канарейками (canaries protection) или размещение стека в невозможной для исполнения области памяти (nonexecutable stack). И еще один - проверка подлинности указателя/адреса возврата (pointer authentication) перед переходом на него. На последнем остановимся подробнее.

Защита канарейками

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

Стек в неисполняемой области

Даже если злоумышленник получит доступ к стеку и запишет нужную ему последовательность команд, они не будут исполнены. Иначе он должен либо отключить защиту, либо найти не защищенную область памяти и разместить код там.

Pointer Authentication

Основная идея данной техники заключена в том, что фактическое адресное пространство в 64-битных архитектурах меньше 64-бит. В значениях адреса есть неиспользуемые биты, которые можно использовать для размещения кода проверки подлинности указателя (PAC - pointer authentication code).

Хранение адреса в 64-битной архитектуре
Хранение адреса в 64-битной архитектуре

Можно вставлять его в каждый указатель, который хотим защитить, прежде чем записывать в память, и проверять его целостность перед использованием. Злоумышленник, который хочет изменить защищенный указатель, должен будет найти или угадать правильный PAC, чтобы иметь возможность управлять потоком программы.

Указатель с расположенным PAC
Указатель с расположенным PAC

Однако не все адреса имеют одно и тоже назначение. Есть адреса (указатели) перехода на команды, адреса для доступа в память и адреса инструкций общего назначения. Поэтому важен контекст (обычно это SP - stack pointer, указатель текущего расположения "каретки" стека) программы, в которой используется данный адрес. Контекст полезен для выделения различных типов указателей, используемых с одним и тем же ключом. Контекст указывается в качестве дополнительного аргумента ("настройки") вместе с адресом при вычислении и проверке PAC.

Проверка подлинности адреса (далее - PA) состоит из трех основных компонентов: инструкций, криптографии и управления ключами.

Инструкции

Две основные операции, которые нужны для PA: вычисление и приписывание PAC к указателю (PAC instruction) и проверка PAC и восстановление значения указателя (AUT instruction).

инструкция PAC
инструкция PAC
инструкция AUT
инструкция AUT

Если проверка завершается неудачно во время инструкции AUT, процессор заменяет PAC определенным шаблоном, который делает значение указателя невалидным адресом. Фактическое обнаружение ошибки происходит из-за исключения, которое возникает при разыменование невалидного указателя. Обработчик исключений может различать исключение невалидного адреса и неудачную проверку подлинности, проверяя шаблон, который инструкция AUT вставляет при ошибке.

Криптография

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

Выходом стал шифр QARMA - семейство легких "настраиваемых" (tweakable) блочных шифров. Настраиваемый означает, что перестановка, вычисляемая шифром в открытом тексте, определяется секретным ключом и дополнительной "настройкой" (tweak), выбираемой пользователем.

Общая схема работы шифра QARMA. P - plaintext, или то, что шифруем; C - ciphertext, шифротекст; T - tweak, или "настройка"; w0, w1, k0, k1 - получаются из K - master key, ключа, специальным алгоритмом.
Общая схема работы шифра QARMA. P - plaintext, или то, что шифруем; C - ciphertext, шифротекст; T - tweak, или "настройка"; w0, w1, k0, k1 - получаются из K - master key, ключа, специальным алгоритмом.

QARMA - это конструкция Эвена Мансура из трех раундов (циклов), в которой перестановки параметризуются основным ключом (core key), а "смешивания" (mixings) между раундами получаются из "отбеленного" (whitening) ключа. Первая и третья перестановки функционально являются обратными друг другу и дополнительно параметризуются с помощью "настройки". Центральная перестановка спроектирована так, чтобы ее можно было легко инвертировать с помощью простого преобразования ключа.

Про QARMA

Шифр заслуживает отдельной статьи, поэтому в конце будет ссылка на исходный документ с полным объяснением математики и операций.

При использовании для вычисления PAC параметрами QARMA являются указатель (как P) и контекст (как T). Само же значение PAC - усеченная форма вывода QARMA.

Менеджмент ключей

QARMA использует 128-битные ключи. Спецификация проверки подлинности указателя определяет пять ключей (два для указателей на инструкции, два для указателей на данные и один для инструкции общего назначения). Ключи хранятся во внутренних регистрах и недоступны для EL0 (пользовательский режим).

Для каждого ключа добавляются управляющие биты, которые определяют поведение исключения. Это позволяет генерировать или переключать ключи по требованию.

Преимущества

Теперь посмотрим, какие преимущества дает PA перед другими способами защиты.

У защиты методом расположения стека в неисполняемую область памяти есть проблемы: во-первых, указатели все равно должны быть проверены или защищены перед размещением в стек, а во-вторых это не сработает для динамических объектов, таких как адреса возврата в стек или динамически подключаемые библиотеки. PA же справится с этой проблемой: мы независим от типа объекта, так как PAC считается для любого указателя.

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

Заключение

Pointer Authentication - отличный метод защиты, который уже повсеместно используется. Например Apple поддерживает его начиная c Apple A11 (Iphone XS). Однако не стоит забывать, что любую защиту рано или поздно обойдут, а поэтому стоит использовать несколько методов вместе.

Список литературы

Qualcomm, "Pointer Authentication on ARMv8.3"

Mark Rutland, "ARMv8.3 Pointer Authentication"

Roberto Avanzi, "The QARMA Block Cipher Family"

Hans Liljestrand, Thomas Nyman, Kui Wang, Carlos Chinea Perez, Jan-Erik Ekberg, N. Asokan, "PAC it up: Towards Pointer Integrity using ARM Pointer Authentication"

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


  1. MinimumLaw
    01.12.2021 10:34

    Спасибо. В принципе все хорошо, но вот бы еще примеры кода... А то вроде все прозрачно, но... Чего-то не хватает.


    1. lorc
      01.12.2021 10:44
      +1

      Ну на самой первой картинке в принципе и есть пример. В прологе функции подписываем указатель командой pacia, в эпилоге - проверяем что никто его не сломал командой autia.


      1. Arv1k Автор
        01.12.2021 10:55
        +2

        Я решил, что примеры кода добавлять я не буду, так как там по сути всего 4 команды. Но да, на первой картинке пример есть) Однако я не рассмотрел случай совместимости с 32-битным системами, надо добавить


        1. MinimumLaw
          01.12.2021 11:26
          +1

          Спасибо. Ожидалось что-то более глобальное что-ли.

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