Научная статья на тему 'АВТОМАТИЧЕСКАЯ ГЕНЕРАЦИЯ ХЭШ-ФУНКЦИЙ ДЛЯ ОБФУСКАЦИИ ПРОГРАММНОГО КОДА'

АВТОМАТИЧЕСКАЯ ГЕНЕРАЦИЯ ХЭШ-ФУНКЦИЙ ДЛЯ ОБФУСКАЦИИ ПРОГРАММНОГО КОДА Текст научной статьи по специальности «Математика»

CC BY
165
31
i Надоели баннеры? Вы всегда можете отключить рекламу.
Область наук
Ключевые слова
ОБФУСКАЦИЯ / ХЭШ-ФУНКЦИЯ / ГЕНЕТИЧЕСКОЕ ПРОГРАММИРОВАНИЕ / ЛАВИННЫЙ ЭФФЕКТ / SMT-РЕШАТЕЛЬ / OBFUSCATION / HASH FUNCTION / GENETIC PROGRAMMING / AVALANCHE EFFECT / SMT SOLVER

Аннотация научной статьи по математике, автор научной работы — Лебедев Роман Константинович

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

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по математике , автор научной работы — Лебедев Роман Константинович

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

AUTOMATIC GENERATION OF HASH FUNCTIONS FOR PROGRAM CODE OBFUSCATION

The specifics of hash function applications in code obfuscation are considered, as well as the disadvantages of currently existing hash functions for this purpose. Considering these specifics and disadvantages, an automatic hash function generation method is proposed, that is based on a genetic programming approach. Methods of measuring the hash function resilience to automated preimage attacks based on SMT solvers usage and random collision resistance are also proposed. Generated functions were evaluated and a method of weak function detection is proposed, that allows to increase the resilience of generated functions to attacks notably.

Текст научной работы на тему «АВТОМАТИЧЕСКАЯ ГЕНЕРАЦИЯ ХЭШ-ФУНКЦИЙ ДЛЯ ОБФУСКАЦИИ ПРОГРАММНОГО КОДА»

2020 Математические основы информатики и программирования №50

МАТЕМАТИЧЕСКИЕ ОСНОВЫ ИНФОРМАТИКИ И ПРОГРАММИРОВАНИЯ

УДК 004.056.5

АВТОМАТИЧЕСКАЯ ГЕНЕРАЦИЯ ХЭШ-ФУНКЦИЙ ДЛЯ ОБФУСКАЦИИ ПРОГРАММНОГО КОДА

Р. К. Лебедев

Новосибирский государственный университет, г. Новосибирск, Россия

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

Ключевые слова: обфускация, хэш-функция, генетическое программирование, лавинный эффект, SMT-решатель.

DOI 10.17223/20710410/50/8

AUTOMATIC GENERATION OF HASH FUNCTIONS FOR PROGRAM CODE OBFUSCATION

R. K. Lebedev Novosibirsk State University, Novosibirsk, Russia E-mail: n0n3m4@gmail.com

The specifics of hash function applications in code obfuscation are considered, as well as the disadvantages of currently existing hash functions for this purpose. Considering these specifics and disadvantages, an automatic hash function generation method is proposed, that is based on a genetic programming approach. Methods of measuring the hash function resilience to automated preimage attacks based on SMT solvers usage and random collision resistance are also proposed. Generated functions were evaluated and a method of weak function detection is proposed, that allows to increase the resilience of generated functions to attacks notably.

Keywords: obfuscation, hash function, genetic programming, avalanche effect, SMT ssolver.

Введение

Обфускация — процесс запутывания кода программ с целью затруднения их анализа. Анализ исполняемого кода программ применяется как потенциальными нарушителями авторского права, так и антивирусными аналитиками, поэтому как методы обфускации, так и методы противодействия ей становятся темой многих научных работ [1, 2]. Одним из наиболее важных свойств обфускации является сохранение функциональности программы: для любых входных данных результат выполнения об-фусцированной и оригинальной программы должны совпадать. От обфусцированной программы обычно требуют близкого к оригинальной программе размера и быстродействия, однако конкретные требования могут варьироваться в широких диапазонах в зависимости от задач [3].

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

Однако широко используемые хэш-функции, такие, как МВБ, 8ИА-1 и 8ИА-2, зачастую могут быть распознаны в машинном коде по используемым в их реализации константам (например, 0хё76аа478 для МБ5). Из-за этого эффективность их использования в обфускации подвергается критике [1]: аналитик сможет узнать функцию и сделать выводы об используемом методе обфускации, даже не разбирая её код. Современные инструменты обратной разработки программ также позволяют распознать популярные функции исключительно по машинному коду, без использования констант [5]. Существуют и методы обнаружения криптографических функций, основанные на динамическом анализе [6].

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

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

Использование таких хэш-функций призвано обеспечить следующие свойства:

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

2) Невозможность использования существующего программного обеспечения для атак полным перебором.

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

Среди существующих результатов по автоматической генерации хэш-функций наибольший интерес представляет работа [8]. Некоторые из описанных в ней подходов

