logo
PASOIB

3.2. Шифрование кода программы

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

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

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

Выделяют два вида шифрования кода программы – статическую и динамическую.

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

Реализация простейшей процедуры шифрования кода программы по XOR может выглядеть следующим образом.

LEA SI, beginCrypt;

начало зашифрованного блока

Repeat:

Xor Byte ptr [SI], 077h

INC SI

CMP SI, offset endCrypt

JNA Repeat

beginCrypt

……

endCrypt

Следует отметить, что для зашифрованных блоков программы, дизассемблер выдаст неверные результаты.

Атака на статическую шифровку для приведенного выше примера может осуществляться снятием дамба памяти, заменой зашифрованного текста расшифрованным с помощью HIEW редактора и последующей заменой xor 77 на xor 00.

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

Методы атаки на шифрование кода

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

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

2. Расшифровка кода ведется на достаточно длинном ключе, однако для контроля правильности введенного ключа, считается его CRC, после чего CRC ключа проверяется на соответствие требуемому. В данном случае, злоумышленник находит в программе алгоритм контроля CRC в программе, находит тот CRC, которому должен удовлетворять пароль, после чего пишет процедуру перебора различных ключей на соответствие их CRC. Таких паролей, как правило, получается много, и чем хуже CRC, тем их больше. Проведя расшифровку кода программы на данных паролях, у злоумышленника появляется проблема выбора единственно верного исходного текста среди расшифрованных вариантов (все остальные – неверные). При этом, нет никаких достаточно строгих критериев, позволяющих автоматически отличить ложные варианты. Как правило, злоумышленник должен вводить пароль, запускать программу, она вешается, выходить, вводить пароль и т.д. Однако, злоумышленник может воспользоваться различного рода эвристиками, позволяющими ему сократить варианты перебора. Например, он может попытаться использовать косвенное представление об исходном тексте. Можно по типу данных предугадать вероятность того или иного символа, проверить определенные фрагменты на совпадение со словарем, поискать некоторые закономерности, однако эти эвристики будут работать достаточно медленно, и нет никакой гарантии, что мы не пропустим нужный вариант.

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

В данном случае, злоумышленник может пойти другим путем – атакой шифра по открытому тексту. Если злоумышленник обладает хотя бы частью открытого текста, то он может обратить операцию шифрования и найти ключ (рассматривается случай шифрования путем сложения открытого текста с ключом по модулю 2 – xor). Действительно, если X xor Key = Y, то Y xor Key = X, X xor Y = Key. Если ключ шифрования равен 16 бит, то достаточно знать всего лишь 2 байта исходного текста, чтобы найти ключ и применить его для всего текста.

Инструкции, встречающиеся в исходном тексте мы можем предположить с достаточно большой вероятностью. Весьма вероятно, что в приведенном шифротексте встречается инструкция int 21 (0x21CD), следует поискать также такие последовательности, как CopyRight, OK, и т.д. Всегда встречаются вызовы стандартных библиотек. Такого рода эвристические элементы могут значительно упростить задачу злоумышленнику.

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

Однако, в данном случае злоумышленник может применить «атаку по маске». Суть ее состоит в следующем - пусть нам не известно достаточно длинной строки открытого текста, но мы знаем наверняка много коротких, и с некоторой достоверностью расстояние L между ними.

Алгоритм «атаки по маске» следующий.

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

Возьмем другую известную последовательность S1 и повторим аналогичную операцию. Выберем теперь общие для f(S0) и f(S1) элементы. Вероятнее всего из них и составлен пароль.

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