Conditional obfuscation is an extension feature to indirect declarative obfuscation. Conditional obfuscation allows to process the types in a bulk according to their natural properties such as visibility (public, internal, protected, private), subtype (class, struct, enum, delegate) and others.
This functionality is achieved by when
conditional clause which can be specified in an obfuscation attribute.
So the full conditional attribute notation has the following form:
[assembly: Obfuscation(Feature = "Apply to type [name mask] when [condition]: [feature]")]
where [condition]
is a string defining the condition for a match.
Condition is defined as a boolean predicate with Pascal syntax. Quick example of an conditional attribute is shown below.
Example 4.8. Indirectly disable renaming of all internal enums and their members
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when internal and enum: renaming", Exclude = true, ApplyToMembers = true)]
Please take a closer look at internal and enum
predicate in the sample above.
What it says is this: the result of predicate is true
when internal
variable equals to true and
enum
variable equals to true too; otherwise the result of predicate is false
.
The values of internal
and enum
variables are calculated for every type in the assembly,
describing the natural properties of a CLR type in boolean form.
The list of available variables is presented in the table below.
Table 4.1. The list of available variables for conditional obfuscation of types
Variable | Description |
---|---|
abstract |
true if the type is abstract; otherwise, false
|
anonymous |
true if the type is anonymous; otherwise, false
|
class |
true if the type is a class; otherwise, false
|
delegate |
true if the type is a delegate; otherwise, false
|
enum |
true if the type is an enumeration; otherwise, false
|
generic |
true if the type is generic; otherwise, false
|
interface |
true if the type is an interface; otherwise, false
|
internal |
true if the type is internal; otherwise, false
|
nested |
true if the type is nested; otherwise, false
|
private |
true if the type is private; otherwise, false .
Applies to nested types only; false if the type is not nested
|
protected |
true if the type is protected; otherwise, false .
Applies to nested types only; false if the type is not nested
|
public |
true if the type is public; otherwise, false
|
sealed |
true if the type is sealed; otherwise, false
|
serializable |
true if the type is serializable; otherwise, false
|
static |
true if the type is static; otherwise, false
|
struct |
true if the type is a structure; otherwise, false
|
Predefined constants can be used in expressions as well. The list of available constants is presented in the table below.
Table 4.2. The list of available constants for conditional obfuscation
Constant | Description |
---|---|
false |
false value
|
true |
true value
|
Built-in functions can be used in expressions too. The list of available functions is presented in the table below.
Table 4.3. Built-in functions for conditional obfuscation
Function | Description |
---|---|
|
Returns true if the type inherits another type specified by type_name parameter; otherwise, false .
Inherits function considers base and all inherited types including interfaces.
Type name parameter can contain either the full type name or a mask for a bulk match
|
|
Returns true if the type extends another type specified by type_name parameter; otherwise, false .
Extends function only checks the base type, but all implemented and inherited interfaces are considered.
Type name parameter can contain either the full type name or a mask for a bulk match
|
|
Returns true if the type or member has a custom attribute specified by type_name parameter; otherwise, false .
Type name parameter can contain either the full type name or a mask for a bulk match.
Example:
|
Variables, functions and constants can be combined by operators. They have the standard Pascal precedence. The list of available operators is presented in the table below.
Table 4.4. The list of available operators for conditional obfuscation
Operator | Description | Priority |
---|---|---|
not | Unary operator for boolean negation | Highest |
and |
Binary operator for boolean and operation
|
Medium |
or |
Binary operator for boolean or operation
|
Lower |
= |
Binary operator for boolean equal operation
|
Lowest |
<> |
Binary operator for boolean not equal operation
|
Lowest |
The precedence of operations can be changed by parentheses.
Let's take a look on examples.
Example 4.9. Disable renaming of all types except enums
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when not enum: renaming", Exclude = true)]
Example 4.10. Disable renaming of all internal nested and serializable types together with their members
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when internal and (nested or serializable): renaming", Exclude = true, ApplyToMembers = true)]
Example 4.11. Disable renaming of all types except internal nested and serializable types
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when not (internal and (nested or serializable)): renaming", Exclude = true)]
Example 4.12. Disable renaming of all interfaces in Contoso.Core.Services namespace
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type Contoso.Core.Services.* when interface: renaming", Exclude = true)]
Example 4.13. Disable renaming of all classes derived from System.IDisposable interface. Renaming of members for matched classes is disabled too
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when class and extends('System.IDisposable'): renaming", Exclude = true, ApplyToMembers = true)]
As you can see the conditions can have any complexity and can be freely defined to achieve your specific goals.
It was described how to tune the obfuscation of types in a bulk way in the section above. But what about type members such as methods, fields, properties and others? Sometimes it may be beneficial to process them in a bulk way too.
The declarative attribute for conditional obfuscation of type members has the following form:
using System.Reflection; [Obfuscation(Feature = "Apply to member [name mask] when [condition]: [feature]")] class Sample1 { … }
[name mask]
is a pattern which selects the members according to their names.
[condition]
allows to specify a boolean predicate to select members according to their properties.
The rules are all the same as for types; the only difference is a set of variables which can be used in a boolean predicate.
The list of available variables is presented below.
Table 4.5. The list of available variables for conditional obfuscation of type members
Variable | Description |
---|---|
abstract |
true if the member is abstract; otherwise, false
|
const |
true if the member defines a literal constant; otherwise, false .
Applies to fields only; false if the member is not a field
|
constructor |
true if the member is a constructor; otherwise, false
|
event |
true if the member is an event; otherwise, false
|
field |
true if the member is a field; otherwise, false
|
generic |
true if the member is generic; otherwise, false .
Applies to methods only; false if the member is not a method
|
internal |
true if the member is internal; otherwise, false
|
method |
true if the member is a method; otherwise, false
|
private |
true if the member is private; otherwise, false
|
property |
true if the member is a property; otherwise, false
|
protected |
true if the member is protected; otherwise, false
|
public |
true if the member is public; otherwise, false
|
readonly |
true if the member is read-only; otherwise, false .
Applies to fields and properties only; false if the member is neither a field nor a property
|
static |
true if the member is static; otherwise, false
|
virtual |
true if the member is virtual; otherwise, false
|
The information on this topic is extremely bare, so let's take a relaxed look on some real-life samples.
Example 4.14. Disable renaming of public properties
using System.Reflection; [Obfuscation(Feature = "Apply to member * when property and public: renaming", Exclude = true)] class ImageQualityService { … }
Example 4.15. Disable renaming of all methods
using System.Reflection; [Obfuscation(Feature = "Apply to member * when method: renaming", Exclude = true)] class CellCallEngine { … }
Example 4.16. Disable renaming of internal fields
using System.Reflection; [Obfuscation(Feature = "Apply to member * when field and internal: renaming", Exclude = true)] class ContosoHeadquarters { … }
Conditional obfuscation of types and type members can be easily combined to achieve specific goals in an elegant and powerful way.
Just take a look at the samples below.
Example 4.17.
Disable renaming of property Contoso
in type Acme.Services
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type Acme.Services: apply to member Contoso when property: renaming", Exclude = true)]
Example 4.18. Disable renaming of all public properties in all types
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type *: apply to member * when public and property: renaming", Exclude = true)]
Example 4.19.
Disable renaming of all public properties in types defined in MyNamespace
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type MyNamespace.*: apply to member * when public and property: renaming", Exclude = true)]
Example 4.20. Disable renaming of all internal events in all public types
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when public: apply to member * when event and internal: renaming", Exclude = true)]
Sometimes it may be useful to get the full list of classes and members that are targeted by specific conditional obfuscation statement.
To achieve that, you can use log
feature as shown below:
Example 4.21. Log all affected members of all public classes
using System.Reflection; [assembly: Obfuscation(Feature = "Apply to type * when public: apply to member *: log")]
Note | |
---|---|
Logging does not affect obfuscation in any way. It just dumps the list of items at Eazfuscator.NET's console output. To see this list in Visual Studio, please take a look at Output Window with View → Output (Ctrl+W,O) right after obfuscation. |