нашли применение и в данном исследовании, хотя авторы [8] преследовали другую цель, в результате чего найденная ими хэш-функция gp-hash оказалась не слишком сложной для ручного анализа, а также тривиально обратимой. Продолжением [8] является работа [9], в которой описано создание блочного шифра ШЬее^аш на основе сети Фейстеля при помощи нелинейных функций, подобных найденным в [8]. При помощи конструкции Миагучи — Пренеля из этого шифра, в свою очередь, была создана криптографическая хэш-функция МРШ-512. Однако в силу использования готовых конструкций она не является сложной для обнаружения и анализа в исполняемом коде, что делает её малоподходящей для нужд обфускации. Кроме того, [8, 9] имеют целью поиск одной лучшей функции, а не семейства, что в значительной степени отличает их от данной работы.

1. Требования к хэш-функциям для обфускации 1.1. Механизм обфускации Рассмотрим хэш-функции, применимые для механизма обфускации, описанного в [4]. Его идея состоит в следующем: значения констант в коде условий заменяются результатами выполнения хэш-функций, благодаря чему восстановление их первоначальных значений становится вычислительно сложной задачей. Пример использования подобного механизма обфускации некоторой функции проверки лицензионного ключа приведён в листингах 1 и 2.

1 int check_serial(char * serial) {

2 if (!strcmp(serial, "S0M3S3CR3TK3Y"))

3 return 1;

4 else

5 return 0;

6 }

Листинг 1. Функция до обфускации

1 int check_serial(char * serial) {

2 // 0x8cd28b8294f34457 == hash("S0M3S3CR3TK3Y")

3 if (hash(serial) == 0x8cd28b8294f34457)

4 return 1;

5 else

6 return 0;

7 }

Листинг 2. Функция после обфускации

1.2. Сравнение с криптографическими хэш-функциями Хотя к криптографическим хэш-функциям предъявляется множество различных требований [10], для целей обфускации не все из них строго обязательны [4]. Рассмотрим основные требования к криптографическим хэш-функциям и их применимость в данной работе:

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

2) Стойкость к поиску второго прообраза — сложность нахождения для данного ш\ такого m2, что f (m1) = f(m2) и m\ = m2. В отличие от стойкости к поиску первого прообраза, данное требование не столь критично для обфускации. Существование таких коллизий неизбежно приведёт к нарушению свойства сохранения функциональности, но не повлияет негативно на стойкость программы к обратной разработке. Заметим, что в контексте сохранения функциональности наиболее интересны случайные коллизии, а не намеренно подобранные в результате криптографической атаки (если речь не идёт об участках кода, связанных с аутентификацией или другой обработкой недоверенного ввода, строго требующей отсутствия коллизий).

3) Стойкость к коллизиям — сложность нахождения таких m1 и m2, что f (m1) = = f (m2 ) и m1 = m2. Из данного требования следует стойкость к поиску второго прообраза, поэтому его также можно считать необязательным для специально выбранных m1 и m2. Однако именно это требование удобно использовать для проверки полученной функции при случайных m1 и m2. Если оно соблюдается, можно утверждать о сохранении функциональности с достаточной для стабильной работы обфусцированной программы точностью.

1.3. Специфические для обфускации требования

В то время как для криптографических хэш-функций наличие понятного и открытого описания является желательным согласно принципу Керхгоффса, в обфускации ценно запутывание любого рода, способное замедлить обратную разработку [11]. Соответственно для целей работы принцип Керхгоффса можно нарушить, не только скрыв от аналитика исходное сообщение, но и сделав саму функцию максимально непрозрачной. Для этого следует потребовать отсутствия закономерностей в её описании, например повторяющихся математических операций. Хорошим примером функции, нарушающей данное условие, является функция gp-hash, представленная в [8], в которой операция циклического сдвига вправо применяется к аргументу 18 раз подряд, что позволяет заменить эту цепочку операций одиночным сдвигом на 18 бит, сильно упростив код функции. Реализация этой функции и её упрощённый вариант представлены в листингах 3 и 4, где ror и ror_18 — операция циклического сдвига вправо на 1 и 18 бит соответственно.

1 int gphash(int h, int m) {

2 return 0x6CF575C5 * ror(ror(ror(ror(ror(ror(ror(ror(ror (ror(ror(ror(ror(ror(ror(ror(ror(ror(0x6CF575C5 * (h + m )))))))))))))))))));

3 }

Листинг 3. Реализация исходного варианта функции gp-hash

1 int gphash(int h, int m) {

2 return 0x6CF575C5 * ror_18(0x6CF575C5 * (h + m));

3 }

Листинг 4. Упрощенная реализация функции gp-hash

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

1) Отсутствие раундовой структуры. Так как с ростом числа раундов пропорционально растёт количество исполняемых инструкций, не имеет смысла использовать множество раундов, если функция способна обеспечить требуемые свойства за один. Наряду с этим наличие повторяющихся раундов нарушает требование максимальной запутанности кода.

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

