PreEmptive Protection - Dotfuscator 4.31
User Guide

Advanced Topics

P/Invoke Methods

P/Invoke methods (i.e. native platform methods) are automatically not renamed if Dotfuscator detects that the name is used to find the corresponding native function. P/Invoke methods that are mapped to native functions by an alias or by ordinal can be renamed.

Managed C++ and IJW (It Just Works) Thunking

Dotfuscator can process assemblies containing managed and unmanaged (native) code, such as those created by the Managed C++ compiler. Dotfuscator performs renaming and metadata removal on mixed code modules; however, string encryption, control flow obfuscation, and removal are automatically disabled. These features are still enabled on other "pure managed" input modules included in the run.

Obfuscating Assemblies with Managed Resources

Managed resources may be embedded inside a module (internal) or may be in files external to the module. Often, part of the name of the managed resource is a type name (see the .NET Framework documentation for more information about the “hub and spoke model” for lookup of managed resources.).

When the type name is renamed, Dotfuscator attempts to locate and rename the corresponding managed resource. If the resource is internal to the assembly, this is automatic. If the resource is embedded inside an external file, then the file must be in the same directory as the referencing module. If the resource is embedded inside another assembly, then that assembly must be one of the input assemblies.

Obfuscating Assemblies with Satellite DLLs

Localized applications can be seamlessly obfuscated along with their satellite resource DLLs. Dotfuscator automatically discovers these DLLs using the same rules that the runtime uses and automatically adds them as inputs to the obfuscation process. You do not need to explicitly specify them as inputs.

Your localized resources contained in the satellite DLLs will be renamed in sync with your culture neutral resources in the main assembly.

Obfuscating Multi-module Assemblies

A .NET assembly may be made up of multiple modules (i.e. files on disk). Usually an assembly is made up of one module, and this is the scenario that most tools, such as Visual Studio, support. Occasionally it is desirable to create assemblies made up of more than one module. Dotfuscator supports this scenario. Note that obfuscating a multi-module assembly is not the same as obfuscating multiple input assemblies.

To obfuscate a multi-module assembly, only the prime module needs to be listed as an input assembly. The non-prime modules are searched for in the same directory as the prime module.

In the prime module’s assembly manifest, Dotfuscator automatically updates the hash values of the other modules.

Obfuscating 64-Bit Assemblies

Dotfuscator can transparently obfuscate managed assemblies written explicitly for specific CPU architectures, including 64-bit architectures.

Dotfuscator itself is a managed application and can run on 32-bit and 64-bit versions of Windows.

Reflection and Dynamic Class Loading

Reflection and dynamic class loading are extremely powerful tools in the .NET architecture. However, this level of runtime program customization prevents Dotfuscator from infallibly determining whether it is safe to rename all types loaded into a given program.

