Visual Basic, .NET, ASP, VBScript
 

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

Опыт и некоторые аспекты работы в Spices.Net

Вводная часть.

Материал статьи несет не только сугубо информативный характер касающийся эффективного использования Spices.Net но и может быть интересен начинающим или интересующимся технологией .Net и различными аспектами использования Spices.Net в .Net разработках. В этой статье авторы постарались ознакомить читателей с кругом проблем и задач возникающих при создании .Net приложений а также с решениями этих проблем и задач, предлагаемыми Spices.Net.

 

Небольшой словарик терминов употребляемых в статье:

.Net (.Net Framework)– технология Microsoft являющаяся следующим поколением СOM технологий, позволяющая создавать т.н управляемые (managed) приложения, именуемые сборками. Основным языком программирования является C#, но для создания .Net приложения разработчик может использовать VB.Net, managed C++ (with managed extensions), Delphi.Net (Pascal for .Net), Fortran.Net. Технология претендует на кросс-платформменность, т.к есть порты под Linux (Mono, Portable.Net (PNet)) а также для BeOS и FreeBSD (SSCLI). Также существует .Net CompactFramework – реализация .Net для PocketPC/ Windows.Mobile.

PE- portable executable, архитектура executable файлов (.exe, .dll)

Assembly, сборка – таково название executable файлов, создаваемых для исполнения в .Net

Метаданные – структура классов, их взаимоотношения, а также вся необходимая информация о содержимом .Net сборки описаны метаданными, которые представляют из себя набор таблиц, сформированных по определенным принципам.

Член сборки – элемент метаданных.

Член класса – элемент класса, обычно это поле(field), метод(method), свойство(property) или событие(event)

 

 

Spices.Net - Что это за программа?

Spices.Net – мощный и гибкий инструмент для .Net разработчиков, предоставляющий широкий спектр инструментов для просмотра и навигации, детального исследования структуры .Net сборок (от структуры PE и метаданных до структуры классов и дизассемблирования/декомпиляции тел методов), построения моделей отображающих различные виды взаимоотношений между членами .Net сборок, а также позволяющий эффективно защищать .Net сборки, создавать и управлять документацией к сборкам, подготавливать для локализации и локализовывать Net для различных культур и языков.

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

 

Возможности и перспективы Spices.Net

Знакомство с модулями, входящими в состав Spices.Net

Базовые возможности и базовые модули Spices.Net

Spices.Informer

Spices.Investigator

Spices.Modeler

Spices.Obfuscator

Зачем защищать .Net сборки?

Как это работает?

Различные конфигурации проектов и типы сборок и предлагаемые для них виды защиты

Рекомендации (best practices):

1. Before coding: Take into account possibility of obfuscation in step of designing(before coding) of your software.

2. Designing: Separate classes on 2 groups - public(this group will not obufscated: serialization, reflection, managed resources) and hidden (core functionality, secret resources).

3. Separate classes on subclasses (and place them into nested classes) on functionality (serialization, public part - that can be used in reflection and serialization)- hidden classes can be nested, each classes can be directed onto decision of certained task.

4. Place core functionality into nested classes. As result of obfuscation you can receive all public classes (that should load serialization info and display property values) untouched and core classes - obfuscated.

5. Place secret resources/protected resource into byte arrays.

6. Mask parts of functionality by intensive use of the interfaces

for example:

Public class MyPublicClass : IServiceProvider

{

//nested

private class MyNestedClass : ISomeInterface

{

}

//properties

internal ISomeInterface MyHiddenProperty {...}

//events

public object GetService(Type servicetype)

{

if (serviceType == ISomeInterface)

return MyHiddenProperty;

if (serviceType == ISomeInterface1)

return new MyNestedClass(this);

}

}

7. Mask interface implementations:

internal class SomeClass : IServiceProvider

{

private IServiceProvider parent

public SomeClass(IServiceProvider parent)

{

this.parent = parent;

}

public object GetService(Type serviceType)

{

if (serviceType == MyProperty.GetType())

return MyProperty;

if (serviceType == typeof (ISomeService))

return new SomeService(this);

if (parent != null)

return parent.GetService(serviceType);

return null;

}

public SomeAnotherService MyProperty

{

get {return new SomeAnotherService(this);}

}

}

 

А теперь замаскируем:

 

internal class SomeBaseClass : IServiceProvider

{

protected IServiceProvider parent = null;

public object GetService(Type serviceType)

{

if (serviceType == MyProperty.GetType())

return MyProperty;

if (serviceType == typeof (ISomeService))

return new SomeService(this);

if (parent != null)

return parent.GetService(serviceType);

return null;

}

 

public SomeAnotherService MyProperty

{

get {return new SomeAnotherService(this);}

}

}

 

