Demeanor for .NET Enterprise Edition Version 5.0 - Release Notes

These release notes provide the most current information about the installation, features, documentation, and known issues for Demeanor for .NET Enterprise Edition.

1.    Introduction

2.    Installing Demeanor for .NET

3.    Running Demeanor for .NET

4.    Known Issues

5.    Tips and techniques

6.    Version history


1 Introduction

Demeanor for .NET Enterprise Edition is an obfuscation utility for .NET assemblies. It is designed to obfuscate as much information in a .NET assembly as possible while allowing the code to run as the developer intended. Demeanor for .NET is extremely configurable so that you can disable obfuscations at a type, method, field, property, or event level should the need arise. Demeanor can obfuscate symbols using several different techniques allowing you flexibility with regard to the obfuscated names produced.

The Enterprise Edition renames all possible identifiers in a set of assemblies to meaningless, trivial names using a specialized algorithm that produces the maximum possible name overloading. By maximizing the name overloading, the Enterprise Edition produces assemblies that are more difficult to reverse engineer and that are significantly smaller than those obfuscated by the Personal Edition. In addition, the Enterprise Edition contains many additional features that protect your assemblies against reverse engineering such as entire application obfuscation, string encryption, metadata obfuscation and removal, incremental obfuscation, unused code and data detection, automatic satellite assembly obfuscation, and more.

New and non-breaking changed functionality in this release is indicated in the document using green text.

Breaking changes to previously existing functionality is indicated in the document using red text.


2 Installing Demeanor for .NET

2.1 Software Requirements

Demeanor for .NET requires the following:

        An operating system supporting development of .NET applications.

        Version 4.8 or later of the .NET runtime.

Demeanor for .NET can obfuscate assemblies built for .NET 1.0 through 4.8 plus .NET Standard 1.0 and 2.0.

2.2 Recommended Hardware

See the Visual Studio .NET hardware requirements.

2.3 Installing Demeanor for .NET

Before installing Demeanor for .NET, you should already have installed the .NET runtime. As Demeanor for .NET is a tool for .NET developers, we do not include a redistributable copy of the .NET runtime.

Demeanor for .NET no longer contains a product activation feature. Your purchase of a license for Demeanor for .NET Enterprise Edition 5.0 allows the purchaser to run Demeanor on all their personal development systems.

2.4 Removing Demeanor for .NET

To remove Demeanor for .NET, open Control Panel, double-click Add/Remove Programs, and then click the entry for the version of Demeanor that you have installed, then click Remove.

2.5 Running Demeanor for .NET

Demeanor for .NET is a command line utility. The only required command line parameter is the file name of the assembly to obfuscate. The obfuscated version of the assembly, by default, will be placed in a subdirectory of the current working directory named 'Demeanor'.


3 Running Demeanor for .NET

Demeanor for .NET is a command line utility. Run it as follows:

Demeanor <options> <assemblyName>

where the options are listed below and assemblyName is the path to the assembly you wish to obfuscate. The following options are available:

Command line options

Description

/all

Obfuscate as many symbols as possible ignoring the visibility of the type.

Normally top-level types with public visibility cannot be obfuscated because a developer defines such a type with public visibility to allow code in a different assembly to bind to the type. The runtime performs this binding using the type name so changing the name would cause this binding to fail. However, some assemblies, typically .EXE assemblies, expect no external assemblies to bind to their public types. (In fact, Visual Studio.NET prevents such a use of an .EXE assembly though it can be done using the compilers at the command line.) Technically, a developer should define all types contained in such an .EXE assembly with internal visibility but they often do not. You typically use the /all option when obfuscating an .EXE assembly to force all types contained in the assembly to be obfuscated regardless of the type's visibility.

/cc

This option requests Demeanor for .NET to change the accessibility of obfuscated methods and fields to compiler-controlled (a.k.a. privatescope) accessibility. Using compiler-controlled accessibility strengthens the obfuscation of your assembly in three ways.

First, compiler-controlled accessibility is another lossy transformation. The original accessibility of the member is discarded. A decompiler will have to perform a global data flow analysis over the entire assembly in order to determine the proper accessibility of the obfuscated member.

Second, this accessibility does not exist in high-level languages such as C# and VB.NET. A decompiler will have to synthesize (i.e. make up) a new accessibility for such fields and methods. Without a global data flow analysis, a decompiler will have to assume public or internal accessibility for such members.

Third, the runtime never uses the names of such members. Therefore Demeanor for .NET can assign all compiler-controlled members exactly the same name. The more members using the same name, the smaller the metadata tables, and, therefore, the smaller the resulting assembly.

/exclude:<name>[,name>]

This option forces Demeanor for .NET to not obfuscate the specified type or member. Demeanor expects you to specify the type or member name using the ILASM and ILDASM syntax for types and members. For example, when <name> is a top-level type, it should be in the form Namespace.Type. When name is a nested class, it should be in the form Namespace.ContainingType/NestedType and Namespace.ContainingType/NestedType/DoubleNestedType. You can also specify that Demeanor for .NET should not obfuscate a particular member of a type by stating the full name of the member, e.g. Namespace.Type/NestedType::Field and Namespace.Type::Method. Embedded space characters are preserved and are interpreted as part of the type or member name. You can specify multiple names using a comma-separated list. Alternatively, you can specify multiple exclude command line options, each excluding a single name. (Short form /x:)

