Attributes
You can configure Dotfuscator's protection by annotating your application's source code with .NET attributes. Dotfuscator doesn't require you to edit your source to configure protection though. Dotfuscator's protection can also be configured separately from the source using the Dotfuscator config file.
Dotfuscator recognizes certain attributes when it processes assemblies, which tell Dotfuscator how to process the code elements the attributes annotate. By default, Dotfuscator also strips (removes) these attributes during processing, so that the shipped application does not contain sensitive configuration information.
References for Dotfuscator's attributes can be found in Obfuscation Attributes and Check Attributes.
Compatible Attributes
Dotfuscator recognizes certain attributes and ignores all others. The attributes that Dotfuscator recognizes are:
- Obfuscation Attributes which configure Declarative Obfuscation
- Check Attributes, which tell Dotfuscator how to inject Checks
For details on each set of attributes, see the linked pages. The rest of this page will cover aspects common to many attributes.
When to Use Attributes
Using attributes makes it easier to see, when looking at the source code, how code elements will be affected by Dotfuscator.
For instance, the following C# snippet uses the ObfuscationAttribute
to indicate a class (but not its members) should be excluded from renaming:
using System.Reflection;
[Obfuscation(Feature = "renaming", Exclude = true, ApplyToMembers = false)]
private class MyClass { /* ... */ }
However, there are some scenarios that are better served by using the Dotfuscator Config Editor to modify the Dotfuscator config file, rather than by using attributes. These scenarios include:
- When working with assemblies whose source code you cannot edit.
- When recompiling the code every time you want to change the protection settings is too much overhead for your development process.
- When using rules to exclude or include code items from certain protection features is more convenient or efficient than annotating each individual code item with an attribute.
- When configuring a Check that targets multiple locations.
You can combine attributes with Dotfuscator config file settings.
For instance, the snippet above indicates to Dotfuscator that MyClass
should be excluded from renaming, but you can exclude other types using the Dotfuscator Config Editor and save those exclusions to the config file.
When Dotfuscator processes the assemblies per the config file, it will not rename code items that were excluded either by attributes or the config file.
Adding Attributes to Code
To add attributes to your code and have Dotfuscator recognize them:
Add the appropriate references to your project. For details, see:
Apply compatible attributes to your code as you would any attribute.
The namespace (and thus the necessary
using
statement) varies depending on the attribute. See each attribute's section for details.As with other attributes, you may omit the suffix
Attribute
in the usage. For instance, this is a usage ofTamperCheckAttribute
:[TamperCheck(Action = CheckAction.Exit)] public void MyMethod() { // method body }
Build your application.
If using Obfuscation Attributes, use the Dotfuscator Config Editor to configure the following settings:
For each assembly that contains such attributes, ensure the Honor Obfuscation Attributes and Strip Obfuscation Attributes options are enabled.
Ensure the relevant protection features (such as renaming) are enabled.
If using Check Attributes, use the Dotfuscator Config Editor to configure the following settings:
For each assembly that contains such attributes, ensure the Honor Check Attributes and Strip Check Attributes options are enabled.
Ensure that Checks are enabled.
Save your project, and then have Dotfuscator protect your app as it normally does. For example, if Dotfuscator is integrated into your Visual Studio project, build the project in a Release configuration.
Interacting with Application Code
There are several scenarios where code injected by Dotfuscator can interact with existing application code. Application code that provides information to the injected code is known as a source; application code that receives information from the injected code is known as a sink.
Each Check can define one or more sinks, as well as one or more sources, for different contexts. The sources and sinks of a Check are defined by three of its properties:
An element property, which specifies the kind of code item that the source or sink is, such as Field or Method. For more information, see the Source Element Kinds and Sink Element Kinds subsections.
A name property, which specifies the name of the code item (if applicable).
An optional owner property, which specifies the type that defines the code item (if applicable). If not specified, this defaults to the type that defines the location of the Check. For more information, see the Owners subsection.
For instance, when a Check runs, it can inform the application of its result via an application notification sink. The sink can be configured through the Check's ApplicationNotificationSink properties:
ApplicationNotificationSinkElement
ApplicationNotificationSinkName
ApplicationNotificationSinkOwner
Details on the sources and sinks available for a given Check are listed on the relevant sections of the Check Attributes page.
Source Element Kinds
A source is an application code element that provides information to code injected by Dotfuscator. This section lists the kinds of code elements that can be used as a source for injected code.
Note that the supported elements, signatures of the elements, and meaning of the values obtained from the source vary by Check. See the Check Attributes page for details.
For instance, a ShelfLifeCheckAttribute
's Token Source requires a string
method argument, field, or property; or a method with the signature string()
.
The string value is the externally-stored Shelf Life Token.
What follows is a list of possible values for the element property of a Check (e.g., ShelfLifeTokenSourceElement for ShelfLifeCheckAttribute
).
The possible source element kinds are:
None: The source is disabled and no information is provided from the application to the injected code.
- The name and owner properties are ignored with this setting.
MethodArgument: The injected code copies the information from a method argument. The method argument must be an argument to all locations of the Check.
The name property specifies the name of the method argument.
The owner property is ignored with this setting.
Field: The injected code copies the information from a field. The field must be accessible to all locations of the Check.
The name property specifies the simple name of the field.
The owner property specifies the type that declares the field.
Property: The injected code copies the information from a property. The property and its getter must be accessible to all locations of the Check.
The name property specifies the simple name of the property.
The owner property specifies the type that declares the property.
Method: The injected code calls a method which returns the information. The method must be accessible to all locations of the Check.
The name property specifies the simple name of the method (e.g.,
MyMethod
, notbool MyMethod()
).The owner property specifies the type that declares the method.
DefaultAction: The injected code uses some default information. The exact behavior varies depending on the particular source being configured, which can include this setting being an invalid configuration.
Despite its name, this is not always the default value for the element property.
The name and owner properties are ignored with this setting.
Sink Element Kinds
This section lists the kinds of code elements that can be used as a sink for injected code.
Note that the supported elements, signatures of the elements, and meaning of the values provided to the sink vary by Check. See the Check Attributes page for details.
For instance, a Check's Application Notification Sink requires a bool
field or property; or a method, delegate field, or delegate method argument with signature void(bool)
.
The Boolean value is true
if the Check detected the unauthorized condition (such as an attached debugger or tampering) and false
otherwise.
What follows is a list of possible values for the element property of a Check (e.g., ApplicationNotificationSinkElement for a Tamper Check). The possible sink element kinds are:
None: The sink is disabled and no information is provided from the injected code to the application.
This is usually the default value for the element property.
The name and owner properties are ignored with this setting.
Method: The injected code calls a method, providing the information through the method's parameters. The method must be accessible to all locations of the Check.
The name property specifies the simple name of the method (e.g.,
MyMethod
, notvoid MyMethod(bool)
).The owner property specifies the type that declares the method.
Field: The injected code provides the information by setting a field. The field must be accessible and settable to all locations of the Check.
The name property specifies the simple name of the field.
The owner property specifies the type that declares the field.
Property: The injected code provides the information by setting a property. The property and its setter must be accessible to all locations of the Check.
The name property specifies the simple name of the property.
The owner property specifies the type that declares the property.
MethodArgument: The injected code calls a delegate specified by a method argument, providing the information through the delegate's parameters. The method argument must be an argument to all locations of the Check.
The name property specifies the name of the method argument.
The owner property is ignored with this setting.
Delegate: The injected code calls a delegate specified by a field, providing the information through the delegate's parameters. The field must be accessible to all locations of the Check.
The name property specifies the simple name of the field.
The owner property specifies the type that declares the field.
DefaultAction: The injected code performs some default behavior based on the information. The exact behavior varies depending on the particular sink being configured, which can include this setting being an invalid configuration.
Despite its name, this is not always the default value for the element property.
The name and owner properties are ignored with this setting.
Owners
A source's or sink's owner is the type that declares the source or sink.
The value of the owner property is a type name is expressed including the namespace, but not the assembly name.
For instance, consider a field named wasTampered
on the class MyType
which is declared in the namespace Outer.Inner
.
The properties defining a sink to this field would be as follows:
The element property: Field
The name property:
wasTampered
The owner property:
Outer.Inner.MyType
A source or sink may be a static member or an instance member of the owner. This distinction can affect how the owner property is used. Additionally, when using a Check with multiple locations, it is possible to have each location refer to a different owner.
For a Static Member
If the source or sink is a static member, then it may be defined on any type, provided it is accessible to all locations of the Check. The owner property specifies which type this is. If the property's value is blank, the type is assumed to be the type that defines the location of the Check.
For an Instance Member
If the source or sink is an instance member, then the following must be true:
The location of the Check must also be an instance member.
The location of the Check must be defined by the same type that declares the source or sink.
The owner property must be the name of this type, or be blank.
At runtime, the instance that was used when calling the location will also be used for the source or sink.
Multiple Owners with Checks
When using a Check with multiple locations, each location is evaluated individually with respect to sources and sinks. This means that each location may have a different source or sink, provided the following are true:
Each type that defines a location for the Check must have the specified source or sink member.
If a particular location is a static member, then the corresponding source or sink member must also be a static member.
If a particular location is an instance member, then the corresponding source or sink member may be an instance member or a static member.
The owner property must be left blank.