Информационная безопасность

       

Применение запутывающих преобразований


Существующие методы запутывания и инструменты для запутывания программ используют не единственное запутывающее преобразование, а некоторую их комбинацию. В данном разделе мы рассмотрим некоторые используемые на практике методы запутывания.

В работах Ч. Ванг [23][24] предлагается метод запутывания, и описывается его реализация в инструменте для запутывания программ на языке Си. Предложенный метод запутывания использует преобразование введения "диспетчера" в запутываемую функцию. Номер следующего базового блока вычисляется непосредственно в самом выполняющемся базовом блоке прямым присваиванием переменной, которая хранит номер текущего базового блока. Для того чтобы затруднить статический анализ, номера базовых блоков помещаются в массив, каждый элемент которого индексируется несколькими разными способами. Таким образом, для статического прослеживания порядка выполнения базовых блоков необходимо провести анализ указателей.

В работе [3] предлагается метод запутывания, основанный на следущих запутывающих преобразованиях: каждый базовый блок запутываемой функции разбивается на более мелкие части (т. н. piece) и клонируется один или несколько раз. В каждом фрагменте базового блока переменные локализуются, и для связывания базовых блоков создаются специальные связующие базовые блоки. Далее в каждый фрагмент вводится мёртвый код. Источником мёртвого кода может быть, например, фрагмент другого базового блока той же самой функции или фрагмент базового блока другой функции. Поскольку каждый фрагмент использует свой набор переменных, объединяться они могут безболезненно (при условии отсутствия в программе указателей и вызовов функций с побочным эффектом). Далее из таких комбинированных фрагментов собирается новая функция, в которой для переключения между базовыми блоками используется диспетчер. Диспетчер принимает в качестве параметров номер предыдущего базового блока и набор булевских переменных, которые используются в базовых блоках для вычисления условий перехода, и вычисляет номер следующего блока.
При этом следующий блок может выбираться из нескольких эквивалентных блоков, полученных в результате клонирования. Выражая функцию перехода в виде булевской формулы, можно добиться того, что задача статического анализа диспетчера будет PSPACE-полна. Работа [3] описывает алгоритм запутывания, но не указывает, реализован ли этот алгоритм в какой-либо системе.

Запутыватели для языка Java, например Zelix KlassMaster [25], как правило, используют следующее сочетание преобразований: из .class-файлов удаляется вся отладочная информация, включая имена локальных переменных; классы и методы переименовываются в короткие и семантически неосмысленные имена; граф потока управления запутываемой функции преобразовывается к несводимому графу, чтобы затруднить декомпиляцию обратно в язык Java.

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


Содержание раздела