1.4. Итоговые требования к хэш-функциям для обфускации

Объединив требования к хэш-функциям, применимым в обфускации, поставленные в п. 1.2 и 1.3, получим следующий список:

1) стойкость к поиску первого прообраза — необратимость хэш-функции;

2) стойкость к случайным коллизиям — малая вероятность совпадения значений хэш-функции для случайных сообщений;

3) запутанность описания функции — отсутствие в описании хэш-функции закономерностей, позволяющих упростить её исследование;

4) высокая скорость работы — скорость работы, сравнимая с распространёнными некриптографическими хэш-функциями.

1.5. Количественные оценки

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

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

Ух, у (ё(х, у) = 1 ^ (х), Г(у)) « N/2).

Здесь ^х,у) —расстояние Хэмминга между х и у; Г(х) — функция, обладающая лавинным эффектом; N — размер её результата в битах.

Существует и более строгое условие — строгий лавинный критерий [12], при соблюдении которого автоматически соблюдается и условие лавинного эффекта. Он звучит так: при изменении одного бита входного сообщения каждый бит выходного сообщения должен измениться с вероятностью 1/2; его можно представить в другом виде: общее количество изменившихся бит должно соответствовать биномиальному распределению с параметрами 1/2):

Ух,у (а(х,у) = 1 ^ а(г(х),г(у)) - вN 1/2)). (1)

Для данной работы выбрано именно это условие как более строгое. Соответствие полученного распределения образцовому оценивалось при помощи критерия согласия Пирсона.

Отсутствие закономерностей в описании хэш-функций, в свою очередь, связано с понятием колмогоровской сложности — минимально возможным алгоритмом, гене-

рирующим описание хэш-функции. Так как колмогоровская сложность является невычислимой, в качестве её замены можно использовать алгоритмы сжатия общего назначения, например Deflate, как предложено в [13]. Чем больший размер имеет функция в сжатом виде, тем больше оценка минимальной длины её описания, а значит, тем сложнее она для упрощения и анализа.

2. Алгоритм поиска хэш-функций для обфускации 2.1. Структура генерируемых хэш-функций

В качестве структуры хэш-функций выбран упрощённый вариант структуры Меркла— Дамгора: ввиду того, что стойкость к намеренному поиску коллизий не является обязательной для целей обфускации, этап с дополнением сообщения его длиной можно пропустить. Тогда функция будет выглядеть следующим образом:

Ho = IV, Н = F (Яг-!, тг), i = 1, 2,... (2)

Здесь тг — части исходного сообщения M одинакового размера; F(h,m) —функция сжатия; IV — некоторое начальное значение функции. Значение Hn для сообщения из n частей является результатом хэш-функции от всего сообщения M. В рамках данной работы длина тг и Нг в битах считается равной.

Таким образом, задача поиска хэш-функций сводится к задаче поиска функций сжатия F(h,m).

2.2. Генетическое программирование

Цель работы состоит в автоматической генерации функций, поэтому используемый алгоритм должен быть способен осуществлять поиск в пространстве всех возможных функций. Одним из немногих подходов, пригодных для решения задач подобного рода, является подход генетического программирования [14]. Обычно алгоритмы генетического программирования оперируют функциями, представленными в виде деревьев, где внутренние узлы являются операциями, а листья — операндами (или терминалами). На каждом шаге поиска решения к некоторой популяции функций применяются генетические операторы мутации и скрещивания, после чего в соответствии с требованиями, сформулированными в виде оптимизируемой функции приспособленности, формируется следующее поколение. После некоторого количества итераций поиск завершается и лучшая функция популяции выбирается решением задачи.

В качестве программной реализации выбрана библиотека DEAP для языка Python, содержащая готовые реализации описанного подхода и требующая только задания требований конкретной задачи [15].

2.3. Н а б о р т е р м и н а л о в и о п е р а ц и й

В соответствии со структурой хэш-функций (2) терминалами являются значение функции h, полученное на предыдущем этапе, и m — текущий блок хэшируемого сообщения. В качестве операций выбраны операции, которые быстро выполняются на современных процессорах и являются достаточно простыми, с целью соблюдения предъявленного в п. 1.3 требования к производительности.

В результате выбраны следующие операции:

1) or(x, y), and(x, y), xor(x, y) — побитовые операции ИЛИ, И и исключающее ИЛИ;

2) add(x, y), mul(x, y) —беззнаковые сложение и умножение;

3) not(x)—побитовое отрицание;

4) ror_1(x), ror_half(x)—операции циклического сдвига вправо на 1 бит и на половину размера переменной: хотя вторую операцию можно выразить через

первую, многократный повтор операции противоречит условию максимальной запутанности из п. 1.3;

5) dynror(x, у)—циклический сдвиг х вправо на у бит; хотя эта операция и выглядит менее тривиально, чем предыдущие, на архитектуре х86, широко используемой на существующих компьютерах, она кодируется одной инструкцией ЯОЯ г/ш8, СЬ.

2.4. Функция приспособленности

