Friday, August 20, 2010

Обфускаторы, виртуализаторы, конечные автоматы и защита софта

Частенько приходится слышать что-то вроде этого: "Обфускация - ерунда, а вот виртуализация кода - практически неломаемая штука". Встречается еще и что-то вроде такого: "Это не обфускация. Наш метод базируется на сложной задаче декомпозиции большого конечного автомата". И т.д., и т.п. Если взглянуть в вики, то можно прочитать вот что: Обфускация (от лат. obfuscate — затенять, затемнять; и англ. obfuscate — делать неочевидным, запутанным, сбивать с толку) или запутывание кода — приведение исходного текста или исполняемого кода программы к виду, сохраняющему ее функциональность, но затрудняющему анализ, понимание алгоритмов работы и модификацию при декомпиляции. Т.е. и виртуализация, и создание в программе некоторого конечного автомата (что и есть виртуализация), что-то выполняющего, также являются одними из разновидностей этой самой обфускации. Виртуализация кода подразумевает под собой перевод исходного, как правило машинного, кода в код некоторого виртуального процессора с последующим встраиванием интерпретатора этого кода в тело защищаемой программы. Такой подход усложняет анализ защищенного кода, но не делает невозможным взлом программы и исследование логики ее работы. Аналогичное можно сказать и про декомпозицию конечного автомата, т.е. в данном случае про создание соответствия между исходным автоматом (x86 кодом, например) и полученным в результате обфускации автоматом. В теории все звучит достаточно неплохо, однако на практике, имея доступ к утилите, осуществляющей защиту, в подавляющем большинстве случаев, наделав простых сэмплов, можно набить шаблоны и снять защиту.
В последнее время стали популярны стековые ВМ в самом простом исполнении. Каждой ассемблерной инструкции ставится в соответствие некий обработчик, который делает то же самое, что и исходная инструкция вплоть до сохранения регистра флагов. Плюс имеется некий цикл выборки команд и передачи управления обработчикам. Помнится, был такой язык Forth. Так вот, стековые ВМ очень похожи по архитектуре. У Ахо, Сети, Ульмана такая архитектура называется абстрактной стековой ВМ. Не уверен точно, но, возможно, идея использования виртуального процессора пошла со времен, когда люди столкнулись с виртуальной машиной Visual Basic-а. Соблазны использовать такого рода обфускацию следующие:
1. "Эксперты" нараспев говорят, что это круто.
2. Маркетологи нараспев говорят, что п.1.
3. Размер запутуанного кода вырастает не так сильно.
4. Реализация не требует особых математических знаний и навыков, что позволяет справиться с задачей рядовому системному программисту.
5. Сроки реализации прогнозируемы по времени.
6. Результат тоже прогнозируем.
7. Легко накручивать сверху фичи - контроль целостности, несколько циклов выборки, отсутствие циклов выборки, предварительный анализ флагов и создание обработчиков, которые могут их как угодно менять и т.д.
8. Ломать это бывает лениво.
9. Пипл схавает, ибо ничего лучшего за эти деньги на рынке нет.
Мое глубокое убеждение в том, что без качественной обфускации тела самой ВМ ее можно декомпилировать, а следовательно, и взломать. Поэтому обычно тело ВМ чем-нибудь морфят и вообще всячески защищают. От качества морфера зависит очень многое. Большинство морферов работают на уровне одной инструкции, не анализируя графа потока управления в целом (см. п. 9). Зачастую здесь на помощь приходит антиотладка (эвенты, многопоточность и т.д.). Т.е. достаточно старые приемы, никак не обоснованные математически и дающие сомнительную стойкость в сочетании с жуткими тормозами и глюками. Причем, чем больше антиотладки, тем больше этих самых глюков. Замедление 4-5 порядков - это норма для таких ВМ. Часто можно встретить выполнение логических инструкций на базе стрелки Пирса, например, что хорошо конвертируется обратно посредством вбивания шаблона в декомпилятор.
А можно ли создать идеальный обфускатор? Нет, т.к. существует класс подпрограмм, которые, чем ни накрой, не защитишь. Вырожденный случай - программа, распечатывающая сама себя. А можно ли создать хороший обфускатор - такой, чтобы крайне сложно было написать автоматический деобфускатор? Да. Но для этого нужно, как минимум, уметь делать следующее:
1. Отслеживать граф потока данных защищаемой подпрограммы.
2. Распознавать переменные на стеке.
3. Обфусцировать не на уровне одной инструкции, а на уровне всей подпрограммы, а лучше - на уровне программы.
4. Использовать минимальный набор инструкций.
А для того чтобы создать сам механизм обфускации на уровне всей подпрограммы, необходимо неплохо разбираться в математике и криптографии. В частности, методы White-Box криптографии после некоторой модификации вполне подходят и для решения задачи обфускации. Совершенно очевидно, что создание такого обфускатора - более ресурсоемкое занятие. Причем, в целях ограничения размера, код можно и на стековой ВМ интерпретировать - еще уровень защищенности добавится. Т.е. значимость системных программистов в данной работе не отвергает никто. Однозначно можно утверждать, что это работа не для одного человека, а для целой команды. Работу эту делать долго и может встретиться масса подводных камней. Плюс еще есть немаловажный политический момент. К сожалению, очень много специалистов по защите софта таковыми себя только мнят. Любой человек мало-мальски знающий ассемблер, немножко знающий WinAPI и PE-формат, а также умеющий пользоваться IDA и OllyDbg - уже такой специалист. Если он еще и C++ знает, то вообще очень крут :) Вот так, на таком уровне понимания, и пишется подавляющее большинство программных защит. Не удивительно, что и стойкость их невысока, и взламываются они достаточно стандартно. Один такой "эксперт" мне сказал как-то: "Специалисту по защите софта не надо знать математику, ему надо знать что с чем в нужный момент поксорить". О чем можно было говорить дальше? На пути создания хорошего обфускатора стоит еще и вопрос окупаемости, т.к. обфускация в подавляющем большинстве случаев используется для защиты относительно недорогого софта, а протекторы с кучей фич, пусть и в большинстве своем несложно отламываемых, стоят недорого, принося авторам мизерные доходы. Мнение "экспертов" вкупе с непониманием, как извлечь выгоду из столь дорогого в производстве продукта, крайне отрицательно сказываются желании инвесторов полноценно финансировать создание обфускаторов. Таким образом, действительно интересные идейно, но крайне глючные в реализации, продукты создаются энтузиастами и забрасываются на этапе альфа-версий в лучшем случае - либо создаются на инвестиции различного рода секретных ведомств, что сильно ограничивает к ним доступ со стороны простых смертных, да и, в силу ограниченности применения, продукты тоже глючные. В связи с таким положением дел я бы рекомендовал для действительно стойкой защиты софта использовать ключи с загружаемым кодом. Либо, если есть ощущение собственной крутости, не надеяться на протекторы, а писать защиту для своих программ самостоятельно, используя сторонние продукты не более как на вспомогательный инструмент, т.к. производители таких продуктов полагаются, в основном, на п. 9.

Интересное мнение про обфускацию

Наткнулся на subj. Особенно понравилось fuzzy security - как нельзя точно. Вообще говоря, г-н Барак послужил источником вдохновения для целого ряда людей, занимающихся теориями в области обфускации, хотя его доказательство о невозможности обфускации, мягко говоря ... Впрочем, каждый может оценить это самостоятельно.