Visual Basic, .NET, ASP, VBScript
 

   
 
Описание для автора не найдено
 
     
   
 

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

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

Вот тут на помощь приходит обфускация.

Результат.

Вот пример исходного метода (перед обфускацией):

	private void CalcPayroll(SpecialList employeeGroup) 
		{ 
		   while(employeeGroup.HasMore()) 
		   { 
		     employee = employeeGroup.GetNext(true); 
		     employee.UpdateSalary(); DistributeCheck(employee); 
		   } 
		}
				

 А вот результат (после):

	private void _1(_1 _2) 
		{ 
		  while(_2._1()) 
		  { 
		    _1 = _2._1(true); 
		    _1._1(); 
		    _1(_1); 
		   } 
		} 

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

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

Что делает обфускатор:

  1.  Анализирует метаданные, так как не все члены сборки он может обфусцировать. Например обфускатору не стоит заменять имена конструкторов типа – это может быть чревато. Хотя в некоторых случаях это бывает возможно. Также иногда невозможна обфускация виртуальных или абстрактных методов.
  2. Список членов сборки для обфускации готов. Обфускатор присваивает им новые имена, базируясь на определенном алгоритме. Одни обфускаторы присваивают имена такой же длины что и были но лишенные прежнего смысла, другие базируются на нумерации всех членов и обфусцируют тх соответствии номеру члена сборки, третьи – базируясь на токен (token) члена сборки – уникальный идентификатор члена сборки в MSIL, четвертые стараются минимизировать длину имени и почаще использовать одно и то же имя среди членов сборки (в dotfuscator это называется overload induction)– это дает максимальный эффект обфускации (представьте себе что у вашего класса все методы и поля названы как «1»).
  3. После этого производится запись данных обратно в сборку или генерация новой сборки – все это в руках авторов обфускаторов, ее оптимизация – так как многие из обфускаторов способны удалить ненужную информацию из сборки (дебаг-информация, неиспользуемые методы, поля, классы). Вуа-ля – сборка готова. Фактически сборка после обфускации отличается от исходной одной деталью – модифицированным разделом строк (String Heap или #Strings)– одним из пяти разделов метаданных, это итог символьной обфускации, когда обфускатор не модифицирует тела методов. Может быть модифицирован еще один раздел (User Strings или #US) – но пока не встречал таких обфускаторов. Если интересно, могу как-нибудь рассказать о структуре метаданных и PE-файла в .Net сборках. Открываем в ildasm или Reflector или в другом навигаторе по сборкам и оцениваем результат. Некоторые обфускаторы имеют дополнительные фичи: запутывание namespaces (изменение принадлежности различных классов определенным namespace), шифрование строковых и графических ресурсов, контроль обфускации на базе специальных атрибутов, которыми помечаются члены классов в исходном коде, некоторые стараются заигрывать с MSIL-кодом методов сборки. Наука не дремлет, изыскивая новые возможности вам помочь и предложить немного больше чем предлагают (или предполагают :) ) конкуренты.

 

Плюсы и минусы

Плюсы:

  1. Обфускаторы делают дизассемблированный код тяжелым для изучения, превращая IsLicensed() в x().
  2. Некоторые обфускаторы используют баги ILDASM для защиты от дизассемблинга в нем (Salamander).
  3. Некоторые обфускаторы даже конвертируют код в native код, делая бесполезным дизассемблинг (Salamander).
  4. Некоторые обфускаторы шифруют и пакуют ваш exe и иже с ним referenced сборки в один exe-файл, так что размер проги может уменьшиться 2-4 раза и не поддается дизассемблингу (Thinstall).

Минусы

  1. Продукт остается дизассемблируемым.
  2. Собрать сборку после дизассемблинга не составит труда.
  3. IL код – доступный для чтения и понимания по сравнению c ассемблерным.
  4. «Защита» обфускаторов, которые используют баги ILDASM будут бессильны перед дизассемблерами других разработчиков.
  5. Защита обфускаторов, которые используют шифрование символьной части метаданных, строковых и бинарных ресурсов мешает пользователям продукта, отлаживать и тестировать свои продукты. Кроме этого – это риск, так как некоторые символьные данные используются в Reflection – для получения типа (GetType(“MyType”)), или загрузки ресурса (GetManifestResourceStream(“MyResource”)).
  6. Зачастую обфускаторы имеют кучу настроек, непонятных или сложных для понимания обычному юзеру. Неинформированность юзера может привести к тому что его обфусцированная прога будет работать не так как хотелось и иногда приводить к крэшу, да еще и не отлавливаемому отладчиком.
  7. Цена – порядок цен – от 40 до 1500 долларов за программу. И цена может ничего не говорить о качестве обфускации. Может так статься что 40-долларовый обфускатор защитит ваш куда лучше чем более дорогой.
  8. Насчет 4-его плюса. Защищенная таким образом прога «жестко привязана» к используемому .Net Framework, и сервис-пак установленный вами, «порушит» корректность работы защищенной программы. Да и такая защита возможна только для Intel-процессоров.
  9. Насчет 5-го плюса. Да действительно защита сильна и лишена недостатков 4-го плюса. Продукт загружает из ресурса нужные сборки в память и managed exe файл и передает ему управление, занимаясь только разруливанием проблем со сборками, типами, ресурсами (через AssemblyResolve, TypeResolve, ResourceResolve). Но – не все сборки нужны сразу, загрузка их требует дешифровки и распаковки – дополнительного времени и нагрузки на процессор. Не все производят exe-файлы. А Thinstall будет работать только с exe, так как dll-сборки уже не имеют как раньше процессорного DllMain, из которой это было возможно производить. Но – взломать ее проблем также не составляет труда. Есть такая программа ProcDump – она может продампить запущенный процесс и соответственно легко получить расшифрованными и распакованными защищенный exe и referenced сборки. Thinstall будет иметь проблемы с загрузкой в память managed C++ сборки.
  10. Некоторые обфускаторы создают замкнутую систему обфусцированных сборок, где необфусцированными остаются сборки сторонних производителей и MS-вские. Но и здесь все далеко от совершенства – обфускатор не знает как используются ресурсы сборки, поэтому может привести вашу прогу к нерабочему состоянию. Хотя в данном случае качество обфускации становится идеальным – ни к чему не подкопаться. Опять же – это возможно только для exe-прог. Если вам необходимо защищать библиотеку классов, то вам необходимо будет оставлять необфусцированными публичные члены классов и публичные классы – отсюда крэкер может начать свою работу.

Обфускаторы и их возможности:

RemoteSoft Slamander Obfuscator -

Имеет навороченный навигатор, позволяющий лазать не только по метаданным, но и по структуре PE-файла, анализировать ресурсы, бинарные данные. Богатые возможности для анализа метаданных. Нет анализа callgraph методов. Есть свой дизассемблер, но дизассемблирует сразу весь класс, не меньше - приходится ждать. Может использоваться как редактор сорцов. Неплохой символьный обфускатор, декларируемая защита от дизассемблера не работает - у меня спокойно открывался метод, обфусцированный им. Не работает с проектами и с наборами сборок. Навигатор (explorer) имеет плагинную архитектуру что позволяет подключать модули от Salamander к нему. Применяют overload induction - т.е максимально короткие и часто применяемы названия для членов сборки. Также предлагают Protector - который превращает ваши методы в native embedded code - не дизассемблируются, но зависимы от текущей версии .Net Framework. Попробовать не дают :)) Декларируемая оптимизация сборок не была произведена (работает ли на самом деле?). Написан в native code.