Искомая функция Г (к, т) применяется к двум аргументам, поэтому условие (1) уточнено следующим образом: потребуем соответствия функции строгому лавинному критерию для обеих переменных одновременно (как если бы к и т были одним аргументом х двойного размера для функции Г(х)).

Для количественного измерения соответствия условию (1) последовательно осуществляются следующие шаги:

1) генерация пары ко, то при помощи генератора псевдослучайных чисел;

2) подсчёт значения Г0 = Г(к0,т0);

3) изменение одного случайного бита в одной из переменных ко или то, новая пара значений после этой операции обозначается как к1, т1;

4) подсчёт значения Г1 = Г(к1,т1);

5) подсчёт и запоминание числа различающихся в Г0 и Г1 бит d(F0, Г1).

После проведения достаточного числа итераций полученная выборка d(Г0, Г1) проверяется на соответствие распределению В(^ 1/2) при помощи критерия согласия Пирсона: получаем значение х2:

2 = £ (О* - Е*)2

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

i=0

Ei

Здесь Oi — наблюдаемое количество итераций, где изменилось i бит, а Ei — ожидаемое количество для распределения B(N, 1/2). В данной работе использован генератор случайных чисел MT19937 (вихрь Мерсенна), а число итераций, обеспечивающее достаточную точность оценки лавинного эффекта, было экспериментально установлено равным 1024 (оно должно быть значительно больше числа бит функции).

Для оценки запутанности кода полученной функции необходимо подвергнуть его сжатию. Для этого сначала необходимо преобразовать дерево функции F(h, m) в линейное представление, что можно сделать средствами библиотеки DEAP. Затем каждый элемент (операция или терминал) заменяется численным представлением — индексом данной операции или терминала, в результате чего получается список чисел. Так как общее количество операций и терминалов не превышает 28, каждый элемент занимает один байт, а значит, список можно тривиально преобразовать в байтовую строку. Полученная строка подвергается сжатию при помощи алгоритма Deflate, а длина сжатых данных является характеристикой S функции F(h,m).

Для обеспечения выполнения обоих условий из п. 1.5 значение х2 должно быть подвергнуто минимизации, а S — максимизации. Поэтому возникает вопрос объединения данных характеристик в одну функцию, оптимизируемую при помощи генетического программирования. Поскольку условие строгого лавинного критерия менее тривиально, решено отдать ему приоритет и только при достижении им некоторого целевого значения x2gt обеспечивать отсутствие в функции закономерностей.

Так как S намного меньше х2 для условий данной работы, итоговая функция приспособленности, подвергающаяся минимизации, имеет следующий вид:

Fopt = тах(х2 - Х^ 0) - S.

В качестве значения xigt можно взять характеристику х2 любой современной криптографической хэш-функции, стойкость которой к атакам поиска первого прообраза не вызывает опасений. В данной работе использована функция SHAKE256 из семейства SHA-3, удобная тем, что позволяет получить результат произвольной длины [16].

2.5. О п и с а н и е а л г о р и т м а

Приведём разработанный метод в алгоритме 1. Здесь measureSAC и measure-Complexity — функции оценки лавинного эффекта и запутанности кода (см. п. 2.4); createPopulation и generateOffspring — функции создания популяции и генерации потомков, предоставленные библиотекой генетического программирования DEAP. Функция check служит для дополнительной проверки функций на применимость в обфус-кации, принцип её работы описан далее в п. 4.4.

Алгоритм 1. Алгоритм поиска хэш-функций для обфускации

1: params := параметры генетического программирования;

2: scoretgt := целевое значение функции;

3: Xtgt := measureSAC(SHAKE256);

4: population := createPopulation (params).

5: Повторять

6: population := generateOffspring (population, params).

7: Для всех ind E population

8: ind .score := max( measureSAC (ind) — x2gt, 0) — measureComplexity (ind).

9: score min := min (ind .score).

ind Gpopulation

10: Пока scoremin ^ scoretgt

11: indbest := arg min (ind.score).

ind Gpopulation

12: Если check (ind best), то 13: Вывести indbest.

3. Проверка применимости полученных хэш-функций в обфускации

3.1. М е т о д а в т о м а т и ч е с к о г о а н а л и з а н а о б р а т и м о с т ь В программном обеспечении для осуществления деобфускации активно применяются подходы символьного исполнения и SMT-решатели [17, 18]. Использующие их инструменты angr и S2E способны автоматически генерировать входные данные, необходимые для выполнения различных ветвей условий программы, а значит, эти инструменты могут быть использованы и для атак на механизм обфускации, описанный в п. 1.1. По этой причине крайне важна проверка полученных функций на устойчивость к SMT-решателям. Проверка хэш-функций осуществлялась при помощи следующих инструментов:

1) Microsoft Z3 (версия 4.8.7) — наиболее важный решатель для целей данной работы, потому что именно он используется в инструменте автоматического анализа кода angr по умолчанию [17, 19];

2) CVC4 (версия 1.8)—решатель, который также поддерживается инструментом angr [20];

