Статья расскажет на примере алгоритма AES как можно найти конструкции алгоритма шифрования и доказать, что приложение действительно использует именно этот алгоритм для преобразования данных.
Общие данные
Почему реверсить крипту сложно? Если рассматривать обратную разработку как дисциплину, в которой анализ данных подразделяется на различные виды, то можно выделить следующие группы:
обратная разработка приложений из операционной системы;
обратная разработка прошивок устройств;
обратная разработка формата файла.
Считается, что наиболее простой вариант обратной разработки - обратная разработка приложений из ОС. В этом виде обратной разработки руководствуются простыми правилами:
Анализ приложения=чтение документации по API операционной системы.
Анализ приложения проще провести на уже запущенном приложении (есть возможность писать лог вызова API функций).
До 90% алгоритма таких приложений можно восстановить не вникая в промежуточные преобразования, а просто читать последовательность вызовов API функций. При таком подходе очень просто выявлять авторские
конструкции, который использует программист. Потому что остальной код будет практически полностью обитать в библиотеках операционной системы.
Давайте рассмотрим такой пример, приложение для операционной системы windows должно произвести процедуру шифрования файла с использованием популярного алгоритма шифрования. Давайте для примера выберем AES. Какие API библиотеки и функции для этого может предложить операционная система:
-
Стандартная библиотека для криптографии:
старые версии ОС до 10й - wincrypt.h
новые версии ОС от 10 - bcrypt.h
-
Функции для работы с AES wincrypt.h:
CryptAcquireContext -> должна вызываться с параметрами, которые описывают алгоритм AES
CryptGenKey
CryptEncrypt
CryptReleaseContext Для bcrypt.h:
BCryptOpenAlgorithmProvider -> должна вызываться с параметрами, которые описывают алгоритм AES
BCryptGetProperty
BCryptGenerateSymmetricKey
BCryptEncrypt
По сути на этом шах и мат, больше ничего не нужно знать, чтобы понять где будет вызываться процедура шифрования. То есть чтобы дальше получить необходимые данные от приложения или заставить его выдать ключ необходимо просто поставить точку останова в отладчике на нужно функции (определяется задачей исследования) и все данные теперь доступны. В итоге профит.
Всё бы это было так просто, если бы не одно НО - операционные системы позволяют приложениям приносить с собой библиотеки, которые приложение будет использовать для части своего функционала. Той части, которая будет выполнять что-то действительно полезное - считывать информацию из проприетарного формата файла, шифровать данные и т.д. и в этом случае API функции операционной системы будут вызываться только в случае, если приложению необходимо получить доступ к ресурсам ОС или устройству. Именно в этом варианте анализ приложения перестает быть такой простой задачей и нужно будет погружаться в особенности того как приложение работает с данными и что именно делает.
Чтобы делать это успешно нужно представлять что именно мы ищем. В нашей статье будем разбираться именно с криптографическими алгоритмами, но аналогичные рассуждения можно производить и по отношению к анализу любого другого алгоритма.
И так шаги для проведения реконструкции алгоритма:
выявить область применения;
выяснить примитивы для обработки данных алгоритмом;
выяснить какие наиболее популярные константы, конструкции кода используются для описания примитивов.
В качестве примера попробуем разобраться с AES симметричным алгоритмом криптографии.
Пример сбора данных
Область применения
Симметричные алгоритмы используются для преобразования большого количества данных, примитивы, которые они используют достаточно эффективно можно имплементировать на современном железе. Поэтому эти алгоритмы используются в разных режимах и являются основными рабочими лошадками если нужно очень много информации быстро преобразовать в нечитаемый вид.
Соответственно отсюда область применения можно вычислить достаточно просто:
алгоритма может применяться для шифрования файлов в несколько гигибайт объема;
алгоритм может использоваться для так называемого поточного шифрования. Очень часто это шифрование работает при сетевых соединениях.
Примитивы для обработки данных
Алгоритм использует достаточно сложный математический аппарат. Успех его анализа в приложении может зависеть от того как хорошо аналитик понимает этот математический аппарат. Так как на устройствах скорее всего используемые операции для преобразования данных могут оптимизироваться и заменяться наиболее эффективными аналогами. Это может затруднить анализ.
Официальная документация алгоритма находится тут.
Основная работа алгоритма AES начинается с того, что алгоритм будет разбивать данные порциями по 16 байт. Затем будет проводиться процедура шифрования. В зависимости от того какой режим работы и какой длины ключ был выбран, алгоритм будет проводить различной степени сложности преобразования. Чтобы было проще обрабатывать информацию, алгоритм создает так называемую таблицу и работает с её строками и столбцами.
Основные действия при преобразовании данных:
Замена байт по заложенному в алгоритм массиву.
Перемешивание строк при преобразовании.
Перемешивание столбцов при преобразовании.
Добавление к данным блоков данных из ключа.
С этим набором данных можно теперь поискать примеры имплементаций алгоритма.
Популярные константы и кодовые конструкции
Алгоритм не может работать самостоятельно, генерируя все данные налету. Так происходит в силу того, что алгоритм должен иметь заданную стойкость для защиты данных, поэтому в такие алгоритмы как AES записывается таблица замены, которая участвует в примере действий 1, которую описали выше. Найти эту таблицу достаточно просто. В документации она указана последовательностью чисел, соответственно в приложениях это будет массив шестнадцатеричных чисел. Ниже примедена часть этих чисел:
0x63 0x7C 0x77 0x7B 0xF2 0x6B 0x6F 0xC5 0x30 0x01 0x67 0x2B 0xFE 0xD7 0xAB 0x76
0xCA 0x82 0xC9 0x7D 0xFA 0x59 0x47 0xF0 0xAD 0xD4 0xA2 0xAF 0x9C 0xA4 0x72 0xC0
0xB7 0xFD 0x93 0x26 0x36 0x3F 0xF7 0xCC 0x34 0xA5 0xE5 0xF1 0x71 0xD8 0x31 0x15
....
Как видно из последовательности, числа записаны так же в таблицу шириной в 16 байт, для удобства поиска нужных индексов.
Из всего выше перечисленного у нас есть мощный артефакт, который можно использовать для поиска AES алгоритма шифрования в любом коде (если не позаботились сокрытием этих данных). Это данные таблицы замен. Кстати, уже существуют скрипты, которые позволяют обнаруживать самые популярные константы алгоритмов шифрования. Можно использовать например вот этот.
Для этого этапа, пожалуй, лучше всего использовать представление файла в наиболее низкоуровневом варианте, то есть в ассемблерном листинге. Ниже будут примеры на псевдокоде, который очень схож с синтаксисом ассемблера для intel платформы.
С разной степенью оптимизации код может отличаться, но основная суть будет одна и та же. Ниже приведены маски кода, которые получились от приложений, которые были скомпилированы без оптимизации:
Алгоритм создания раундового ключа:
mov register, memory[source]
mov memory[source], register
...
...
add register, 4
cmp register, 0x10
...
...
xor register, memory[source]
xor register, memory[source]
int register
add register, 4
xor register, memory[source]
xor register, memory[source]
...
Шаблон обработки алгоритма для перемешивания колонок или строк:
xor register, register
call routine
xor register, register
mov register, register
xor register, register
xor register, register
xor register, register
xor register, register
...
Таким образом для идентификации алгоритма у нас есть:
Набор констант для идентификации.
Шаблон кода для идентификации примитивов алгоритма шифрования.
Взяв эти данные можно искать алгоритмы криптографии, которые будут имплементироваться приложением самостоятельно согласно документации, либо обнаруживать собственные модифицированные версии.
В заключение хочу пригласить всех на бесплатный урок курса Reverse Engineering, где рассмотрим проблемы, возникающие при восстановлении таблицы импорта, почему её нужно восстанавливать при снятии дампов памяти и на практике напишем свою программку на С для автоматизации процесса.