Preemptive Solutions Dotfuscator - Community Edition этого обфускатора распространяется с VS .Net 2003. Бедный интерфейс (Дельфи?). обфусцирует как и все, трудно настраиваемый, трудно понимаемый, но интегрирован в VS как addin. Не стал устанавливать, от греха подальше. Работает с телами методов запутывая control flow, т.е как раз всяческие for и while. Почему именно dotfuscator распространяется с VS - вопрос не ко мне. Работает с проектами через интеграцию состудией. Возможно другие версии его покруче - но Community Edition мне не понравилась.

9Rays.Net ILObfuscator - новая версия 2.5 разительно отличается от предыдущей 2.0. Очень много качественных изменений. Во-первых появился GUI. Что многим облегчает задачу работы со сборками. ILOGUI - это и навигатор по сборкам, дизассемблер, система работы с проектом обфускации, в ней можно просматривать и экстрагировать ресурсы, содержащиеся в сборке. Чем отличается от других? Managed code - С# и managed С++, работа с набором сборок, а значит и кросс-обфускация, позволяющая достичь максимального уровня обфускации, неплохой и понятный GUI c VS IDE-like MDI интерфейсом, с ILO поставляется SDK, позволяющим вам создать свою собственную систему обфускации, удобная работа со свойствами проекта, есть коллекции замен и исключений, гибкие возможности по запутыванию - скажем фича по запутыванию namespaces (можно выбрать один из варантов), также предлагаются на выбор варианты наименования обфусцированных классов. Есть функция оптимизации сборки после обфускации, правда только для managed сборок, без embedded native code. ILO самообфусцирован - вы можете убедиться в качестве его обфускации. Из заявленных на следующую версию фичей - поддержка solutions  - т.е наборов проектов, генерация карт обфускации и импорт их при обфускации проектов, эти фичи уже позволят фиксировать результат обфускации и использовать его в следующей обфускации проекта и уменьшать время тестирования сборки после обфускации и максимально обфусцировать проект. Кроме этого обещано функцию оптимизации дополнить фичей удаления неиспользуемых членов сборок и вставить функцию анализа проекта для выявления оных, дополнить навигацию закладками и history. 