3) Yices2 (версия 2.6.2)—победитель трека QF_BV (application) в соревнованиях SMT-COMP 2018 [21];

4) Boolector (версия 3.2.1)—победитель трека QF_ABV в соревнованиях SMT-COMP 2018 и SMT-COMP 2019 [22].

Основой предлагаемого метода проверки хэш-функций является сравнение времени поиска m для определённых H = F(h,m) и h при помощи SMT-решателей и полного перебора: если первое из времён больше (или решение вовсе не найдено), можно говорить об устойчивости функции к автоматическому анализу, так как SMT-решатели не способны ускорить процесс поиска.

Соответственно для проверки функции на обратимость достаточно осуществить следующие операции:

1) найти Tbf — худшее возможное время атаки полным перебором. Это значение соответствует суммарному времени подсчёта F(h, m) для всех возможных m при фиксированном h;

2) найти Tsmt —время, которое лучший из SMT-решателей затратит на решение задачи нахождения m по значению H = F(h, m) для известного h;

3) сравнить Tsmt и Tbf и сделать вывод об устойчивости функции, если Tsmt > Tbf.

Для реализации программы перебора использовался язык Си в сочетании с компилятором GCC версии 10.1 с уровнем оптимизации O3. Перебор осуществлялся в од-нопоточном режиме.

Время анализа при помощи SMT-решателей измерялось путём их параллельного запуска на разных ядрах процессора. Время, через которое первый из SMT-решателей справлялся с задачей, являлось искомым Tsmt. Длительность работы SMT-решателей сложно спрогнозировать, а время экспериментов не может быть неограниченным, поэтому максимальное время их работы ограничивалось некоторым Tsmtiim, по достижении которого задача объявлялась неразрешимой за допустимое время.

3.2. Метод анализа устойчивости к случайным коллизиям

Традиционным способом анализа устойчивости к случайным коллизиям является использование парадокса дней рождения: при хэшировании случайных значений идеальной хэш-функцией длины N бит через « 2n/2 операций будет обнаружена коллизия. Данный способ исследован Д. Кнутом в [23], в результате чего установлено выражение для среднего количества операций до первой коллизии: Cideal = 1 + Q(2N). Функция Q(x), в свою очередь, изучена в работе [24], где для неё предложено следующее приближение:

ч Гпх 1 1 ПП 4

Q(x) «J----1--W----+ ...

J V 2 3 12 V 2х 135х

Именно это приближение использовано в данной работе.

Для тестируемых функций среднее количество хэширований до коллизии Cbday может отличаться в меньшую сторону от Cideai в силу их неидеальности, например в случае, если реальная область значений хэш-функции меньше чем {0,... , 2N — 1}. Оценить количество потерянных бит области значений Niost можно при помощи выражения Cbday = 1 + Q(2N-Nl-)

Предложим следующее условие устойчивости к случайным коллизиям: если для хэш-функции Nlost мало в сравнении с N, то она является достаточно устойчивой

к случайным коллизиям. Метод анализа устойчивости к случайным коллизиям состоит из следующих шагов:

1) поиск Смау — количества хэширований до первой коллизии. Для этого необходимо генерировать значения Н хэш-функции от случайных сообщений до первого совпадения с одним из ранее полученных значений, число выполненных итераций будет искомым значением. Здесь Н = Г(Г(IV, т1),г2) в соответствии со структурой хэш-функций (2), где г1 и г2 — случайные значения; IV — некоторое постоянное значение. Для увеличения точности следует провести несколько экспериментов, усредняя Сьаау;

2) подсчёт с использованием выражения Сьаау = 1 + Q(2N). Осуществляется численно, так как Q(x) в любом случае является приближением;

3) сравнение N1031 и N: если N1031 ^ N, то функция устойчива к случайным коллизиям.

Заметим, что в данном методе хэшируется сообщение из двух блоков (г1 и г2), а не из одного. Это связано с тем, что размер аргумента т совпадает с размером функции Г(к,т), а значит, вероятность повтора случайных т достаточно высока, чтобы повлиять на итоговое Сьаау: она столь же высока, как и вероятность равенства выходных значений идеальной хэш-функции. Нас же интересуют только коллизии, где /(т1) = /(т2), но т1 = т2, поэтому необходимо хэшировать сообщения длины, как минимум вдвое большей, чем длина значения функции: тогда вероятность равенства исходных сообщений становится пренебрежимой.

4. Экспериментальная проверка хэш-функций 4.1. Условия экспериментов

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

Время выполнения итоговой функции определяется общим количеством операций, поэтому решено не вносить ограничений на высоту дерева функции в процессе генетического программирования, а использовать ограничение на общее число узлов. С целью обеспечения производительности, близкой к существующим некриптографическим хэш-функциям, это значение выбрано равным 32. Тогда для 32-битных функций для хэширования одного байта сообщения потребуется не более 8 инструкций (учитывая использованный в п. 2.3 набор операций), для сравнения — широко распространённой функции ЕМУ требуется не меньше трёх, без учёта более частых условных переходов [25].

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

