4.3. Conditional Obfuscation
Conditional obfuscation is an extension feature to indirect declarative obfuscation.
4.3.1. Types
Conditional obfuscation allows to process the types in 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 [name mask]
is a glob mask for the type name and [condition]
is a string defining the condition for a match.
Condition is defined as a boolean predicate with Pascal syntax. A quick example of a 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 |
|
anonymous |
|
class |
|
closure |
|
compiler-generated |
|
delegate |
|
enum |
|
generic |
|
interface |
|
internal |
|
nested |
|
obfuscator-generated |
|
private |
|
protected |
|
public |
|
record |
|
sealed |
|
serializable |
|
static |
|
struct |
|
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 |
|
true |
|
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 |
| Returns |
| Returns
|
| Returns |
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 | Medium |
or | Binary operator for boolean | Lower |
= | Binary operator for boolean | Lowest |
<> | Binary operator for boolean | Lowest |
The precedence of operations can be changed by parentheses.
Let's take a look at 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 glob mask 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 |
|
compiler-generated |
|
const |
|
constructor |
|
event |
|
field |
|
generic |
|
internal |
|
method |
|
obfuscator-generated |
|
private |
|
property |
|
protected |
|
public |
|
readonly |
|
static |
|
virtual |
|
The information on this topic is extremely bare, so let's take a relaxed look at 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 types and members targeted by a specific conditional obfuscation directive. To achieve that, you can use the log
feature as shown below:
Example 4.21. Log all members targeted by a conditional obfuscation directive
using System.Reflection;
[assembly: Obfuscation(Feature = "apply to type * when public: apply to member *: log")]
Logging does not affect obfuscation in any way. It just dumps the list of all targeted items to the Eazfuscator.NET's log.