Lesser Software LSW IL-Obfuscator - простая и быстрая прога, которая для многих подойдет. Немного настроек, все понятно и просто. Берет как Il-файлы, так и скомпилированные сборки. Правда не все - иногда выдает какую-то странную ошибку и скопилированную сборку не подымает. Такое впечатление что он ее сначал дизассемблирует. Не работает с наборами сборок, но поддерживает замены и исключения из обфускации. Native code. Но за $40.

Thinstall (теперь уже тоже от Lesser Software) - эта штуковина как раз работает только с exe -файлами. Создает самоустанавливающийся exe-файл, со всеми файлами на борту, закрывая от дизассемблирования сборки. Такой файл не без усилий но вскрывается, не стоит обманываться. Как признаются сами производители - лучше всего использовать вместе с обфускатором.

Wise Owl Demeanor - не юзал, не дают. Много чего может - и удаляет ненужную информацию из метаданных, шифрует строковые переменные, работает с мультимодульными сборками (редко используемая вещь)  и интегрируется с VS.Net. Но стоит немеряно $1250. Оценить сей шедевр не дают.

Так с чего же начать?

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

Не стоит путать проектирование с программированием. Какие факторы необходимо учитывать. Обфускатор не Господь Бог, всего не может, поэтому стоит пойти его возможностям навстречу. Типичной обфускацией является символьная (учитывая все плюсы и минусы упомянутые выше)– когда обфускатор только и всего изменяет названия типов, полей, методов, свойств и событий на бессмысленные. Скажем тип Obfuscator переименовывается в 0, а его метод Run() – тоже в 0, а параметры методов просто перенумеруются – 0,1,2,3,4. После подобной обфускации теряется логическая связь между классами, дизассемблированной код – трудночитаем.

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

К слову сказать кроме символьной обфускации есть еще обфускация алгоритмов методов – когда простейшее умножение I*3 может быть представлено более сложным алгоритмом, например – I*((1+1/2)*2) или запутаны while и for. Но мы будем говорить только о символьной обфускации, применительно к обфускации .Net сборок.

В дальнейшем обфускацию я буду обзывать О!, для краткости. Редко можно встретить полностью замкнутые, автономные системы, поэтому типичным случаем будет являться частичная О!. Хотя идеальной задачей для более качественной О! будет являться как раз написание максимальной замкнутой системы сборок. Вот вам и первый фактор.