Итоговые параметры, использованные для генетического программирования, приведены в табл. 1. Для экспериментов применялся компьютер с ОС Ubuntu Linux 18.04, процессором Intel Core i7-5820K (4 ГГц) и 32 ГБ ОЗУ.

Таблица 1 Параметры генетического программирования

Параметр Значение

Размер популяции Вероятность скрещивания Вероятность мутации Максимальное число узлов Набор операций Набор терминалов Функция приспособленности Целевое значение функции Отбор Эволюционная стратегия 200 0,9 0,1 32 or, and, xor, add, mul, not, ror_1, ror_half, dynror h, m max(x2 - Xt2gt, 0) - S -30 Турнирный отбор размера 3 (1+1)-стратегия

4.2. Результаты автоматического анализа При помощи генетического программирования было сгенерировано 256 функций, подвергнутых проверке. Среднее наихудшее время полного перебора для сгенерированных функций оказалось равным Tbf = 6,65 с. Так как SMT-решатели могут выполнять исследуемую функцию медленнее, чем оптимизированная программа на языке С, было решено взять максимальное время их работы равным Tsmtiim = 180 с. Наибольший интерес с практической точки зрения имеют времена решений меньшие, чем Tf поэтому значение Tsmtnm может быть выбрано приблизительно.

С учётом данных значений осуществлена проверка при помощи SMT-решателей, результаты которой приведены в табл. 2 и на рис. 1. Можно видеть, что, несмотря на ограниченное пространство поиска, в 159 случаях SMT-решатели не смогли найти ответ за время Tsmtiim. Количество функций, в которых время решения Tsmt оказалось меньше Tf, составило 53. Для этих функций можно считать, что алгоритм поиска хэш-функций совершенно не справился с задачей.

Таблица 2 Результаты проверки 32-битных функций при помощи SMT-решателей

Категория Количество

Функции, для которых Тат < Ты 53

Функции, для которых Тт < Т8тШт 97

Функции, для которых Тт > Т^Нт 159

Таким образом, в 62 % случаев алгоритм смог сгенерировать 32-битные функции, которые не поддаются анализу при помощи БМТ-решателей за допустимое время, причём в 17% оставшихся случаев время решения оказалось больше, чем худшее время полного перебора. Проанализировав гистограмму на рис. 1, заметим, что наибольшее количество решений происходит за небольшое время, причём за пределами 120 с количество решений сократилось до нуля. Если совместить поиск функций с их быстрой проверкой в течение времени Тм, можно повысить долю неразрешимых за время Т8т))ит функций до 78 %.

50-

40-

х о

30-

20-

10-

0 20 40 60 80 100 120 140 160 180

T~smt. С

Рис. 1. Гистограмма количества 32-битных функций, для которых SMT-решатели нашли решение за указанное время, без учёта случаев Tsmtl > Tsmtliim

4.3. Устойчивость к случайным коллизиям Поскольку устойчивость к случайным коллизиям является второстепенным свойством после устойчивости к автоматическому анализу, проверке подвергались только те функции, которые не были обращены при помощи SMT-решателей за время меньшее Т^. За счёт небольшой области значений функций описанная в п. 3.2 проверка может быть выполнена с достаточной для экспериментов производительностью. Для каждой функции измерялось среднее значение Cbday за 512 попыток, после чего для него рассчитывалось приблизительное значение iViost.

Для 203 проверенных функций среднее значение Cbda.y составило 78080, наименьшее равно 63641. Отсюда среднее число потерянных бит iViost = 0,15 и наихудшее — 0,74. Таким образом, в наихудшем случае хэш-функция имеет не больше коллизий, чем идеальная хэш-функция с длиной значения 31 бит.

4.4. Функции большей разрядности Хотя многие 32-битные функции оказались устойчивы к анализу при помощи SMT-решателей, они уязвимы к переборным атакам (которые атакугций может провести на обычном процессоре в силу малого пространства поиска), вследствие чего было решено проанализировать функции с длинами значений, практически применимыми в обфускации: 64 и 128 бит. Для таких функций использование полного перебора затруднительно, поэтому в отличие от сценария, описанного в п. 3.1, в этих экспериментах имеет значение не сравнение Ты и Tsmtl, а принципиальная достижимость решения за какое бы то ни было разумное время.

Из соображений продолжительности эксперимента значение TSmtiim оставлено прежним (180с), для каждой размерности сгенерировано по 256 функций. Результаты проверки для 64- и 128-битных функций представлены на рис. 2 и 3 соответственно. Явление, наблюдавшееся для 32-битных функций, здесь ещё более выражено: для 64-битных функций максимальное время решения, меньшее Tsmtiim, составило 17 с, а для 128-битных — 8 с. Таким образом, можно отсеивать крайне слабые функции, запуская сразу после их генерации дополнительную проверку, заключающуюся в анализе при помощи SMT-решателей в течение 17 и 8 с соответственно: если решение в течение этого времени найдено, функцию можно считать слабой. Этот способ позволяет повы-