Consider the following (C#) code fragment:

C# Code Fragment:

public object GetNewType() {
   Type type = Type.GetType( GetUserInputString(), true );
   object newInstance = Activator.CreateInstance( type );
   return newInstance;
}

This code loads a type by name and dynamically instantiates it. In addition, the name is coming from a string input by the user!

There is no way for Dotfuscator to predict which type names the user will enter. The solution is to configure Dotfuscator to exclude the names of all potentially loadable types. Note that method and field renaming can still be performed. This is where manual user configuration plays an important role.

Often the situation is less serious. Consider a slight variation:

C# Code Fragment Variation:

public MyInterface GetNewType() {
   Type type = Type.GetType( GetUserInputString(), true );
   object newInstance = Activator.CreateInstance( type );
   return newInstance as MyInterface;
}

Now it is immediately obvious that only a subset of types need to be excluded: those implementing MyInterface.

Declarative Obfuscation using Custom Attributes

The .NET Framework provides two custom attributes designed to make it easy to automatically obfuscate assemblies without having to set up configuration files. This section outlines how you can use these attributes with Dotfuscator. It is assumed that you are familiar with custom attributes and how to apply them in your development language.

If you are using an earlier version of the .NET Framework that does not have these custom attributes, Dotfuscator ships with a DLL containing compatible attributes.

System.Reflection.ObfuscateAssemblyAttribute

The ObfuscateAssemblyAttribute is used at the assembly level to tell Dotfuscator how to obfuscate the assembly as a whole. This attribute's properties specify whether the assembly is in library mode and whether obfuscation attribute stripping is enabled for the assembly.

Full configuration details for the ObfuscateAssemblyAttribute can be found here.

System.Reflection.ObfuscationAttribute

The ObfuscationAttribute is used on types and their members and has a Feature property that specifies what action Dotfuscator should perform on the annotated code element.

Dotfuscator supports configuring the following features via ObfuscationAttributes:

Multiple features may be configured independently by annotating a single code element with multiple ObfuscationAttributes, each with a different feature property value.

Dotfuscator ignores attributes with feature strings that it does not understand.

Full configuration details for the ObfuscationAttribute can be found here.

Enabling or Disabling Declarative Obfuscation

Dotfuscator allows you to switch Declarative Obfuscation on or off for any or all input assemblies. If not enabled, Dotfuscator completely ignores obfuscation-related custom attributes.

Configure the Honor Obfuscation Attributes setting in the Dotfuscator GUI.

Stripping Declarative Obfuscation Attributes

Dotfuscator can remove the obfuscation attributes when processing is complete, so output assemblies will not contain clues about how they were obfuscated.

Configure the Strip Obfuscation Attributes setting in the Dotfuscator GUI or configure attribute stripping via the StripAfterObfuscation property of the obfuscation-related custom attributes.

Using Feature Map Strings

Dotfuscator allows you to map values contained in the Feature property of an ObfuscationAttribute to feature strings that Dotfuscator understands.

For example, you can annotate your application with ObfuscationAttributes that reference a feature called "testmode". Dotfuscator, by default, does not understand this feature string; therefore, it ignores the attributes. Later, if you want Dotfuscator to use these attributes to configure Renaming and Control Flow obfuscation, then map the feature string "testmode" to Dotfuscator's built-in "renaming" and "controlflow" strings.

Configure the Feature Map Strings on the Settings Tab of the Dotfuscator GUI.

Friend Assemblies

The .NET Framework has the concept of friend assemblies, where an assembly may declare that its internal type definitions are visible to specified other assemblies. This is done using the System.Runtime.CompilerServices.InternalsVisibleToAttribute. Dotfuscator detects the use of this attribute and modifies its renaming and removal rules as described below.

Assume A and B are two assemblies where assembly B references A, and A is marked with InternalsVisibleTo ( B ).

There are a few cases of interest:

  1. A and B are both input assemblies in library mode. If A has no other external, non-input assembly friends, Dotfuscator safely mangles internal names and fixes up the references in assembly B. If A does have other external friends, then internal names are preserved.
  2. A and B are both input assemblies not in library mode. Internal names in A are, by default, mangled and references in B are fixed up, regardless of the existence of other external friend assemblies. As usual in Dotfuscator, names and groups of names can be preserved via manual configuration.
  3. A is an input assembly in library mode, and B is not an input assembly at all. Internal names in A are not mangled in order to not break potential references in B.
  4. A is an input assembly not in library mode, and B is not an input assembly at all. Internal names are mangled, potential references in B are not fixed up since it is not an input assembly. As usual in Dotfuscator, names and groups of names can be preserved via manual configuration. This case would require manual configuration if B actually does reference A’s internals.

Finding External Tools

Dotfuscator uses ildasm and ilasm to process the input assemblies. Ildasm is the MSIL disassembler that ships with the .NET Framework SDK. Ilasm is the MSIL assembler that ships with the .NET Framework Redistributable.

On systems with .NET 1.1 or below, Dotfuscator attempts to match each input assembly with the toolset that ships with the version of the .NET Framework that it was compiled with. If Dotfuscator cannot find the version appropriate toolset for an input assembly, it uses a later version if present. It never uses an older version.

On systems with .NET 2.0 and above, Dotfuscator will use the latest tools even for .NET 1.x assemblies. When building, Dotfuscator passes the appropriate command line arguments to ilasm to ensure that the output assemblies target their correct framework versions.

By default, Dotfuscator searches for these external tools using the following algorithm:

  • Determine the version of the .NET Framework that the input assembly was compiled on.
  • Look at user-specified properties that override the default locations of the tools. You can do this in a tool and version specific way. The following table summarizes by example:
Property Value
ILASM_v1.0.3705 C:\tools\ilasm.exe .NET v1.0.3705 assemblies will use this version of ilasm.
ILDASM_v2.0.50215 C:\tools\ildasm.exe .NET v2.0.50215 assemblies will use this version of ildasm.
ILDASM_v1.1 C:\tools\ildasm.exe .NET v1.1.xxxx assemblies will use this version of ilasm.

Note: These properties are case sensitive.

  • Search the .NET Framework and .NET Framework SDK directories corresponding to the .NET Framework version determined in step 1.
  • Search the .NET Framework and .NET Framework SDK directories corresponding to later versions of the .NET Framework determined in the first step.

If Dotfuscator cannot find one or both of these programs, it issues an error.

Dotfuscator uses the strong naming tool (sn.exe) to automatically resign your strong named assemblies. This tool also ships with the .NET Framework SDK and Dotfuscator searches for it in the same directory as ildasm.

Dotfuscator Version 4.31.0.6091. Copyright © 2017 PreEmptive Solutions, LLC