Рекомендации по подготовке проекта(продукта) к обфускации:

  1. Для усложнения дизассемблирования вы можете использовать подмену типов (которую не всегда возможно реализовать из-за sealed типов) Имеется ввиду такой приемчик – некий системный тип SomeType наследуется в новом типе AnotherType, который подходит для O! (скажем, он лежит в пределах видимости вашей сборки, его можно назначить как internal) и вы его спокойно обфусцируете. На выходе мы получаем испоьзование SomeNamespace.0 вместо известного System.SomeType. Конечно можно установить что AnotherType – это простой наследник от SomeType, но на это уйдет время, не так ли? А нам и нужно тратить время злоумышленника покусившегося на ваш код.
  2. Если вы планируете использовать некоторые типы как публичные но хотели бы их максимально защитить, стоит их поместить "защитную скорлупу" наследования. Некий тип public SomeType можно превратить в два класса : internal _SomeType (underground class), который несет всю реализацию класса, кроме публичных свойств, необходимых для сериализации и для использования во внешнем для сборки коде, и public SomeType, который будет наследоваться от _ SomeType (front class), но нести внешнюю нагрузку – иметь публичные свойства, необходимые для сериализации, конверсии, использования во внешнем для сборки коде. Таким образом у вас будет еще возможность воспользоваться приемчиком 1. для порождения класса SomeType- подобных типов но наследуемых от SomeType.
  3. Внимательнее с атрибутами. Не стоит забывать об использовании атрибутов в вашем коде. Многие из них достаточно тесно помогают взаимодействовать среде .Net с вашими классами. Например атрибут TypeConverterAttribute – им вы привязываете к вашему классу класс конвертера SomeConverter. Не каждый обфускатор «знает» об этом – и поэтому стоит уберечь класс конвертера от обфускации или проверить как обфускатор работает с атрибутами. Иначе связь, установленная между двумя классами посредством атрибута может быть разрушена.
  4. Если ваш класс идет под нож обфускации, стоит также задуматься о механизме его сериализации. Скажем класс- наследник Form сериализует себя таким образом что если обфускатор изменил имя его типа SomeForm на 0, то возникнет проблема при инициализации десериализации такого класса – он просто не сможет найти ресурс 0.resources, так как сериализовался в в SomeForm.resources.
  5. Используйте static string обьявления вместо const string – это затруднит поиск инициализации этого поля (в метаданных оба обьявления будут представлены как поля).
  6. Если вы имеете список строк, которые у вас представлены как список строковых констант, лучше список строк обьявите/ опишите как строковый массив, а в константах храните индекс к необходимой строке в строковом массиве.
  7. Если вы хотите защитить некий алгоритм от лишнего просмотра – отдайте его выполнение нескольким классам, этим вы распределите задачу, может быть разгрузите память, но безусловно затрудните задачу исследователя понять, что у вас тут происходит.
  8. Пользуйтесь такими фичами как nested types, это не повлияет на производительность, но повлияет на «трудноизучаемость» вашего кода.
  9. Если говорить об защите алгоритмов, то стоит переложить их выполнение не одному методу, а части алгоритма передавать в выполнение различным классам, т.е фактически выполнение алгоритма будет являться взаимодействием нескольких классов.
  10. Инициализируйте класс формы без использования .resx и .resources файлов - к ним лучше обращаться по индексу во избежание проблем при обфускации ресурсов.

После обфускации:

  1. Обязательное тестируйте обфусцированную сборку. Обфускатор не Господь Бог, повторюсь. Он всего лишь делает свою работу, а вы – свою. Поэтому не стоит пренебрегать тестами сборки после обфускации.
  2. После обфускации, обязательно проверьте сборку утилитой peverify, которая идет с .Net Framework SDK – эта утилита проверяет метаданные вашей сборки на корректность. Если ваша сборка помечена как CLS-compliant – это тестирование обязательно. Есть халявщики, которые некорректно обфусцированную сборку прикрывают специальным разрешением из PermissionSet.SkipVerification для избежания встречи результирующей сборки с ее верификацией. Проверьте – не появилось ли такое разрешение в вашей сборке после обфускации, понятно на что намекаю. Правда managed C++ сборки имеют изначально такое разрешение, даже если вы о нем не упоминали в вашем коде, это тоже намек :) ).
  3. Немного о качестве обфускации – не поленитесь, посмотрите как обфусцирован сам обфускатор. Если обфускатор написан в native code – возможно авторы сомневаются в качестве своей обфускации?
  4. Создайте тестовый проект, который быстренько протестирует вашу сборку , статья не об этом, но пока обфускаторы не обладают возможностью протестировать вашу сборку на функциональность, лишь единицы вообще проверяют полученную сборку хотя бы на загрузку.
  5. И самое главное - при проблемах, контактируйте с авторами, присылайте сборки, пишите какой цели вам необходимо достичь – специалист подобен флюсу, при работе со своим продуктом глаз у него «замылен», ему надо подкидывать материал для тестирования – чем его больше, тем выше качество обфускации.

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

 
     

   
   
     
  VBNet рекомендует