internal class SomeClass : SomeBaseClass

{

public SomeClass(IServiceProvider parent)

{

base.parent = parent;

}

}

5. Не используйте литерные строчные константы (string const someField = “Hello, world!”), используйте вместо них string static someField = “Hello, world!”, это будет оптимально как с точки зрения метаданных (в первом случае инициализационые данные будут записаны в метаданные 2 раза – не оптимально, во втором случае - один).

6. Necessarily!!!: Sign your assemblies, protect your software from viruses and reproducing. Damaged assembly can't be run, signed assembly can't be re-signed by another strong-name key.

7. Review assemblies in different assembly browsers to see the quality of obfuscation.

 

Рекомендации по использованию Reflection

Reflection предоставляет оргомные возможности для разработчика для того чтобы «добраться» до каких-либо данных через информацию о типе. Но после обфускации наименования членов сборки могут измениться и код использующий Reflection перестанет работать.

Не зависьте от названий.

Для того чтобы снять с себя эту головную боль и искать, где-же все таки не работает код необходимо не зависеть от названий членов сборки.

Ниже приводятся некторые решения этого вопроса.

 

Использование интерфейсов.

Языки программирования позволяют имплементировать несколько интерфейсов на одном типе, вы можете этим воспользоваться для экстрагирования необходимой информации:

 

private string ExtractSomeInfo(object obj)

{

ISomeInfo si = obj as ISomeInfo;

if (si != null)

return si.SomeInfo;

return null;

}

 

Декларативный способ использования Reflection:

 

Используя богатые возможности использования атрибутов вы можете помечать необходимые члены сборки атрибутами-метками и по этим атрибутам находить необходимую информацию:

 

public enum MarkerType

{

unknown,

what,

which

}

 

public class MarkerAttribute : Attribute

{

private MarkerType type = MarkerType.unknown;

public Marker(MarkerType type)

{

this.type = type;

}

public MarkerType Type

{

get

{

return type;

}

}

}

 

internal class SomeClass

{

[Marker(MarkerType.what)]

private static string what = "12345";

[Marker(MarkerType.which)]

private static string which = "54321";

}

 

private string GetString(SomeClass someClass, MarkerType markerType)

{

if (someClass != null)

{

foreach(FieldInfo fi in someClass.GetType.GetFields())

{

foreach(MarkerAttribute marker in fi.GetCustomAttributes(typeof(MarkerAttribute), false))

{

if (marker.Type == markerType)

return fi.GetValue(someClass);

}

}

}

return null;

}

 

 

Включение в обфускацию(использование атрибутов ObfuscateAttribute, ObfuscateMembersAttribute):

В конце концов вы можете исключить необходимые для использования в Reflection члены сборки, достаточно пометить их специальным атрибутом NotObfuscate:

 

using NineRays.Obfuscator;

 

[Obfuscate]

public class ReflectionTest

{

[Obfuscate]

public int which;

[Obfuscate]

public int what;

internal ReflectionTest(int data) : base()

{

this.which = data;

this.what = data;

}

}

 

Вы также можете воспользоваться атрибутом ObfuscateMembers для задания маски включенных в обфускацию членов сборки:

Например – при использовании ObfuscateMembersAttribute(“MyCompany.Foo*”) будут включены все классы их члены и nested классы подходящие под это выражение.

В данном случае используется полное имя члена (Namespace.DeclaringType.MemberName)

Вы можете «прицепить»(attach) несколько ObfuscateMembersAttribute атрибутов к деларации сборки для определения нескольких pattern-ов включения в обфускацию.

Помните: оценка включенности члена в обфускацию производится после оценки исключенности члена, если член исключен из обфускации оценка его включенности уже не производится.

 

Исключение из обфускации(использование атрибутов NotObfuscateAttribute, NotObfuscateMembersAttribute):

В конце концов вы можете исключить необходимые для использования в Reflection члены сборки, достаточно пометить их специальным атрибутом NotObfuscate:

 

using NineRays.Obfuscator;

 

class ReflectionTest

{

[NotObfuscate]

private int which;

[NotObfuscate]

private int what;

internal ReflectionTest(int data) : base()

{

this.which = data;

this.what = data;

}

}

 

Вы также можете воспользоваться атрибутом NotObfuscateMembers для задания маски исключенных из обфускации членов сборки:

Например – при использовании NotObfuscateMembersAttribute(“MyCompany.Foo*”) будут исключены все классы их члены и nested классы подходящие под это выражение.

В данном случае используется полное имя члена (Namespace.DeclaringType.MemberName)

Вы можете «прицепить»(attach) несколько NotObfuscateMembersAttribute атрибутов к декларации сборки для определения нескольких pattern-ов исключения из обфускации.

 

 

