Attributes
Dotfuscator is a post-build tool, meaning it operates on compiled .NET assemblies, not source code. Because of this, most options for how Dotfuscator protects an application are stored in a Dotfuscator config file, separate from the source code.
However, if you do have access to the source code, you can also configure some Dotfuscator features with .NET attributes.
Dotfuscator recognizes certain attributes when it processes assemblies, which configure how Dotfuscator will 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.
Important: Attributes by themselves do not cause code elements to be protected. The compiled assemblies must still be processed by Dotfuscator for protection to be applied. Attributes are merely an additional way to configure the protection, alongside the use of a Dotfuscator config file.
Compatible Attributes
Dotfuscator recognizes certain attributes and ignores all others. The attributes that Dotfuscator recognizes are:
- Obfuscation Attributes which configure Declarative Obfuscation
- Injection Attributes, further subdivided into:
- Check Attributes, which tell Dotfuscator how to inject Checks
- Instrumentation Attributes, which tell Dotfuscator how to inject Instrumentation features
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 user interface 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 code items from obfuscation is more convenient or efficient than annotating each individual code item with an attribute.
- When configuring a Check that targets multiple locations.
- When configuring settings not available through attributes, such as enabling telemetry.
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 user interface 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 user interface 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 Injection Attributes, use the Dotfuscator user interface to configure the following settings:
For each assembly that contains such attributes, ensure the Honor injection attributes and Strip injection attributes options are enabled.
Ensure that injection is enabled.
If using Check Telemetry or Instrumentation, ensure the appropriate telemetry options are enabled.
Build the config file. Dotfuscator acts according to the configured attributes, then removes the attributes (and the related assembly references) from your application.
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.
Note: These are not to be confused with the code element which an attribute annotates, or the methods a GUI-configured Check targets. Those are known as the locations of an instrumentation attribute or Check.
Each instrumentation attribute or Check can define one or more sources, as well as one or more sinks, for different contexts. The sources and sinks of an instrumentation attribute or 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 instrumentation attribute or 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 instrumentation attribute or Check are listed on the relevant sections of the Check Attributes and Instrumentation Attributes pages.
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 instrumentation attribute or Check. See the Check Attributes and Instrumentation Attributes pages for details.
For instance, a SetupAttribute
's Opt-In Source requires a bool
method argument, field, or property; or a method with the signature bool()
.
The Boolean value is true
if the user should be opted-in to Instrumentation Telemetry, and false
otherwise.
What follows is a list of possible values for the element property of an instrumentation attribute or Check (e.g., OptInSourceElement for SetupAttribute
).
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 instrumentation instrumentation attribute or 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 instrumentation instrumentation attribute or 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 instrumentation attribute or 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 instrumentation attribute or 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.
ApplicationSetting: The injected code retrieves information from an application settings configuration file (
AssemblyName.exe.config
). This element kind is only available for the Endpoint Source of aSetupAttribute
.The name property is the name of the key in the application configuration file. For example,
MyKey
for this configuration file:<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> <appSettings> <add key="MyKey" value="mycompany.com:80/telemetry/endpoint"/> </appSettings> </configuration>
The owner property is ignored with this setting.
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
A sink is an application code element that receives information from code injected by Dotfuscator. 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 instrumentation attribute or Check. See the Check Attributes and Instrumentation Attributes pages 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 an instrumentation attribute or 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 instrumentation attribute or 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 instrumentation attribute or 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 instrumentation attribute or 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 instrumentation attribute or 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 instrumentation attribute or 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.
Note: Some kinds of source and sink elements do not require a type to be specified in the owner property, and thus ignore whatever value is configured. The element types that ignore the owner property are: None, MethodArgument, ApplicationSetting, and DefaultAction.
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 to 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 instrumentation attribute or 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 instrumentation attribute or Check.
For an Instance Member
If the source or sink is an instance member, then the following must be true:
The location of the instrumentation attribute or Check must also be an instance member.
The location of the instrumentation attribute or 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.