сить долю неразрешимых за время TSmtiim функций до 100 % и может быть реализован в функции check алгоритма 1.

40-

хзон е

о m

ti 20-

10-

0 20 40 60 80 100 120 140 160 180

T"smt. С

Рис. 2. Гистограмма количества 64-битных функций, для которых SMT-решатели нашли решение за указанное время, без учёта случаев Tsmt > Tsmtiim

40

30-

20-

о 10-

0 20 40 60 80 100 120 140 160 180

T"smt. С

Рис. 3. Гистограмма количества 128-битных функций, для которых SMT-решатели нашли решение за указанное время, без учёта случаев Tsmt > Tsmtiim

Ввиду того, что может возникнуть вопрос адекватности выбора Tsmtiim, было решено проверить несколько случайных функций, устойчивых к анализу за время Tsmtiim, в течение намного большего времени. Для этого было выбрано по 16 соответствующих 64- и 128-битных функций, каждая из них подверглась анализу при помощи SMT-решателей в течение 4 ч. В результате эксперимента решение не было найдено ни для одной из функций, что позволяет утверждать, что данные функции являются устойчивыми к атакам с практической точки зрения.

Заключение

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

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

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

ЛИТЕРАТУРА

1. Banescu S., Collberg C. S., Ganesh V., et al. Code obfuscation against symbolic execution attacks // Proc. 32nd Ann. Conf. Computer Security Appl. 2016. P. 189-200.

2. UdupaS., Debray S., and Madou M. Deobfuscation: Reverse Engineering Obfuscated Code. 12th Working Conf. WCRE'05. 2005. 10 p.

3. Junod P., Rinaldini J., Wehrli J., and Michielin J. Obfuscator-LLVM — software protection for the masses // IEEE/ACM 1st Intern. Workshop Software Protection. 2015. P. 3-9.

4. Sharif M., Lanzi A, Giffin J., and Lee W. Impeding malware analysis using conditional code obfuscation // Proc. NDSS. San Diego, California, USA, 2008.

5. https://www.hex-rays.com/products/ida/lumina/ — IDA: Lumina server. 2020.

6. Xu D., Ming J., and Wu D. Cryptographic function detection in obfuscated binaries via bit-precise symbolic loop mapping // IEEE Symp. Security Privacy (SP). Los Alamitos, CA, USA, 2017. P. 921-937.

7. Qiu W., Gong Z., Guo Y., et al. GPU-based high performance password recovery technique for hash functions // J. Inform. Sci. Eng. 2016. V.32. No. 1. P. 97-112.

8. Estebanez C., Hernandez-Castro J., Ribagorda A., and Isasi P. Finding state-of-the-art non-cryptographic hashes with genetic programming // LNCS. 2006. V. 4193. P. 818-827.

9. Tapiador J., Hernandez-Castro J., Peris-Lopez P., and Ribagorda A. Automated design of cryptographic hash schemes by evolving highly-nonlinear functions // J. Inform. Sci. Eng. 2008. V. 24. No. 5. P. 1485-1504.

10. Menezes A, van Oorschot P., and Vanstone S. Handbook of Applied Cryptography. CRC Press, 1996. 661 p.

11. Collberg C.S. and Thomborson C. Watermarking, tamper-proofing, and obfuscation — tools for software protection // IEEE Trans. Software Eng. 2002. V.28. No. 8. P. 735-746.

12. Webster A. F. and Tavares S. E. On the design of S-boxes // LNCS. 1985. V. 218. P. 523-534.

13. Cilibrasi R. and Vitanyi P. M. B. Clustering by compression // IEEE Trans. Inform. Theory. 2005. V. 51. No. 4. P. 1523-1545.

14. Koza J. R. Genetic programming as a means for programming computers by natural selection // Stat. Comput. 1994. V.4. No. 2. P. 87-112.

15. Fortin F. A., De Rainville F. M., Gardner M. A. G., et al. DEAP: Evolutionary algorithms made easy // J. Machine Learning Res. 2012. V. 13. No. 1. P. 2171-2175.

16. Dworkin M. J. SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions. National Institute of Standards and Technology, 2015.

17. Shoshitaishvili Y., Wang R., Salls C., et al. SOK: (State of) The art of war: Offensive techniques in binary analysis // IEEE Symp. Security Privacy (SP). 2016. P. 138-157.

18. Chipounov V., Kuznetsov V., and Candea G. S2E: A platform for in-vivo multi-path analysis of software systems // ACM SIGPLAN Notices. 2011. V.46. No.3. P. 265-278.

19. De Moura L. and Bjorner N. Z3: An efficient SMT solver // LNCS. 2008. V. 4963. P. 337-340.

20. Barrett C., Conway C. L., Deters M., et al. CVC4 // Intern. Conf. Comput. Aided Verification. 2011. P. 171-177.

21. Dutertre B. Yices 2.2 // Intern. Conf. Comput. Aided Verification. 2014. P. 737-744.

22. Brummayer R. and Biere A. Boolector: An efficient SMT solver for bit-vectors and arrays // LNCS. 2009. V. 5505. P. 174-177.

