Obfuscating with Dotfuscator

Without installing Dotfuscator to every engineer's system

.Net assemblies are easily deconstructed into readable code. You can use tools like Red-Gate's Reflector (http://www.reflector.net/) or ILSpy (http://wiki.sharpdevelop.net/ILSpy.ashx) to view the contents of a .Net assembly. This poses a problem both for intellectual property reasons where we don't want our competitors to know how we did something as well as for security reasons where we don't want a hacker to know what our security algorithms might be. For this reason, we must obfuscate and encrypt our code. Obfuscation simply makes the assembly harder to read, not impossible to read. So it acts as a deterrent to anyone we don't explicitly want reading our code.

Our process

Each engineer is responsible with making sure their code is properly obfuscated before it is released. They have to make sure their project contains all the necessary code so that it gets obfuscated. Following that, they commit their code to the code server which will compile and obfuscate it before copying it to the Latest Builds shared folder on our network.

The .NET Framework provides custom attributes designed to make it easy to automatically obfuscate assemblies without having to set up configuration files. You can read about those here: http://msdn.microsoft.com/en-us/library/ms227281.aspx. We use Dotfuscator by Preemptive (http://www.preemptive.com/) to obfuscate our assemblies. Dotfuscator will read the custom attributes in .Net and act accordingly.

Create a post-build event to obfuscate your build output

In VB.Net go to the project properties, select Compile and click the Build Events... button

In C# go to the project properties and click on Build Events.

The following script will obfuscate the assembly post-compile.

IF $(ConfigurationName) == Release (
   IF EXIST "$(dotfuscator)" (
      IF NOT EXIST "$(ObfuscationMapFolder)\$(ProjectName)" md "$(ObfuscationMapFolder)\$(ProjectName)"
      IF NOT EXIST "$(ObfuscationMapFolder)\$(ProjectName)\map.xml" copy "$(ObfuscationMapFolder)\map.xml" "$(ObfuscationMapFolder)\$(ProjectName)"
      "$(dotfuscator)" /in:"$(TargetPath)" /out:"$(TargetDir)Obfuscated" /honor:on /strip:on /prune:off /enhancedOI:on /suppress:on /mapin:"$(ObfuscationMapFolder)\$(ProjectName)\map.xml" /mapout:"$(ObfuscationMapFolder)\$(ProjectName)\map.xml" /debug:pdb
      copy /Y "$(TargetDir)Obfuscated\$(TargetFileName)" "$(TargetPath)"
      rmdir /S /Q "$(TargetDir)Obfuscated"
   )
)

$(dotfuscator) refers to an Environment variable (like Path or Windir). If it is not set on your system (it should only be set on the build machine) then the IF EXIST statement will be false and the rest of the script will not run.

$(ObfuscationMapFolder) refers to an Environment variable (like Path or Windir). It should contain the path to the location of the map files.

The options set in this example are:

You can run Dotfuscator.exe with the /? option from the command line to get a list of all available options and their defaults. We use the default settings for everything except the above mentioned settings. We also specify prune be set to off (it's default) only because dotfuscator appears to prune classes if I do not specify it.

Environment variables

Got to thank the guys at stackoverflow.com for tipping me off to this (http://stackoverflow.com/questions/128634/how-to-use-system-environment-variables-in-vs-2008-post-build-events).

You can reference any environment variable in the pre or post-build events by stating it like this $(variableName). This can be helpful when trying to determine if your system has certain components installed or not during the build process. Otherwise the build process would report a failure if the build event returned an error.

Nested Conditional Statements and other neat command line tricks

The build events are treated exactly the same as the commands in a batch file. Certain commands, such as for, goto, and if, enable you to do conditional processing of the commands in the build event. For example, the if command carries out a command based on the results of a condition. Other commands allow you to control output and call other batch files. For more information about batch file operations, see http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds.mspx?mfr=true.

Add a call statement before all post-build commands that run .bat files.

For a full list of commands you can use in a build event try http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds.mspx?mfr=true.

Add assembly attributes

You should add the following attributes to the Assembly file.

<Assembly: ObfuscateAssemblyAttribute(True, StripAfterObfuscation:=True)>

The ObfuscateAssemblyAttribute attribute instructs dotfuscator to use its standard obfuscation rules for the appropriate assembly type.

The first argument, assemblyIsPrivate, is set to True in the example code above. It should be true for any assembly that will not be referenced by other assemblies. A public assembly, a library referenced by another assembly for instance, should have this argument set to False so that the obfuscator will use default settings appropriate for a public assembly.

The StripAfterObfuscation property is true, to so that the attributes are stripped after processing.

Remember to import (using in C#) the System.Reflection namespace to use the ObfuscateAssembly attribute.

To prevent renaming of a specific class

You may have a class or structure referenced by name elsewhere. For instance an application configuration file may reference a class by name

For example:

<userNameAuthentication userNamePasswordValidationMode="Custom"
        customUserNamePasswordValidatorType="GCDataService.Configuration.SetConfigurationUserNameValidator,GCDataService"/>

We do not want to rename this class, but we have no problem obfuscating the class's contents. So we write this attribute above the class

<Obfuscation(ApplyToMembers:=False, Exclude:=True)>

This will exclude the class from the obfuscation rules, but all class members will still have the default obfuscation rules applied to it. Remember to import (using in C#) the System.Reflection namespace to use the Obfuscation attribute.

To prevent a class from having any part of it obfuscated

You may have a class or structure that needs to be publicly exposed and you don't want any part of it obfuscated. Instances of this might be WCF endpoint contracts (interfaces).

We do not want to rename this class, no do we want to obfuscate any of its contents. So we write this attribute above the class:

<Obfuscation(ApplyToMembers:=True, Exclude:=True)>

This will exclude the class/structure and all of its members from the obfuscation rules. Remember to import (using in C#) the System.Reflection namespace to use the Obfuscation attribute.

To encrypt the contents of a string

The string constants that you enter into a .Net assembly are easily readable. If they contain anything remotely sensitive, you must encrypt them before we release them to the public.

An example of a string you need to encrypt:

Private Shared Function ConnectionString() As String
    Return "Password=1234;Data Source=" & PermissionsDB
End Function

To encrypt the contents of a function add the following attribute to the top of the method to encrypt:

<Obfuscation(Feature:="stringencryption", Exclude:=False)>

You must set Exclude to false, because the default for string encryption is to not encrypt (ie exclude) all methods. Store sensitive strings inside of functions or methods that you can add this attribute to. You cannot apply this attribute to a variable, so do not assign a sensitive string to a member variable of a class on the same line where you declare that variable. Remember to import (using in C#) the System.Reflection namespace to use the Obfuscation attribute.

And that is all I have to say about that.