M-o-Vfuscator2, безумный компилятор

в 7:05, , рубрики: M/o/Vfuscator2, Блог компании VDSina.ru, Компиляторы, ненормальное программирование

M-o-Vfuscator2, безумный компилятор - 1

Однажды один умный чувак (Кристофер Домас) читал статью другого умного чувака (Стивена Долана) про удивительную особенность архитектуры x86. Стивен ругал её за избыточность и утверждал, что набор инструкций можно сократить до одной лишь mov, потому что она Тюринг-полная. Если бы Стивен не был таким умным, в его словах можно было бы усомниться, но у Кристофера загорелись глаза: проработав двадцать лет с x86, он не слышал ни о чём подобном, и ему страшно захотелось написать компилятор, который бы переводил весь код в наборы одних лишь mov-инструкций. Так родились M/o/Vfuscator и M/o/Vfuscator2, наглядно иллюстрирующие ненормальное программирование.

Идея

Небольшое отступление про инструкцию mov: это самая простая инструкция в ассемблере, перемещающая значение из памяти в регистры или из регистров в память. Как может штука, перекладывающая байты из одного места в другое, оказаться Тьюринг-полной? Ну, если вам правда интересно, почитайте оригинальную статью Стивена с доказательством. Если не очень, перейдём сразу к выводу:

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

Собственно, на последних словах Кристофер и загорается этой идеей и попутно соображает, что через подобную компиляцию можно нехило так обфусцировать код — сам чёрт ногу сломит в этих бесконечных mov'ax! Для сравнения, обычный, «читаемый» ассемблер:

M-o-Vfuscator2, безумный компилятор - 2

и та же самая программа на мувах:

M-o-Vfuscator2, безумный компилятор - 3

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

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

  mov [x], 0
  mov [y], 1
  mov R, [x]

Если x == y, то в третьей строчке, где считывается значение по адресу x, окажется не ноль, а перезаписавшая его единица.
Если x != y, то считается ноль, так как единица лежит по другому адресу.

Код выполняется без ветвлений
Согласно идее Стивена, правильно написанный блок кода может либо что-то делать, либо не делать, в зависимости (только!) от исходного состояния системы. То есть ветвление отсутствует как класс, если абсолютно все инструкции исполняются последовательно.

M-o-Vfuscator2, безумный компилятор - 4

Ограничения
Для выполнения требуется одна инструкция jmp start (в конце списка mov'ов) для перевода программы в начало; для остановки нужен заведомо нерабочий адрес памяти.

Дальше Крис добавляет свои требования:

  • Использовать примитивные операции машины Тьюринга как основу для высокоуровневой логики
  • Работать надо с реальными данными, не с абстрактными символами (эксперимент Стивена всё-таки академичен, далёк от реального мира)
  • Должны быть реализованы основные операции: условные ветвления, арифметика, логика, циклы и так далее

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

Реализация

Первая версия компилятора была написана для брейнфака, для ощущения максимального абсурда и тщетности жизни реверсера, но, конечно, она осталась ужасно далека от реальных примеров и задач. Поэтому Крис спустя пару лет ВНЕЗАПНО выпустил M/o/Vfuscator2, рабочий mov-компилятор для С. Впечатляющий апгрейд, не правда ли?

M-o-Vfuscator2, безумный компилятор - 5

M-o-Vfuscator2, безумный компилятор - 6

Заявлена относительно легкая адаптация компилятора под другие платформы и языки, но всё же создавался он именно для x86, и с ним связан ворох особенностей и ограничений:

  • Для дробных чисел используется самописный эмулятор плавающей точки, из-за размера поставляется в трёх версиях: softfloat32.o для float, softfloat64.o для float и double, и softfloatfull для полной поддержки стандарта IEEE
  • Так как арифметика строится на таблицах поиска, таблицы символов могут занимать огромное количество места, и их, возможно, придётся обрезать флагом -s
  • Компилятор работает строго на C89 из-за использования LCC в качестве фронтенда. Нельзя использовать bool, for (int ...), и другие фишки C99
  • Код с нестрогой типизацией или небезопасными конвертациями, скорее всего, не скомпилируется — тоже из-за LCC
  • Функция, использующая внешние библиотеки, без прототипа лишает компилятор информации о необходимости и моменте подключения этих библиотек, что почти гарантированно повесит приложение
  • Вызовы внешних функций (printf и т.д.) через указатели функций еще не реализованы
  • Для подключения библиотек, скомпилированных не на mov, могут потребоваться другие инструкции. Полностью избавиться от них можно, перекомпилировав в mov все ресурсы

Заключение

Несмотря на всю крутизну проекта и Кристофера, нужно понимать, что такая обфускация скорее игрушка, чем реальный рабочий инструмент. И всё же, учитывая возможность прикрутить другие фронтенды и архитектуры открывает для M/o/Vfuscator больше возможностей, чем мог бы получить другой безумный ассемблерный проект.

Информацию по установке и использованию можно найти на гитхабе.


На правах рекламы

Эпично! Недорогие серверы на базе новейших процессоров AMD EPYC для размещения проектов любой сложности, от корпоративных сетей и игровых проектов до лендингов и VPN.

Присоединяйтесь к нашему чату в Telegram.

Автор: Mikhail

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js