/help

Display usage information to the console (short form /?).

/insitu

Forces Demeanor to obfuscate the metadata in situ and leave the rest of the portable-executable (PE) file untouched.

Demeanor normally produces an obfuscated assembly that it has optimized in a number of ways. Demeanor rearranges the code and data structures in the PE file more efficiently than those present in the original assembly. This results, on average, in an assembly that is 8% - 12% smaller than the original, which also downloads faster, loads into memory more quickly, and executes using a more efficient working set.

However, when there is unmanaged code and datain an assembly as well as managed code and data, the unmanaged code and data may require that it loads and executes at the original memory location. In this case, Demeanor must not rearrange the memory layout of the assembly. Compilers that produce assemblies containing unmanaged and managed code (for example, Microsoft's Managed C++) normally flag the assembly to indicate that it contains unmanaged code. Demeanor automatically enables the 'in situ' option when it determines it is obfuscating an assembly that contains both managed and unmanaged code. So, typically, you should never need to specify this option.

However, you can use the ILASM compiler to create an assembly that contains unmanaged code without setting the appropriate flag. In this rare situation, you may need to specify the 'in situ' option.

/keycontainer:<string>

Specify a strong name key container.

You can specify the name of the container holding the public and private strong name key pair using this option. Demeanor uses this key container to resign the assembly after obfuscating it. When you specify both the /keycontainer and /keyfile options, Demeanor will use the /keyfile option. Obfuscation modifies an assembly. When the original assembly has a strong name, this modification invalidates any strong name signature in the assembly causing the runtime to treat the assembly as altered. You must resign the obfuscated assembly to update it with the correct strong name signature.

/keyfile:<file>

Specify a strong name key file.

You can specify the name of the file holding the public and private strong name key pair using this option. Demeanor uses this key file to resign the assembly after obfuscating it. When you specify both the /keycontainer and /keyfile options, Demeanor will use the /keyfile option. Obfuscation modifies an assembly. When the original assembly has a strong name, this modification invalidates any strong name signature in the assembly causing the runtime to treat the assembly as altered. You must resign the obfuscated assembly to update it with the correct strong name signature.

/out:<directory>

Directory in which to place the obfuscated assembly. Demeanor for .NET will create the directory when necessary. The directory cannot be the same as the directory containing the input assembly. When you omit this option, the directory name defaults to 'Demeanor' and the utility creates this subdirectory in the current working directory.

/names:alpha

This option forces Demeanor for .NET to use alphabetic Latin characters as the obfuscated names. The first 26 obfuscated names in a scope will be called 'a' through 'z'. The Second 26 names in the same scope will be called 'A' through 'Z'. Then it continues on to produce 'aa', 'Aa', 'aA', 'AA', 'ab', etc. This option produces assemblies with the least number of bytes used for obfuscated names. (This is the default setting.)

/names:numeric

This option forces Demeanor for .NET to use digits for the obfuscated names. The first obfuscated name in a particular scope will be '_1', then '_2', and so on.

/names:Unicode

This option forces Demeanor for .NET to use high Unicode code points for obfuscated names. In particular, Demeanor for .NET creates names using code points from the Canadian Aboriginal Syllabic character set. Without a font for that character set installed, all such names display as a box.

/nologo

Suppress the logo display.

/noenumerations

/noevents

/nofields

/nomethods

/noparameters

/noproperties

/notypes

/noresources

/forceparameters

These diagnostic options allow you to disable all obfuscation of the specified category of types or members. This is useful for diagnosing problems you might encounter when using an obfuscated assembly. For example, if an assembly doesn't work when completely obfuscated but works correctly when you specify the nofields option, it's likely that some logic is late-binding or using Reflection to one or more fields.

Method parameters are normally obfuscated only when the method itself is obfuscated. You can force all parameter names to be obfuscated, for obfuscated and unobfuscated methods, by specifying the /forceparameters command line option. You can prevent all parameters from being obfuscated using the normal /noparameters switch. Note that specifying /noparameters forces the /forceparameters switch off and vice-verse. In other words, only one of these two switches is used and it will be the last one specified on the command line.

/report[:<filename>]

The report option requests Demeanor to create an XML-based obfuscation report file in the output directory. The report lists all types, fields, methods, properties and events in the obfuscated assembly. For each entry, the XML document identifies the original type/member name and metadata values and maps the type/member to its new obfuscated name and metadata values. The default file name is "<assemblyName>Report.xml" in the output directory. (Short form /r[:<filename>]).

The value for a NestedType Name element has changed in this release. Previously, it was the full type name, for example, "a+b+c". Now the value of a NestedType Name element is the simple name of the nested type, for example, "c". The prior qualification was redundant because the element defining the nested type "c" resides in the definition of nested type "b", which itself, resides in the top-level type definition for "a".

/verbose

Display obfuscation statistics to the console (short form /v). This option displays the number of types, field, methods, properties and events contained in each module of the assembly as well as the number obfuscated.

/xr:<regex>

This option forces Demeanor for .NET to not obfuscate all types and members that match the specified regular expression. The regular expression should match type and member names specified using ILASM and ILDASM syntax. See the /x command line option for examples. The regular expression syntax accepted is described in the .NET Framework SDK documentation at: .NET Framework / Programming with the .NET Framework / Working with Base Types / Manipulating Strings / .NET Framework Regular Expressions (ms-help://MS.VSCC/MS.MSDNVS/cpguide/html/cpconcomregularexpressions.htm).

/a:<assemblyName>

This option adds an assembly to the set of assemblies obfuscated when you specify the /application command line option. The assemblyName parameter is the display name of the assembly; it is not the file name. For example, you would specify /a:"MyAssembly, Version= 1.0.0.0, Culture= neutral, PublcKeyToken= 0123456789abcdef" not /a:MyAssembly.dll.

An application that dynamically loads one or more private assemblies (for example, "plug-in" assemblies) based on a configuration file, presence in a well-known directory, or some other dynamic resolution scenario might want to use this obfuscation option. Such an application typically does not have a static reference to all possible plug-in assemblies compiled into the application. Therefore, the /application option wouldn't normally obfuscate such assemblies. You can specify the /a command line option one or more times to instruct Demeanor to obfuscate one or more additional assemblies as part of the whole application obfuscation.

/application

This option requests obfuscation of an entire application. Demeanor begins by loading the assembly that you specify on the command line; typically this is an .EXE assembly. Demeanor obfuscates the application plus all dependent private assemblies that are statically referenced by the specified application, plus their statically referenced dependent private assemblies, and so on. Demeanor uses the .NET Framework assembly resolution rules when locating referenced private assemblies. Demeanor will only obfuscate a referenced assembly when it can be loaded from the application's private assembly path. (In other words, Demeanor doesn't process any shared assembly that the application might reference.)

Demeanor recursively processes private assemblies. For example, when the application assembly listed on the command line references private assembly A, and assembly A references private assembly B, Demeanor for .NET will obfuscate all types and members, public and private, in assembly B. Demeanor then updates the references in A so they correctly reference the obfuscated types and members of B. Demeanor then obfuscates the public and private types and members of A, and similarly updates the references in the application assembly so they correctly reference the obfuscated types and members of A. Demeanor then obfuscates the public and private types in the application.

Demeanor places all obfuscated assemblies in the output directory.

/config:<configFile>

This option requests Demeanor for .NET to read one of its previously written report files and use the information in the file when performing the current obfuscation.

Presently, you use this option to request incremental obfuscation on one or more assemblies. Incremental obfuscation causes Demeanor to assign the same obfuscated names to types and members during multiple obfuscation runs. This allows you to obfuscate a new version of an assembly and having previously existing types and members to be assigned to the same obfuscated names as an earlier version. Client applications that reference the obfuscated types and members will continue to work as the types and members in the new assembly have the same names as before.

When Demeanor assigns an obfuscated name to a type or member, it first checks to see if an obfuscated name for the type/member is present in the specified configuration file. When present, Demeanor assign that obfuscated name, when possible, to the type or member. When not present, Demeanor assigns a new obfuscated name to the type or member. When the prior name isn't acceptable during the current obfuscation run, Demeanor displays a warning message on the console and assigns a new, unique obfuscated name to the type or member. Short form (/c:)

/encryptstrings

This option requests Demeanor for .NET to encrypt the literal strings present in the obfuscated assemblies. The presence of clear-text literals in an assembly can provide significant information to someone attempting to reverse engineer an assembly. For example, she could search for a significant literal string, for example "Invalid password", find the method that references the literal and have a good idea on which method to concentrate.

Demeanor for .NET also injects the appropriate decryption code into all methods that reference a string literal. This additional code increases the size of your methods and causes them to run slightly more slowly. (Short form /es)

/flow:<level>

This option requests Demeanor for .NET to obfuscate the control flow of the methods in the obfuscated assemblies. The <level> parameter can be 'None', 'Minimum', 'Moderate' and 'Maximum'. The default value is 'None'. Increasing the level of control flow obfuscation produces more difficult to understand methods. However Demeanor injects additional instructions into your methods to obfuscate the code. These additional instructions increase the size of your methods and causes the methods to run more slowly. (Short form /f:<level>)

/noserializable

When you specify this option, Demeanor does not obfuscate the name of all types to which you have application the [Serializable] attribute. Additionally, Demeanor does not obfuscate the name of any of the fields in such types. However, Demeanor will continue to obfuscate the name of any methods in such types, as appropriate.

When you serialize a type, the .NET runtime writes the name of the type and the names of each of the serialized fields to the output stream. When you obfuscate such types, the output stream contains the obfuscated type and field names. When you want an unobfuscated build and an obfuscated build of your assembly, for example, a Debug and a Release build, to be able to exchange serialized types, it is necessary to use consistent names in the serialized data stream. While you can achieve this using incremental obfuscation and configuration files, this option makes the process much easier. (Short form /noserial)

/hinderreflection[+|-]

By default, Demeanor injects additional constructs into an obfuscated assembly that increase the difficulty of decompiling the assembly. The /hinderreflection- option prevents Demeanor from injecting these additional constructs.

/prefix:<value>

The /prefix:<value> option allows you to specify a namespace prefix that Demeanor will prepend to each obfuscated top-level type name. For example, when you specify the -prefix:WiseOwl command line option, the top-level obfuscated type names will be, by default, WiseOwl.aWiseOwl.bWiseOwl.c and so on.

/satelliteassemblies[:<culture>[,<culture>]*]

The satelliteassemblies option request Demeanor to update the names of managed resources in culture-specific satellite resource assemblies. You can specify the exact culture-specific satellite assemblies to update using this command line option multiple times or by listing a comma-separated list of cultures on a single command line option.
(Short form /sa:[<culture>[,<culture>]*])

/suppressildasm[+|-]

By default, Demeanor injects additional metadata into an obfuscated assembly that suppresses decompilation of the assembly by ILDasm. The /suppressildasm- option prevents Demeanor from injecting this additional metadata, which allows decompilation by ILDasm.

/xa:<assemblyName>

This option excludes an assembly from the set of assemblies obfuscated when you specify the /application command line option. The assemblyName parameter is the display name of the assembly; it is not the file name. For example, you would specify /a:"MyAssembly, Version=1.0.0.0, Culture=neutral, PublcKeyToken=0123456789abcdef" not /a:MyAssembly.dll.


4 Known Issues

4.1 None

There are no known issues with the latest version of Demeanor for .NET. However, we continually upgrade our product so check back occasionally to review the latest release notes.

 

 5 Tips and techniques

5.1 The ResourceManager, Reflection and managed resources

Some parts of the .NET Framework Class Libraries use Reflection to determine the name of a type at runtime. They then use the name in various ways to locate other data. When such code uses a type that has been obfuscated, it may not be able to find the other data it needs when the code locates the other data by comparing names.

For example, the Visual Studio.NET Form Designer generates the following code in the InitializeComponent method.

System.Resources.ResourceManager resources = new System.Resources.ResourceManager (typeof(MainForm));

This ResourceManager constructor usage tells the resource manager to infer the assembly, the base name, and a namespace for .resources files from the constructor's Type argument. The ResourceManager assumes you will be using satellite assemblies and want to use the default ResourceSet class. Given a full type name such as MyCompany.MyProduct.MainForm, the ResourceManager will look for a .resources file (in the main assembly and satellite assemblies) named "MyCompany.MyProduct.MainForm.[culture name.]resources" in the assembly that defines MainForm. However, once Demeanor obfuscates the name of the type, for example, to 'Aa', the resource manager will look for a .resources file (in the main assembly and satellite assemblies) named "Aa.[culture name.]resources" in the assembly that defines MainForm.

Demeanor looks for managed resources in an obfuscated assembly that have the same name as an obfuscated class name with the addition of the ".resources" suffix. When it finds such a resource, Demeanor changes the name of the resource to be the obfuscated class name plus a ".[culture name.]resources" suffix. This means that the ResourceManager's previously described behavior continues to work correctly on an obfuscated class and its resource.

Demeanor, by default, does not try and update the names of resources in culture-specific satellite assemblies. Unfortunately for Demeanor (but fortunately for you as a developer), there is no information in a primary assembly that describes what, if any, satellite assemblies exist that are associated with the primary assembly. This is good for you as a developer because it allows you to create additional satellite resource assemblies, deploy them into the appropriate culture-specific subdirectory of the primary assembly's directory, and the Runtime will automatically use the new satellite assembly as required -- all without any need to recompile or redeploy the primary assembly.

However, you can request that Demeanor update all culture-specific satellite resource assemblies by using the -satelliteassemblies command line option. For example, the following command line requests Demeanor obfuscate the specified assembly, then find all installed culture-specific satellite resource assemblies and update the names of their resources.

Demeanor -sa MyAssembly.exe

In the above example, Demeanor would update the French satellite resource assembly (fr\MyAssembly.resources.dll) and the German satellite resource assembly (de\MyAssembly.resources.dll) if they are present (specified relative to the location of the MyAssembly.exe file) and place the obfuscated satellite resource assemblies in the same subdirectory relative to the Demeanor output directory. In this example, the output directory defaults to "Demeanor" so the three obfuscated assemblies would be placed in "Demeanor\MyAssembly.exe", "Demeanor\fr\MyAssembly.resources.dll" and "Demeanor\de\MyAssembly.dll".

Because there is no information in an assembly that describes which cultures the assembly supports, when you specify the -satelliteassemblies command line option without any cultures, Demeanor searches for all 202 possible culture-specific satellite resource assemblies. When you additionally request entire application obfuscation using the -app command line option, Demeanor search for each of the 202 possible culture-specific satellite resource assemblies for each assembly in the entire application. There are, by default, four locations in which a satellite resource assembly can be placed. This is a lot of searching for assemblies that frequently are not present. However, it does make Demeanor easy to use and it automatically finds and obfuscates new culture-specific resource assemblies as you create them.

However, if you have a fixed, known set of cultures that you support, you can have Demeanor more efficiently look for and obfuscate only the satellite resource assemblies for the specific cultures. For example, the following two command lines are effectively identical and each requests Demeanor to obfuscate the specified assembly plus its French and German satellite resource assemblies.

Demeanor -sa:fr -sa:de MyAssembly.exe

Demeanor -sa:fr,de MyAssembly.exe

You can theorectically have a resource that has a name that matches an obfuscated class name without there being any implied relationship between the resource and the class. In this case, you may want to disable Demeanor's automatic renaming of the resource. You can globally disable this automatic resource renaming using the -noresources command line option. Alternatively, you can specify the name of the resource as an item to exclude using either the /x or /xr command line options.

 5.2 Examples of regular expression exclusions

You can specify the /xr (exclude using regular expression) switch as often as needed on the command line to specify multiple exclusion patterns.

The following regular expression excludes the type "Namespace.Type" as well as the members of the type:

/xr:Namespace\.Type(::.+)?

The following regular expression does not exclude the type itself but does exclude all members of the type "Namespace.Type".

/xr:Namespace\.Type::.+

This regular expression excludes the type itself but does not exclude all members of the type "Namespace.Type". Of course, in this case, it's easier just to use the exclude (-x) command line switch.

/xr:Namespace\.Type$

Demeanor supports the regular expression syntax used by the classes in the System.Text.RegularExpressions namespace.

5.3 Examples of entire application obfuscation

You can specify the /app command line option to request Demeanor to obfuscate the assembly specified on the command line (called the root assembly) plus all of its dependent assemblies. Demeanor searches the root assembly's private path and only obfuscates the dependent assemblies that reside on the private path. Typically, this means Demeanor obfuscates the root assembly plus any dependent assemblies located in the same directory as the root assembly.

For example, consider the case where assembly Client.exe requires private assembly MyLib.dll and both files reside in the same directory.

The following command line obfuscates the public and internal symbols in MyLib.dll, changes Client.exe to reference the public types in MyLib.dll using their new obfuscated names, then obfuscates the internal types of Client.exe leaving its public types unchanged.

Demeanor -app Client.exe

Normally, Demeanor will not obfuscate the public types of an assembly unless it can also modify all references to those public types to use the new, obfuscated type names. This means the public types of the root assembly never get obfuscated by default, as there may exist other assemblies that reference the public types in the root assembly. Typically, you will not have any references to the public types in an .exe assembly so you can modify the above example to obfuscate all types in both assemblies using the following command line. The -all option requests Demeanor to obfuscate the public types of the specified root assembly.

Demeanor -app -all Client.exe

Some applications dynamically reference assemblies. For example, an application could read a configuration file and dynamically load one or more optional "plug-in" assemblies. During entire application obfuscation, Demeanor creates a list of all assemblies statically referenced by the root assembly. Demeanor then recursively adds to the list all assemblies referenced by assemblies in the list that are present in the private path. In other words, Demeanor creates a list of all statically referenced assemblies used directly or indirectly by the root assembly and obfuscates the ones present in the private path. You can add additional dynamically referenced assemblies to this list to have them obfuscated as part of the entire application obfuscation processing.

Here is one example where this is useful. Your application dynamically loads "plug-in" assemblies. Your application statically references an interface assembly. The plug-in assembly also statically references the interface assembly. the application never statically references any types in the plug-in assembly. Entire application obfuscation of your client will obfuscate the client and the interface assembly. However the plug-in assembly references types in the interface assembly that have been obfsucated. You need the plug-in assembly to be updated to use the new obfuscated type names and for the plug-in assembly itself to be obfuscated. Use the -a command line option to add the plug-in assembly to the set of assemblies processed by entire application obfuscation. For example, to include the assembly Plugin.dll, use its display name like this:

Demeanor -app -all -a:"Plugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken= 0123456789abcdef" Client.exe

Demeanor always needs to load all assemblies referenced directly or indirectly from every assembly that it obfuscates. However, when Demeanor loads assemblies, it places the assembly into one of two categories: assemblies to be obfuscated and assemblies used for reference only. Demeanor always places the assembly that you specify on the command line into the "to be obfuscated" category. In addition, Demeanor also places all assemblies that you specify with the /a command line option into the "to be obfuscated" category. Additionally, when Demeanor asks the runtime to load an assembly and the assembly loads from the same directory as the assembly that you specify on the command line (or one of its subdirectories), Demeanor adds the assembly to the "to be obfuscated" category.

Demeanor assumes that assemblies that load from the private path are part of the application, therefore should be obfuscated during entire application obfuscation. Conversely, Demeanor places all assemblies that load from outside of the private path, for example, from the Global Assembly Cache, or elsewhere due to a codebase assembly redirect, into the "for reference only" category and doesn't obfuscate them. Usually, this is exactly the behavior you want. Demeanor obfuscates your application assemblies but doesn't change mscorlibSystem.Windows.Forms, or similar assemblies.

Occasionally, you may have an assembly in the private path but not want Demeanor to obfuscate it. A common example is when your application uses a third-party strong-named assembly that you deploy privately with your application. You cannot include the assembly in entire application obfuscation because obfuscating it invalidates its strong name. In this case, you can specify the /xa command line option. Demeanor will then categorize this assembly as one to be used "for reference only" and therefore not obfuscate it.

Demeanor -app -all -a:"Plugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef"

                   -xa:"ThirdParty, Version=2.3.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef" Client.exe

 5.4 Examples of string encryption obfuscation

The first technique used by someone attempting to reverse engineer your application is to look at the string literals of the program, find an interesting string, locate the code that uses the string and concentrate on that code. Demeanor allows your to make the search for interesting string literals more difficult by encrypting the string literals in the obfuscated assemblies. You can specify the /es command line option to request Demeanor to encrypt the literal strings in the obfuscated assemblies.

Demeanor -es -app -all Client.exe

 5.5 Examples of incremental obfuscation

Specify a report file from a prior Demeanor obfuscation run as the input to a subsequent obfuscation run and Demeanor will assign types and members the same obfuscated name as they had in the prior run.

Demeanor -c:MyAssembly.xml MyAssembly.dll

You can also use this feature to control exactly what names are assigned to each type and member in each assembly in the obfuscation. However, there can be cases where Demeanor cannot assign the requested name to a type or member. Here's exactly how this feature works.

        Demeanor determines all types and members that cannot be obfuscated. For example, normally public and protected types and members are not obfuscated. Excluded types and members are also not obfuscated.

        After Demeanor determines the names that will not be changed, it begins to rename the remaining types and members. For each one, it checks the configuration file to see if the type/member has a preferred name. When a preferred name is present in the configuration file, Demeanor renames the type/member to that preferred name when possible. When the preferred name isn't available, for example, when a non-obfuscatable type/member in the same scope already has the preferred name, Demeanor will issue a warning and will assign a new obfuscated name to the type/member.

        Finally, Demeanor assigns all remaining obfuscatable types and members a new, meaningless name.


In addition to incremental obfuscation, you can also use this feature to assign any desired name to any type or member, within the above constraints. For example, you can specify the original type/member name as the obfuscated name. In effect, this says "Do not obfuscate this name" and is equivalent to excluding the symbol from obfuscation.

 5.7 The -noserializable option

When you use the .NET Runtime's binary serialization functionality, the runtime writes the serialized state of an object to a stream. This information includes the type name of the serialized instance plus the name of each of the serialized fields and their values. .NET determines the type name and the field names at runtime, therefore when serializing an obfuscated type, the names saved to the stream are the obfuscated names. Some developers want to serialize a non-obfuscated type instance and deserialize the data into an obfuscated version of the same type, or vice-versa. Normally this doesn't work because the serialized type name and serialized field names aren't identical in the obfuscated and unobfuscated code. It's easy to work around this issue with the field names. You simply implement the ISerializable interface on the type and you have full control over the serialized field names. However, this still leaves the type name itself as a problem.

You can exclude a type name from obfuscation but, when you have numerous serializable types, this can become tedious. The -noserializable option requests Demeanor to never obfuscate the name of any type with the [Serializable] attribute. In addition, Demeanor will not obfuscate any serializable field of a serializable type. Demeanor obfuscates all other members of a serializable type normally. For example, given the follow class:

[Serializable]

internal class MyClass {

  private string m_MyField;

 

  [NonSerialized] private int m_NotSerialized;

 

  public string MyField {

    get { return m_MyField; }

  }

 

  public void Method1 (string s) { ... }

  private int Method2 (string s) { ... }

}

When you specify the -noserializable option and obfuscate this class, Demeanor translates it to the following:

[Serializable]

internal class MyClass {

  private string m_MyField;

 

  [NonSerialized] private int a;

 

  public string a() { ... }

 

  public void b(string) { ... }

  private int c(string) { ... }

}

However, when you do not specify the -noserializable option and obfuscate this class, Demeanor translates it to this instead:

[Serializable]

internal class a {

  private string a;

 

  [NonSerialized] private int b;

  public string a() { ... }

 

  public void b(string ) { ... }

  private int c(string ) { ... }

}

 

5.8 Obfuscating smart device applications that use the .NET Compact Framework

Demeanor no longer supports obfuscating .NET Compact Framework assemblies.

5.9 The System.Reflection.ObfusactionAttribute class

With the addition of the System.Reflection.ObfuscationAttribute type in .NET 2.0 , there is no longer any necessity for Demeanor for .NET's equivalent custom attribute - WiseOwl.ObfuscationAttribute. All features previously supported by the WiseOwl.ObfuscationAttribute are now available on the standard system attribute. Click here to view documentation on the System.Reflection.ObfuscationAttribute.

You have two alternatives when you need to control exactly which types and members that Demeanor for .NET obfuscates. You can specify exclusions via the command line/configuration file. You can also add such specifications directly to your source files by adding the System.Reflection.ObfuscationAttribute to a type or member. To this attribute, add the custom attribute as shown in the following examples. Note: while most examples show the attribute used on a type definition, you can also apply the attribute to a member of a type (field, method, property, event, and nested type).

For example, Demeanor for .NET normally obfuscates the name of an internal top-level type. You can prevent this default behavior by applying an instance of the System.Reflection.ObfusactionAttribute to the type. The System.Reflection.ObfusactionAttribute instance contains a property named Exclude that has a default value of trueTherefore applying the attribute to a type or member without setting this property causes Demeanor for .NET to exclude the type or member from obfuscation.

[System.Reflection.ObfuscationAttribute ()]

internal class MyClass {

 .

  .

  .

}

Exclude property

As one example, Demeanor will not, by default, obfuscate the name of an public top-level type. You can, however, request that Demeanor obfuscate such a type name by adding a System.Reflection.ObfuscationAttribute with its Exclude property set to false.

[System.Reflection.ObfuscationAttribute (Exclude=false)]

public class MyClass {

  .

  .

  .

}

ApplyToMembers property

By default, the System.Reflection.ObfuscationAttribute attribute applies only to the type or member that you decorate with the attribute. When you decorate a type with the attribute, you can set the ApplyToMembers property to true to specify that the Exclude property setting should apply not only to the type, but also to all members (fields, methods, properties and events) of the type. The following example requests that Demeanor not obfuscate the type and all of its fields, methods, properties and events.

[System.Reflection.ObfuscationAttribute (Exclude=true, ApplyToMembers=true)]

public class MyClass {

  .

  .

  .

}

StripAfterObfuscation property

Demeanor for .NET normally strips all System.Reflection.ObfuscationAttribute instances from an obfuscated assembly as they are not normally needed at runtime. However, you can request that Demeanor not remove a particular ObfuscationAttribute by setting the StripAfterObfuscation property to false. The default value is true.

[ObfuscationAttribute (Exclude=false, StripAfterObfuscation=false)]

internal void MyMethod CalculateRate {double interestRate) {

}

Features

The System.Reflection.ObfuscationAttribute has a string property called Feature. An obfuscator can parse the Feature string to allow you to enable custom features of that obfuscator. Demeanor for .NET supports the following features: ObfuscatedNamePreserveLiteralFieldsPreserveLiteralValues, and PreserveParameters.

ObfuscatedName feature

You can specify the string to use as the obfuscated name of the type. Note that when you specify the obfuscated name, it is your responsibility to insure that the specified name will be unique within the appropriate scope.

[System.Reflection.ObfuscationAttribute (Exclude=false, Feature="ObfuscatedName=Foo")]

public class MyClass {

  .

  .

  .

}

PreserveLiteralFields feature

When Demeanor obfuscates an enumerated type, it normally deletes all enumerated names and values from the enumeration declaration. For example,

internal enum Color {

  Red = 1,

  Green = 3,

  Blue = 5,

}

becomes

internal enum a {

}

This increases the difficulty of reverse engineering the code. However, when your code uses the System.Enum static methods to convert an enumerated value into a string, or parse a string into an enumerated value, or check if a value is defined, previously you had to exclude the enumerated type from obfuscation so Demeanor did not remove the fields of the enumeration. You can use the System.Reflection.ObfuscationAttribute to enable obfuscation for an enumerated type, but also specify that Demeanor for .NET should preserve the enumerated value names. The default setting of the PreserveLiteralFields feature is false but specifying the feature changes the setting to true. For example,

[System.Reflection.ObfuscationAttribute (Exclude=false, Feature="PreserveLiteralFields")]

internal enum Color {

    Red = 1,

    Green = 3,

    Blue = 5,

}

after obfuscation becomes

internal enum a {

    Red = 1,

    Green = 3,

    Blue = 5,

}

You can explicitly specify the true/false setting of the feature:

[System.Reflection.ObfuscationAttribute (Exclude=false, Feature="PreserveLiteralFields=true")]

internal enum Color {

  Red = 1,

  Green = 3,

  Blue = 5,

}

PreserveLiteralValues feature

When Demeanor obfuscates an enumerated type, it normally deletes all enumerated names and values from the enumeration declaration. For example,

internal enum Color {

  Red = 1,

  Green = 3,

  Blue = 5,

}

becomes

internal enum a {

}

This increases the difficulty of reverse engineering the code. However, this can cause your code to fail unexpectedly when your code uses the System.Enum static methods to check if a value is defined. These static methods inspect the definition of the enumeration to determine if the enumerated type defines the value. Previously, to enable such validation code to work, you had to exclude the enumerated type from obfuscation so Demeanor did not remove the fields and values of the enumeration. However, you can use the System.Reflection.ObfuscationAttribute to enable obfuscation for an enumerated type, and obfuscate the names of the symbols, but also specify that Demeanor for .NET should preserve the associated enumerated values. The default setting of the PreserveLiteralValues feature is false but specifying the feature changes the setting to true. For example,

[System.Reflection.ObfuscationAttribute (Exclude=false, Feature="PreserveLiteralValues")]

internal enum Color {

  Red = 1,

  Green = 3,

  Blue = 5,

}

after obfuscation becomes

internal enum a {

    a = 1,

    b = 3,

    c = 5,

}

You can explicitly specify the true/false setting of the feature:

[System.Reflection.ObfuscationAttribute (Exclude=false, Feature="PreserveLiteralValues=true")]

internal enum Color {

  Red = 1,

  Green = 3,

  Blue = 5,

}

When you specify both PreserveLiteralFields and PreserveLiteralValues, Demeanor for .NET uses the PreserveLiteralFields setting.

PreserveParameters feature

When Demeanor for .NET obfuscates a method, by default, Demeanor also obfuscates the names of the parameters to that method. In very unusual scenarios, you may wish to obfuscate the name of the method but preserve the names of the parameters to that method. You can set the PreserveParameters feature to true to request that Demeanor for .NET not obfuscate the parameter names. The default setting of the PreserveParameters feature is false but specifying the feature changes the setting to true.

[System.Reflection.ObfuscationAttribute (Exclude=false, Feature="PreserveParameters")]

internal void MyMethod CalculateRate {double interestRate) {

}

You can set multiple features at the same time on an attribute in the expected way:

[ObfuscationAttribute (Exclude=false, Feature="ObfuscatedName=q, PreserveParameters=true")]

internal void MyMethod CalculateRate {double interestRate) {

}

 

5.10 The /prefix:<value> command line option

Normally Demeanor obfuscates a top-level type name by removing any namespace qualification and replacing the original name with a short meaningless name such as 'a', 'b', 'c' and so on. This technique maximally reduces the size of the obfuscated assembly and removes semantics that expose the original namespace organization of the types. Unfortunately, the Microsoft C# compiler cannot distinguish between two identically named types that reside in different assemblies. (The IL assembler can distinguish such types for those extremely adventurous developers.) For example, if you create a type called 'WiseOwl.Foo' in assembly WiseOwl.dll and another vendor creates a type called 'WiseOwl.Foo' in assembly FlyByNight.dll, a client application cannot reference both assemblies during a single compilation and use either of the two 'WiseOwl.Foo' types.

Normally, you don't run into the situation because developers often use namespaces and generally create unique type names - but there are no guarantees. Similarly, if two vendors each ship an assembly containing a top-level type called 'GreateGizmo' (notice the lack of a namespace qualification), then a client application cannot reference both vendor's assemblies even if the application only needs to use only one of the 'GreatGizmo' types.

Obfuscation exacerbates this problem. By default, an obfuscated assembly contains top-level types named 'a', 'b', 'c' and so on. Two obfuscated assemblies, even from different vendors, may easily have identically named types called "a', 'b', 'c' and so on. The Microsoft C# compiler cannot distinguish two types called 'a' in different assemblies any more than it can type types called GreateGizmo.

 

5.11 Microsoft Visual Studio Integration

Microsoft has removed support for the Visual Studio integration used by Demeanor for .NET, Enterprise Edition. You will need to setup a Post Build step in your Visual Studio project to run Demeanor.


 

5.12 MSBuild Obfuscation Task

Demeanor for .NET, Enterprise Edition installs a build task for the .NET Framework MSBuild utility. Here is a sample project file that invokes the Obfuscation task.

 

<?xml version="1.0" encoding="utf-8" ?>

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003 ">

  <UsingTask TaskName="Wiseowl.Build.Tasks.Obfuscate" AssemblyFile="$(MSBuildExtensionsPath)\Wise Owl Consulting LLC\Demeanor for .NET Enterprise Edition\v5.0\WiseOwl.Build.Tasks.dll"/>

  <Target Name="MyTarget">

    <Obfuscate RootAssembly="C:\Src\MiniApp\obj\Release\MiniApp.exe"

      All="false"

      CompilerControlled="false"

      EncryptStrings="true"

      EntireApplication="false"

      ForceParameters="false"

      Insitu="false"

      NoEnumerations="false"

      NoEvents="false"

      NoFields="false"

      NoLogo="false"

      NoMethods="false"

      NoParameters="false"

      NoProperties="false"

      NoResources="false"

      NoSerializable="false"

      NoTypes="false"

      OverloadOnStatic="false"

      Verbose="true"

      Names="Alpha"

      Exclude="SomeClass"

      ExcludeRegEx="BadClass*"

      Prefix="WiseOwl"

      Flow="Minimum"

      HinderReflection="true"

      SuppressIldasm="true"

      KeyFile="C:\SomeDir\PublicPrivate.snk"

      KeyContainer="KeyContainerName"

      AdditionalAssemblies="MyAssembly, Version= 1.0.0.0, Culture= neutral, PublcKeyToken= 0123456789abcdef"

      ExcludedAssemblies="OtherAssembly, Version= 1.0.0.0, Culture= neutral, PublcKeyToken= 0123456789abcdef"

      Config="MyConfig.xml"

      Report="MyReport.xml" />

  </Target>

</Project>

 

6.0 Version history.

 

5.0.4054.0

Fix 'Unexpected TYPESPEC coded index' error. The problematic TYPESPEC is now permitted.

5.0.4055.0

Network Edition: Fix problem with network license server reaper thread failing to reclaim expired licenses occasionally.

5.0.4123.0

Enhance Demeanor to robustly operate on assemblies obfuscated by tools that produce invalid assemblies. You can run PEVerify on such assemblies and will see errors such as [MD]: Error (Structural): Table=0x00000001, Col=0x00000000, Row=0x00000159, has coded rid out of range.

5.0.4364.0

Further enhance Demeanor to robustly operate on assemblies obfuscated by tools that produce invalid assemblies.

Fix a bug where Demeanor only honored the assembly resigning option when the -verbose option was also requested. Demeanor now will resign assemblies, if requested, without -verbose enabled.

5.0.4366.0

Added more anti-Reflection features to Demeanor obfuscated assemblies.

5.0.4410.0

Added the /hinderreflection and /suppressildasm command line options.

5.0.4459.0

Update the schema parsed by the StackDecode utility to support the /hinderreflection and /suppressildasm command line options.

5.0.4573.0

Update the licensing component to support additional licensing features.

5.0.4774.0

Added Visual Studio 2012 integration.

5.0.5385.0

Added Visual Studio 2013 integration.

5.0.5466.0

Corrected an error during processing of a mixed IL & native code 64-bit assembly.

5.0.5565.0

Significant performance improvement during obfuscation of very large assemblies.

5.0.7058.0

Significant change so that Demeanor no longer needs to transitively load all assemblies potentially used, directly or indirectly, at runtime by the assembly being obfuscated. Demeanor now only loads assemblies directly and statically referenced by the assembly being obfuscated. This significantly reduces the time to obfuscated a binary.

5.0.7077.0

Added -unregister command line option to enable moving a Demeanor installation to a different computer.

5.0.8493.0

Add support for .NET Standard assemblies


Did you find this information useful? Please send your suggestions and comments about the documentation to support@wiseowl.com.