23. Knuth D. E. The Art of Computer Programming. V. 3: Sorting and Searching. 2nd Ed. Addison-Wesley Professional, 1998. 800 p.

24. Flajolet P., Grabner P. J., Kirschenhofer P., and Prodinger H. On Ramanujan's Q-function // J. Computat. Appl. Math. 1995. V. 58. No. 1. P. 103-116.

25. http://www.isthe.com/chongo/tech/comp/fnv/ — FNV Hash. 1991.

REFERENCES

1. Banescu S., Collberg C. S., Ganesh V., et al. Code obfuscation against symbolic execution attacks. Proc. 32nd Ann. Conf. Computer Security Appl., 2016, pp. 189-200.

2. UdupaS., Debray S., and Madou M. Deobfuscation: Reverse Engineering Obfuscated Code. 12th Working Conf. WCRE'05, 2005. 10 p.

3. Junod P., Rinaldini J., Wehrli J., and Michielin J. Obfuscator-LLVM — software protection for the masses. IEEE/ACM 1st Intern. Workshop Software Protection, 2015, pp. 3-9.

4. Sharif M., Lanzi A, Giffin J., and Lee W. Impeding malware analysis using conditional code obfuscation. Proc. NDSS. San Diego, California, USA, 2008.

5. https://www.hex-rays.com/products/ida/lumina/ — IDA: Lumina server. 2020.

6. Xu D., Ming J., and Wu D. Cryptographic function detection in obfuscated binaries via bit-precise symbolic loop mapping. IEEE Symp. Security Privacy (SP), Los Alamitos, CA, USA, 2017, pp.921-937.

7. Qiu W., Gong Z., Guo Y., et al. GPU-based high performance password recovery technique for hash functions. J. Inform. Sci. Eng., 2016, vol.32, no. 1, pp. 97-112.

8. Estebanez C., Hernandez-Castro J., Ribagorda A., and Isasi P. Finding state-of-the-art non-cryptographic hashes with genetic programming. LNCS, 2006, vol.4193, pp. 818-827.

9. Tapiador J., Hernandez-Castro J., Peris-Lopez P., and Ribagorda A. Automated design of cryptographic hash schemes by evolving highly-nonlinear functions. J. Inform. Sci. Eng., 2008, vol.24, no. 5, pp. 1485-1504.

10. Menezes A, van Oorschot P., and Vanstone S. Handbook of Applied Cryptography. CRC Press, 1996. 661 p.

11. Collberg C.S. and Thomborson C. Watermarking, tamper-proofing, and obfuscation — tools for software protection. IEEE Trans. Software Eng., 2002, vol.28, no. 8, pp. 735-746.

12. Webster A. F. and Tavares S. E. On the design of S-boxes. LNCS, 1985, vol. 218, pp. 523-534.

13. Cilibrasi R. and Vitanyi P. M. B. Clustering by compression. IEEE Trans. Inform. Theory, 2005, vol.51, no. 4, pp. 1523-1545.

14. Koza J. R. Genetic programming as a means for programming computers by natural selection. Stat. Comput., 1994, vol.4, no. 2, pp. 87-112.

15. Fortin F. A., De Rainville F. M., Gardner M. A. G., et al. DEAP: Evolutionary algorithms made easy. J. Machine Learning Res., 2012, vol. 13, no. 1, pp. 2171-2175.

16. Dworkin M. J. SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions. National Institute of Standards and Technology, 2015.

17. Shoshitaishvili Y., Wang R., Salls C., et al. SOK: (State of) The art of war: Offensive techniques in binary analysis. IEEE Symp. Security Privacy (SP), 2016, pp. 138-157.

18. Chipounov V., Kuznetsov V., and Candea G. S2E: A platform for in-vivo multi-path analysis of software systems. ACM SIGPLAN Notices, 2011, vol.46, no. 3, pp.265-278.

19. De Moura L. and Bjorner N. Z3: An efficient SMT solver. LNCS, 2008, vol. 4963, pp. 337-340.

20. Barrett C., Conway C. L., Deters M., et al. CVC4. Intern. Conf. Comput. Aided Verification, 2011, pp.171-177.

21. Dutertre B. Yices 2.2. Intern. Conf. Comput. Aided Verification, 2014, pp. 737-744.

22. Brummayer R. and Biere A. Boolector: An efficient SMT solver for bit-vectors and arrays. LNCS, 2009, vol. 5505, pp. 174-177.

23. Knuth D. E. The Art of Computer Programming. vol. 3: Sorting and Searching. 2nd Ed. Addison-Wesley Professional, 1998. 800 p.

24. FlajoletP., Grabner P. J., Kirschenhofer P., and Prodinger H. On Ramanujan's Q-function. J. Computat. Appl. Math., 1995, vol.58, no. 1, pp. 103-116.

25. http://www.isthe.com/chongo/tech/comp/fnv/ — FNV Hash. 1991.

i Надоели баннеры? Вы всегда можете отключить рекламу.