(на картинке изображен великий математик и криптограф Алан Тьюринг).
Цель
Теоритическое описание вычислительной машины с плавающими кодами операций, для исполнения зашифрованных(и таким образов офбусцированных) программ в защищенном и открытом виде.
Для начала освежим в памяти немного терминов
Шифрова́ние — обратимое преобразование информации в целях сокрытия от неавторизованных лиц, с предоставлением, в это же время, авторизованным пользователям доступа к ней.
Обфуска́ция (от лат. obfuscare — затенять, затемнять; и англ. obfuscate — делать неочевидным, запутанным, сбивать с толку) или запутывание кода — приведение исходного текста или исполняемого кода программы к виду, сохраняющему ее функциональность, но затрудняющему анализ, понимание алгоритмов работы и модификацию при декомпиляции.
Вычислительная машина, счётная машина (далее ВМ) — механизм, электромеханическое или электронное устройство, предназначенное для автоматического выполнения операций по заданному алгоритму.
Код операции, операционный код, опкод — часть машинного языка, называемая инструкцией и определяющая операцию, которая должна быть выполнена процессором.
Warning! Не путать с
Машинный код (платформенно-ориентированный код), машинный язык — система команд (набор кодов операций) конкретной вычислительной машины, которая интерпретируетсянепосредственно процессором или микропрограммами этой вычислительной машины.
При этом
Систе́ма кома́нд (также набо́р команд) — соглашение о предоставляемых архитектурой средствах программирования, а именно: определённых типах данных, инструкций, системы регистров, методов адресации, моделей памяти, способов обработкипрерываний и исключений, методов ввода и вывода.
Отсюда понятно, что машина плавающих опкодов – это машина в которой будут изменятся коды(численные значения) машинных кодов, но не всех машинных кодов. Т.е. некоторые машинные коды(инструкции) будут иметь изменчивый характер, а некоторые нет. Давайте назовем машинные коды, которые не будут изменятся — константными(статическими) кодами, а те, которые будут изменятся — плавающими. Понятно, что плавающими кодами будут все опкоды + еще некоторые управляющие коды, и некоторые управляющие коды будут как плавающими так и статическими.
Так же можно сделать вывод, что вместо привычной таблицы команд в ВМ будет три главных элемента:
- Таблица константных кодов
- Таблица базовых смещений плавающих кодов
- Ключевой, криптостойкий ГПСП аппаратно вшитый в ВМ(возможно несколько переключаемых аппаратных ГПСП). (краткая справка: криптостойкость означает то, что даже зная все предыдущие значения ГПСП невозможно вычеслить следующее значения не зная порождающего ключа)
Задание ГПСП открытым ключом:
В начале исполнения программы первой идет команда инициализации ГПСП неким ключем. Т.к. после каждого такта исполнения на выходе ГПСП меняется значение, то обозначим выход ГПСП, зависимый от времени как G(t), а смещение детерменированной команды как commandShift(someComand). Понятно, что commandShift(someCommand) – константа, т.к. всегда остается одной и той же для определенной команды. Так же пусть команда задания ключа ГПСП константна, обозначим ее staticSeed. Далее пример кода исполняющего конкретную операцию, независимо от значения ГПСП на текущий момент:
staticSeed notSecretValue
G(t) + commandShift(someCommand)
Т.к. мы знаем, ключ ГПСП, то без труда можно вычислить следующее значение и сложив его со смещение, мы получим что G(t) + commandShift(someCommand) является константным для определенного notSecretValue и определенного someCommand. Таким образом можно выполнить любой детерменированный код с помощью команды staticSeed. Так можно решить проблему обработки нелинейного исполнения т.е. Таблица константных кодов может состоять только из одной инструкции задания ключа для ГПСП. Все остальные коды могут быть плавающими.
Понятно, что для компиляции и исполнения кода для такой ВМ необходимо чтобы у ВМ и компилятора (программист пишущий в машинных кодах тоже по сути является компилятором) были одинаковые модели (алгоритмы) ГПСП, и начальные задающие ключи к нему. Недостатками во время компиляции для такой ВМ будут необходимость вычислить некоторое множество значений ГПСП для конкретного ключа и сложить его с базовыми смещениями.
Для исполнения защищенного кода необходимо задавать секретный ключ во время исполнения программы, с помощью например считывания ключевого файла, или ввода с клавиатуры. Т. е. компилятор зная секретный ключ может сгенерировать код, который будет работать некорректно (неопределенно) без знания ключа для ГПСП. Такой код после компиляции будет выглядеть так:
<открытая часть программы для получения секретного ключа(secretKey) с помощью staticSeed-детерменированного кода>
staticSeed secretKey
G(t) + commandShift(some command)
G(t) – будет неопределенным, т.к. зависит от ввода secretKey, которого не будет в скомпилированном машинном коде. Т.е. весь следующий код превратится в невосстановимую абракадабру не зная secretKey, а исполнение его с другим ключем приведет к неопределенным результатам. Преимущество данного подхода в том, что не зная secretKey невозможно отличить данные от кода, и невозможно понять, какой тип кода выполняется(плавающий или статический) и какие конкретно инструкции. Т.е. нахождение команды из сырых данных возможно только зная G(t).
Минусы такого подхода в целом:
- для дебага нужно полностью восстановить состояние машины (знать ключи ГПСП в момент времени и все данные в регистрах)
- вычисление компилятором множества значений ГПСП для ключа(ключей)
- при кодинге на машинных кодах есть очень большая возможность выстрелив себе в колено
- оторвать не только ногу но и все остальные части тела.
Плюсы:
- невозможность декомпиляции программы в защищенном режиме без знания секретного ключа
- сведения машинного кода к равномерному распределению (хотя зависит от ГПСП)
P.S. Согласно закону Шнайера каждый человек может изобрести систему безопасности, которую он был бы не в силах взломать. Поэтому любая обоснованная критика приветствуется.
Автор: StrangerInRed