Отладка обфусцированных сборок

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

1.      Установите свойство StripDebugInfo в значение False для того чтобы запретить обфускатору удаление debug info из сборок.

2.      Используйте «читабельные» режимы именования обфусцированных членов сборки в ObfuscationOptions.Naming – любой кроме Naming.NonDisplayable для облегчения процесса отладки.

3.      Скопируйте .PDB файлы, ассоциированные со сборками в место их обфускации (SaveToDirectory).

 

Отлаживать сборки вы можете как в DbgClr.exe GUI дебаггере входящем в состав .Net Framework, так и в самой Visual Studio.

 

Отладка обфусцированных сборок в Microsoft Visual Studio

Для того чтобы организовать отладку в Visual Studio вам необходимо иметь Spices.Obfuscator c Visual Studio Integration Package VSIP (Spices.VSIP.Net Suite или Spices.VSIP.Net Obfuscator).

В свойстве проекта SaveToDirectory установите каталог из которого будет запускаться startup project – обычно .exe файл (для примера: D:\work\MyApp\bin\Debug\MyApp.exe – то необходимо указать D:\work\MyApp\bin\Debug). Это необходимо для того чтобы обфускатор поместил все обфусцированные для отладки сборки в каталог запуска и Visual Studio Debugger использовал бы эти сборки для отладки.

 

Специфика обфускации сборок CompactFramework

Есть специфика J

 

Прячем ресурсы:

Byte[] rules!

Проблемы и их решение

Q:Как обфусцировать сборку чтобы она была CLSComplaint и устанавливалась в GAC?
A:

Q: Как правильно обфусцировать сборку для использования в средствах разработки?

Q: Как максимально обфусцировать сборку?

Q: Как обфусцировать сборки проекта как единое целое?

Q: Как обфусцировать сборки, которые в дальнейшем необходимо патчить?

Q: Как обфусцировать сборки чтобы имена обфусированных элементов при каждой обфускации оставались одинаковыми?

Q: Поддерживает ли Spices.Net Obfuscator Incremental Obfuscation?

 

Рекомендации

На этапе проектирования

1.      Подписывайте сборки, для того чтобы их невозможно было подделать, внедрить вредоносный код. Помните что strong named assemblies невозможно модифицировать не подписав их еще раз, но в этом случае полное имя сборки будет уже другим, не идентичным исходному.

2.      Храните .snk (strong-name key file) ключ в безопасном месте, для того чтобы злоумышленник не смог получить ключ для подписания взломанных сборок.

3.      Помните, что после защиты ващего продукта обфускатором, он остается дизассемблируемым. И

4.      Планируйте видимость (visibility) классов и членов класса.

5.      Разделяйте функциональность на различные виды (layers). Простейшим разделением может быть разделение на сериализуемые классы, классы для публичного предоставления и модификации settings/properties и классов реально реализующих функциональность сборки. Бывает этого трудно достигнуть, поэтому лучше всего об этом подумать заранее.

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

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

8.      Не используйте информацию о типе в виде строки, если не уверены что этот тип не может быть офбусцирован.

Пример:

[SomeAttribute(typeof(MyType))] – правильно

[SomeAttribute(“MyNamespace.MyType”)] – неправильно, обфускатор не знает о том что в качестве аргумента используется информация о типе.

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

На этапе кодирования

1.      Не используйте имена классов в коде (string someName = typeof(SomeClass).Name or string someName = this.GetType().Name), если не уверены что класс точно не будет обфусцирован. Это достаточно часто встречающаяся ошибка.

2.      Use try-catch statements to determine place of exception:

 

try

{

//something...

}

catch (Exception e)

{

// Gui scheme

// MessageBox.Show(string.Format("Exception: {0}\r\nStackTrace:\r\n{1}", e.Message, //e.StackTrace);

//Debug scheme

System.Diagnostics.Debug.WriteLine(string.Format("Exception: {0}", e.Message));

System.Diagnostics.Debug.WriteLine(string.Format("Stack Trace: {0}", e.StackTrace));

}

 

Also in the exe you can attach handler to AppDomain.UnhandledException and Application.ThreadException to trace unhandled exceptions. To show exception messages you can use ThreadExceptionDialog class

 

 

Ограничения Evaluation version

Spices.Decompiler

Возможности Spices.Decompiler

Ограничения Evaluation version

Spices.Documenter

Возможности Spices.Documenter

Ограничения Evaluation version

 

Spices.Localizer

Возможности Spices.Localizer

Использование вместе со Spices.Obfuscator

Ограничения Evaluation version

 

Visual Studio Integration Package

 

Итоги и выводы

 

 
     

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