These release notes provide the most current information about the installation, features, documentation, and known issues for Demeanor for .NET, Personal Edition and Demeanor for .NET, Enterprise edition.
1 Introduction |
|---|
These release notes provide important information that you should read before installing and using Demeanor for .NET.
Demeanor for .NET 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 a number of different techniques allowing you some flexibility with regard to the obfuscated names produced.
Wise Owl provides two versions of Demeanor for .NET - the Personal Edition and the Enterprise Edition.
Demeanor for .NET, Personal Edition is a free version of our obfuscator that contains a subset of the complete features present in the Enterprise Edition. The personal edition renames all possible identifiers in a single assembly to meaningless, trivial names. This results in an assembly that is much harder to reverse engineer as well as an assembly that is smaller in size. This size reduction makes the assembly more efficient in memory and on disk. In addition, the Personal Edition Features supported by Demeanor for .NET, Personal Edition are indicated by the following symbol <P>.
Demeanor for .NET, Enterprise Edition is the commercial version of our obfuscator that contains all the features of the Personal Edition plus numerous additional features to defeat reverse engineering. The Personal Edition renames all possible identifiers in a set of assemblies to meaningless, trivial names using a .NET-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 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 |
|---|
Demeanor for .NET requires the following:
See the Visual Studio .NET hardware requirements.
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.
Inserting the CD-ROM into a drive with Autorun enabled will automatically start the Setup program. When Autorun is disabled, navigate to the root directory of the CD-ROM drive and run the SETUP.EXE application to begin installation of Demeanor for .NET.
Demeanor for .NET contains a product activation feature. The first time you run Demeanor for .NET, it displays a window requesting that you activate your specific license for Demeanor for .NET. Activating a license on a system associates that license to that specific machine. You can activate the license immediately if your system has an Internet connection, otherwise you can activate your license by phone of e-mail. You can decline to activate the license and Demeanor for .NET will run using an unactivated license as long as you are within the two week grace period for activation. After the two week grace period, you must activate the license on a particular system in order to use 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.
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'.
Beta and trial versions of Demeanor for .NET will stop working once their evaluation period has expired. Licensed copies of Demeanor for .NET contain no time bomb. Note that Demeanor for .NET is licensed only to a specific developer within an organization. Licenses are not transferable. You need one license for each developer that uses Demeanor for .NET.
Demeanor for .NET, Enterprise Edition is the commercial, full-featured version of the Demeanor for .NET obfuscator. It is available from Wise Owl, Inc at http://www.wiseowl.com. Wise Owl has licensed an introductory, feature restricted version of Demeanor for .NET, called Demeanor for .NET, Personal Edition, to Borland for inclusion with certain of their .NET software development products. Owners of any Borland product that ships with Demeanor for .NET, Personal Edition may purchase a license for the current shipping version of Demeanor for .NET, Enterprise Edition at 50% off the current retail price. This offer may be discontinued at any time and may not be combined with any other offers or discounts.
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 in both the personal and enterprise editions.
| 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. |
| /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). |
| Enterprise Edition Additional Options | Enterprise Edition Only Command Line Options Description |
| /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) |
|
/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) |
| /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.a, WiseOwl.b, WiseOwl.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. |
| /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 |
|---|
When you specify the /cc command line option to Demeanor for .NET, it changes the accessibility of most fields and methods to 'compilercontrolled'.
Unfortunately, when we used this option in earlier releases of Demeanor for .NET itself, we discovered a bug in version 1.0.3300.0 of the .NET runtime Just-In-Time compiler. In very rare cases, running code obfuscated with this option, in a release build, while not under a debugger, produces a System.InvalidProgramException from the runtime when it is JIT-ting an obfuscated type constructor. Microsoft has reproduced this bug and it is fixed in the .NET 1.1 release of the runtime. In the meantime, should you use this option, be sure to test a release build of the obfuscated assembly and don't run the tests under a debugger.
In addition, compilercontrolled accessibility is incompatible with classes that directly or indirectly derive from MarshalByRefObject when you reference an instance of the class via a remoting proxy across an AppDomain boundary. Unfortunately, there is no way, in all cases, for Demeanor to determine that a class indirectly derives from MarshalByRefObject. (Demeanor doesn't necessarily have access to all assemblies containing the type definitions in an inheritance chain.)
Since the above was written, Wise Owl has uncovered two more bugs in version 1.1 of the .NET Runtime related to compilercontrolled accessibility used on fields and methods. These bugs are still present as of .NET service pack 2. We no longer encourage you to use this option. We wanted to let you know about potential problems in order to save you from hours of debugging should you encounter a problem.
5 Tips and techniques |
|---|
Some parts of the .NET Framework Class Libraries use Reflection to determine the name of a type at runtime. They then use the name is 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.
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.
Demeanor for .NET, Enterprise Edition feature.
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 mscorlib, System.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
Demeanor for .NET, Enterprise Edition feature.
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
Demeanor for .NET, Enterprise Edition feature.
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 for .NET, Enterprise Edition feature.
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 ) { ... }
}
When Demeanor obfuscates an assembly/application, Demeanor establishes a runtime environment exactly like the assembly/applicaiton would have when it loads/runs normally. Demeanor then determines the dependent assemblies used by the application, and asks the .NET Framework to load each one. Demeanor operates this way for two reasons. First, Demeanor must read each dependent assembly in order to understand every data type used directly or indirectly by an assembly that Demeanor obfuscates. Second, it must ask the .NET Runtime to load the assembly, rather than load a particular file, because the .NET Runtime occasionally loads a different assembly than the one your application requests. Obfuscators that work based on file names fail to handle this assembly redirection properly, which also means they cannot obfuscate your applications correctly.
When obfuscating a Win32 application, the runtime typically finds these dependent assemblies without problem. Your dependent priovate assemblies reside in the same directory as the application itself, so the runtime can find them. System assemblies such as mscorlib and System.Windows.Forms reside in the Global Assembly Cache (GAC) therefore the runtime can also find those assemblies. However, when you build a smart device .NET application, the compiler compiles against special versions of many of the standard .NET Runtime assemblies. These assemblies have a different strong name than the Win32 .NET Framework assemblies. When Demeanor asks the .NET Runtime to load these assemblies, the .NET Runtime cannot find the assemblies as they aren't present in the GAC, which is understandable as they normally aren't used by applications running on Windows.
On a development machine, these .NET Compact Framework assemblies normally reside in a Visual Studio .NET 2003 installation directory, typically C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE. This allows you to compile against these assemblies but they aren't normally considered when the .NET Runtime needs to load one of the assemblies. For Demeanor to obfuscate a smart device application, you must create an application configuration file that informs the .NET runtime of the location of each of the assemblies in the Windows CE directory. Then, when Demeanor obfuscates your application and it asks the .NET Runtime to load the dependent assemblies, the application configuration file will tell the .NET Runtime where to find those assemblies.
Demeanor supplies an application configuration file called "SmartDeviceApp.exe.config" in Demeanor's installation directory. Copy this file to your smart device application's directory and rename the file to agree with the name of your application. For example, when your application is named MyApp.exe you should name the configuration file MyApp.exe.config and it must reside in the same directory as MyApp.exe. By default, this application configuration file redirects references to any of the Compact Framework assemblies to the their default installation directory. If you have installed Visual Studio .NET 2003 to a different location than its default location, you'll need to edit the application configuration file so that the href attribute of the codeBase elements point to the proper directory.
Demeanor for .NET, Enterprise Edition feature.
In versions of Demeanor prior to 3.0.1507.0, the input configuration and output report files expected and placed, respectively, the ManagedResources element as the last element in the definition of the Assembly element. Managed resources are more correctly modeled as a component of each module in an assembly. Demeanor versions 3.0.1507.0 and later place the ManagedResurces element as the last element in the definition of the Module element. The ConfigurationAndReportFileSchema.xsd file in the installation directory always contains the current schema for the configuration and report files.
In order to continue using existing configuration and report files, you should edit those files and delete the existing ManagedResources element and its children. This will cause no side effects as prior versions of Demeanor did not use the ManagedResources element from the configuration file during incremental obfuscation. Current and future versions do process each Module's ManagedResources definition in the configuration file allowing you to control the exact renaming of each managed resource.
Demeanor for .NET, Enterprise Edition feature.
You now have two alternatives when you need to control exactly which types and members are obfuscated. You can specify exclusions via the command line/configuration file. You can also add such specifications directly to your source files by adding the WiseOwl.ObfuscationAttribute to a type or member and requesting that Demeanor obfuscate it or not. The WiseOwl.ObfuscationAttribute custom attribute type definition resides in the WiseOwl.Obfuscation assembly which resides in the installation directory. To use the custom attribute, add a reference from your project to the WiseOwl.Obfuscation.dll assembly. Then add the custom attribute as shown in the following examples. Note: while the 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 normally obfuscates the name of an internal top-level type. You can prevent this by applying to the type an instance of the the WiseOwl.ObfusactionAttribute with the required shouldObfuscate constructor argument set to false.
[WiseOwl.Obfuscation (false)]
internal class MyClass {
.
.
.
}
Normally, Demeanor will not obfuscate the name of a public top-level type. You can request that Demeanor obfuscate the type name by setting the WiseOwl.ObfuscationAttribute's required shouldObfuscate argument value to true. Optionally, you can also specify the string to use as the obfuscated name of the type. Note that when you specify the obfuscated name to use, it is your responsibility to insure that the specified name will be unique within the appropriate scope.
[WiseOwl.Obfuscation (true, ObfuscatedName= "Foo")]
public class MyClass {
.
.
.
}
When Demeanor obfuscates an enumerated type, it normally removes all symbolic values from the enumeration declaration. 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. Now you can use the WiseOwl.ObfuscationAttribute to enable obfuscation for an enumerated type, but also specify that Demeanor preserve the literal value definitions for use at runtime.
[WiseOwl.Obfuscation (true, PreservveLiteralFields=true)]
internal enum Color {
Red,
Green,
Blue,
}
Demeanor for .NET, Enterprise Edition feature.
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 guarantee. 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.
6.0 Version history |
|---|
| 3.0.2166.0 |
Enhanced detection of missing linked managed resources. |
| 3.0.2133.0 |
Gracefully handle extraneous metadata present in some MC++ assemblies. Fixed an exception produced when processing some assemblies generated by ILMerge. |
| 3.0.2124.0 |
Fixed incorrect handling of ObfuscationAttribute preserve literal fields option. Added accomodation for mC++ assemblies with zero length structs. Corrected metadata version check for some strange .NET 1.0 assemblies. |
| 3.0.1911.0 |
Updated Wise Owl's phone number in the license activation dialog. Updated the copyright information. |
| 3.0.1851.0 |
Corrected an error when obfuscating VB.NET code containing an On Error Goto statement when requesting string encryption. In rare cases, the resulting program might generate an InvalidProgramException. |
| 3.0.1813.0 |
Corrected an error when obfuscating the name of a nested type with the -prefix option specified. |
| 3.0.1772.0 |
Added the -prefix:<value> command line option. Demeanor now checks to see if the version of metadata in each assembly that it loads is a recognized value. Demeanor no longer attempts to process metadata that it doesn't understand. Basically, this means Demeanor now obfuscates only those assemblies that target a .NET runtime version the same or earlier than the one for which Demeanor itself was built. |
| 3.0.1644.0 |
Fixed an obscure bug that caused an exception when obfuscating a multi-module assembly when one of the modules contained linked resource files. Demeanor now copies linked resource files to the output directory when it obfuscates a module that contains references to linked resource files. Demeanor now suppresses string encryption only for those modules of an assembly that contain native code and IL. Other pure IL modules of the assembly will have their strings encrypted when requested. Previously, when any module of an assembly contained native code and IL, Demeanor suppressed string encryption for all modules of that assembly. Demeanor now handles malformed, invalid metadata more robustly. Some third-party tools produce assemblies containing various kinds of invalid metadata. PEVerify does not detect some of these invalid constructs. Unfortunately, you only find out about the problem at runtime when some code causes .NET to use the malformed metadata. Demeanor does not attempt to correct this invalid metadata but now produces a warning notifying you of the problem. One example of such a warning is: Error: Malformed custom attribute of type DotfuscatorAttribute - ignoring. |
| 3.0.1531.0 |
Fixed a serious bug handling assemblies produced by the Microsoft C++ with Managed Extensions compiler. In a very unique situation, a reference to a string literal in an IL instruction could potentially reference the wrong string literal. Corrected managed resource obfuscation for satellite assemblies. Bug introduced by the support for configuration file managed resource explicit renaming. |
| 3.0.1507.0 |
Added support for source file annotation of obfuscation requests using an ObfuscationAttribute. Added support for control of obfuscated managed resource names using the configuration file. Integrated licensing support into the Demeanor assembly. |
| 3.0.1472.0 |
Add support for phone and email license activation. Added special handling for Xheo licensing resources for the convenience of those using VS.NET projects with a default namespace. |
| 3.0.1440.0 |
Corrected handling of char fixed arguments to a custom attribute. |
| 3.0.1425.0 |
Removed the special handling of Xheo licensing resources as it's no longer required for Xheo.Licensing version 2.0 and greater. Name your license resource <AssemblyName>.plsk and Xheo licensing will find it. |
| 3.0.1400.0 |
Corrected an error that produced invalid metadata when obfuscating custom attributes containing named arguments of enumerated types. |
| 3.0.1393.0 |
New release of Demeanor for .NET, Personal Edition. |
| 3.0.1360.0 |
Fixed an obscure error that produces an overflow exception when processing properties and field of attributes that contain boxed enumerated values. |
| 3.0.1342.0 |
When Demeanor detects invalid metadata in an error, it now produces more detailed error messages. This can help those developers obfuscating assemblies produced by beta compilers. |
| 3.0.1284.0 |
Add the smart device application configuration file to the distribution. |
| 3.0.1265.0 |
Add the -noserializable command line option. Support configuration file specification of report file generation and, optionally, the report file name. Modified method parameter obfuscation as follows: When a method name is obfuscated, remove the names of all parameters to the method. When a method is not obfuscated, leave the existing parameter names. Constructor (.ctor) method names can never be obfuscated so, by default, their parameter names are also never removed. The -forceparameters option causes Demeanor to remove all parameter names for all methods, including constructors Corrected incremental obfuscation to read all exclusion regular expressions from the configuration file. Previously it was only reading the first one. |
| 3.0.1256.0 |
Enhance incremental obfuscation to allow preserving of original top-level type names as well as control their new obfuscated name. Obfuscate custom attribute named property array of type arguments correctly. |
| 3.0.1252.0 |
Fix incremental obfuscation configuration file bug when expected elements are not present in the file. |
| 3.0.1226.0 |
Gracefully handle exception clauses with invalid length settings the way the runtime and ILDASM do. Generate the correct exception clause lengths in the obfuscated output files. |
| 3.0.1225.0 |
Generate Smart Devices optimized assemblies when appropriate. |
| 3.0.1224.0 |
Fix output file corruption bug. |
| 3.0.1223.0 |
Improve performance significantly. Fix bug with string encryption during obfuscation of assemblies built incrementally. |
| 3.0.1222.0 |
Add support for Microsoft J# assemblies. |
| 3.0.1221.0 |
Fix a bug where Demeanor only renamed some of the managed resources in an assembly. Now it correctly renames all appropriate managed resources. |
| 3.0.1216.0 |
Add trial and full version licensing. Handle COM Interop event attributes with malformed arguments. |
| 3.0.1215.0 |
Gracefully handle version-redirected assemblies. |
| 3.0.1209.0 |
Gracefully handle TLBIMP-generated interop assemblies that contain invalid metadata. Correct the invalid metadata when producing the obfuscated assembly. |
| 3.0.1208.0 |
Support for .NET version 1.1. Much deeper analysis of types and methods resulting in better and more automatic obfuscation. Supports version redirected assemblies. Supports even more MC++ assemblies that contain patently invalid metadata. Many improvements to the interfaction of obfuscation with licensing classes and attributes. |
| 2.2.1034.2 |
Properly handle a weird COM interop assembly that contains an external assembly reference to itself. |
| 2.2.1034.1 |
Added additional constraints to name overloading algorithm to avoid .NET Runtime v1.0 Remoting bugs. Removed constraints in name overloading when type is not remotable. |
| 2.2.1033.0 |
Added support for automatic culture-specific satellite assembly resource obfuscation. |
| 2.1.997.0 |
Added support for MC++ assemblies that contain thread local storage. Added more graceful handling of extraneous but harmless metadata left in some assemblies by the Microsoft MC++ compiler. |
| 2.1.993.0 |
Added support for incremental obfuscation of properties and events. When you have specified a configuration file, Demeanor will now assign properties and events the obfuscated name specified in the configuration file. When no such name is present, or when the obfuscated name is "_Deleted", Demeanor obfuscates a property or event normally, which means the property or event is deleted. Note: Property and event name obfuscation is independent of the associated accessor/mutator methods name obfuscation. When using a configuration file to control exactly the obfuscated name of a property or event, you generally will need to control exactly the names of the accessor/mutator methods as well. |
| 2.1.992.0 |
Added /noenumerations (/noenums) command line option due to popular request. |
| 2.1.990.0 |
Fixed bug handling configuration files with types missing an ObfuscatedName entry. |
| 2.1.985.0 |
Fixed bug handling global valuetype fields in MC++ assemblies that resulted in an infinite loop. |
| 2.1.978.0 |
Incremental obfuscation support (/c:) was added. Fixed bug with obfuscation of MC++ assemblies causing a BadImageFormatException to result. |
| 2.0.947.25766 |
Encrypt strings only the first time Demeanor is run on an assembly. Fixed numerous bugs in entire application obfuscation that resulted in type load exceptions, missing method exceptions, and missing field exceptions. |
| 2.0.946.0 |
Fixed a critical bug in the entire application obfuscation (-app) processing. In rare situations, running the obfuscated application produced a missing method exception because the method had been obfuscated in a dependent assembly but the reference to the method was not obfuscated in the root assembly. |
| 2.0.929.0 |
Added an encrypt string option (-es) that obfuscates all literals in processed assemblies. Added the -a:<name> command line option. |
| 2.0.893.0 |
Demeanor now removes custom attributes that aren't required after obfuscation. Fixed a subtle bug is the property and event removal logic. |
| 2.0.873.1 |
Modified the field name overloading algorithm in serializable types due to a runtime serialization bug. Modified the methods with covariant return name overloading algorithm due to a runtime bug in Remoting. Added deletion of obfuscated events. Properties were already being deleted. |
| 2.0.873.0 |
Initial release of the Enterprise Edition. Entire application obfuscation added (command line option /app). Obfuscated properties and events are removed from the obfuscated assembly and cannot be recovered by a decompiler. Managed resources that have a name in the form "<namespace.type>.resources" will have the namespace.type prefix changed to match the obfuscated name when appropriate. The reporting file XML schema was changed to support multiple assemblies per obfuscation. Inside the DemeanorReport element, there is now an Assemblies collection. Each Assembly element in the collection now contains a Modules collection. Each Module element in the collection contains the obfuscation report for the types and members in that module. The /names:C# and /names:VB command line options have been removed. The /noresources command line option was added. |
| 1.2.836.0 |
Obfuscation of managed C++ assemblies now supported. Can now use a regular expression to specify the names of types and members for which you want to disable obfuscation. |
| 1.1.821.0 | Fixed obscure bug triggered by requesting the reporting option and excluding a property or event. |
| 1.1.816.0 | Trial version released. |
| 1.1.809.17429 |
Bug fix for hang on nested class definition. Introduced when /report option added. Modifed report option to allow user to specify the report file name. Changed the default file name (when reporting is requested but no file name is specified) to be "<assemblyName>Report.xml". |
| 1.1.808.37890 | /report (/r) command line option added |
| 1.0.791.10332 | Initial release of Demeanor for .NET Lite |
Did you find this information useful? Please send your suggestions and comments about the documentation to support@wiseowl.com.