4.2. Declarative Obfuscation Using Custom Attributes
The .NET platform since version 2.0 provides two custom attributes designed to make it easy to change obfuscation behavior. Using these two attributes it is possible to override default decisions made by Eazfuscator.NET during obfuscation. Also these attributes can be used to configure advanced Eazfuscator.NET features. It is assumed that you have a knowledge about custom attributes and how to apply them in your development language.
This attribute can be applied to an assembly to tell obfuscator how to treat it. Setting the
AssemblyIsPrivate property to false tells obfuscator to treat an assembly as a library. Setting the
AssemblyIsPrivate property to true tells obfuscator to treat an assembly as an executable. The difference is how Eazfuscator.NET renames and seals public types and their public members. In case of an executable all public types and their public members are considered terminal so they gets renamed. In case of a library, those types and members may be used by other assemblies, thus they do not get renamed.
The default obfuscator behavior is to treat all
.dll assemblies as libraries, and all
.com assemblies as executables. If the assembly has another file extension (neither
.com) then Eazfuscator.NET treats such assemblies depending on the entry point presence. If the assembly has an entry point then it is treated as an executable; otherwise, as a library.
This attribute is the main troubleshooter of the reflection related problems that may occur during obfuscation. As it was mentioned above Eazfuscator.NET has no formal reflection scenarios analysis engine and it uses heuristic algorithms instead. Decisions produced by those algorithms may be wrong in some rare cases so you may need to override them by using
System.Reflection.ObfuscationAttribute can be applied to a type. Possible feature property values are "all" (by default), "renaming" and "properties renaming". So if you want to disable renaming of your class you may write something like an example below (C#):
Example 4.1. Disabling class renaming
[System.Reflection.ObfuscationAttribute(Feature = "renaming", ApplyToMembers = false)]
If you want to disable renaming of your class and all its members you may write:
Example 4.2. Disabling class and its members renaming
[System.Reflection.ObfuscationAttribute(Feature = "renaming", ApplyToMembers = true)]
If you want to disable renaming of the properties in your class you may write:
Example 4.3. Disabling class properties renaming
[System.Reflection.ObfuscationAttribute(Feature = "properties renaming")]
Sometimes it may be useful to disable just single or several properties in a class. In order to do that you may write:
Example 4.4. Disabling single class property renaming
[System.Reflection.ObfuscationAttribute(Feature = "renaming")]
public string DisplayName
In some rare cases you may want to disable the renaming of properties for anonymous types. In order to do that, you should apply the following attribute at the assembly level:
Example 4.5. Disabling the renaming of anonymous type properties
[assembly: Obfuscation(Feature = "anonymous type properties renaming", Exclude = true)]
Another purpose of
System.Reflection.ObfuscationAttribute is to configure Eazfuscator.NET features. You may find more information about this at the following places:
There are no
System.Reflection.ObfuscationAttribute attributes available in .NET Compact Framework, Silverlight and WinRT runtimes. So if you want to use declarative obfuscation you must define corresponding attributes in your assembly. The easiest way to do this is to add ready-to-use file ObfuscationAttributes.cs (for C#) or ObfuscationAttributes.vb (for VB.NET) to your project. These files can be found at Start Menu → Eazfuscator.NET → Eazfuscator.NET Code Snippets menu item. Alternatively they can be found at
C:\Program Files\Eazfuscator.NET\Code Snippets path.
The path may differ depending on the installation options and operating system. For example, this path may look like
C:\Program Files (x86)\Eazfuscator.NET\Code Snippets on 64-bit operating systems.
Sometimes it may not be possible to access the class definition directly to apply a custom attribute for obfuscation tuning. If latter is the case then indirect declarative obfuscation comes to the rescue. It allows to indirectly tune the obfuscation by using custom attributes defined at the assembly level.
Let's take a closer look on an example. Suppose there is a class
MyNamespace.ResourceClass1 and its declaration can not be changed because it was automatically generated by a tool. So how to disable the renaming of that class during obfuscation? The solution is to use indirect declarative obfuscation. In order to do that, you should apply the following attribute at the assembly level (C#):
Example 4.6. Indirectly disable the renaming of a class
[assembly: Obfuscation(Feature = "apply to type MyNamespace.ResourceClass1: renaming", Exclude = true, ApplyToMembers = false)]
Please note, the feature string starts with "apply to type" expression in the sample above. That expression signals Eazfuscator.NET that feature should be redirected to
MyNamespace.ResourceClass1 class. There is a semicolon after the class name; just next to it, there is a real feature name "renaming". So, the above sample code is virtually equivalent to the code shown below (C#):
[System.Reflection.ObfuscationAttribute(Feature = "renaming", Exclude = true, ApplyToMembers = false)]
The class name in indirect expression can contain a glob mask.
As for example, it's possible to disable all obfuscation features for all classes inside a given namespace. The following attribute should be applied at the assembly level in order to do that (C#):
Example 4.7. Indirectly disable all obfuscation features for a given namespace
[assembly: Obfuscation(Feature = "apply to type SomeExcludedNamespace.*: all", Exclude = true, ApplyToMembers = true)]
A more powerful configuration syntax is available in conditional obfuscation.
Sometimes you may want to define obfuscation attributes in several places.
For example, a given assembly may be configured by two files. One file,
ObfuscationSettings.cs, defines the obfuscation directives directly related to the given assembly. Another file,
CommonObfuscationSettings.cs, defines the obfuscation directives common to all obfuscated assemblies in a whole project. That file is shared among all obfuscated assemblies in a project.
Then you decide that string encryption is not needed for most assemblies, so you just disable it at the common level in
[assembly: Obfuscation(Feature = "string encryption", Exclude = true)]
Some time after, it turns out that a particular assembly
Contoso.Engine.dll should have string encryption enabled.
ObfuscationSettings.cs file defined in
Contoso.Engine assembly looks like this:
[assembly: Obfuscation(Feature = "string encryption", Exclude = false)]
Please note that both
CommonObfuscationSettings.cs files are included in compilation of
Contoso.Engine assembly as source code files. So both directives find their ways into resulting compiled
What happens when Eazfuscator.NET sees conflicting obfuscation directives in input assembly? The exact semantics depends on a directive. Eazfuscator.NET may just take the least permissive directive for features like string encryption. Another strategy is to take the very first directive and abandon the rest (note that C# and many other .NET compilers do not guarantee the order of custom attributes in resulting compiled assembly). Nevertheless this is not what you want when it comes to several configuration sources where you want to have priorities. E.g. directives defined in
ObfuscationSettings.cs file should have a higher priority than those defined in
So how to achieve the prioritization of obfuscation attributes? Priority syntax described below makes that possible.
Priority is defined by a numeric prefix:
[Obfuscation(Feature = "1. <some feature here>")]
The idea behind priority prefixes is to form a natural list:
[Obfuscation(Feature = "1. <some high priority feature here>")]
[Obfuscation(Feature = "2. <medium priority feature>")]
[Obfuscation(Feature = "3. <a feature with the lowest priority>")]
where the first item has the highest priority, the second is less important, and so on. Just like your typical TODO list.
Priority prefix can only contain a natural number (a positive integer). Zero and negative priorities are not allowed.
So here is how the aforementioned configuration files should be defined to make the prioritization work according to the task described in introduction. The content of
[assembly: Obfuscation(Feature = "2. string encryption", Exclude = true)]
The content of
ObfuscationSettings.cs file for
[assembly: Obfuscation(Feature = "1. string encryption", Exclude = false)]