Return (age * height) + Today.DayOfWeek
Decompilers .
939
End Function
End Class
End Namespace
Double-clicking the GenerateMagicNumber method in IL Dasm opens up an additional window
that shows the IL for that method. Figure 47-2 shows the IL for the GenerateMagicNumber method,
which represents your super-secret, patent-pending algorithm. In actual fact, anyone who is prepared
to spend a couple of hours learning how to interpret MSIL could quickly work out that the method
simply multiplies the two int32 parameters, age and height, and then adds the current day of the
year to the result.
fiGure 47-2
For those who haven’t spent any time understanding how to read MSIL, a decompiler can convert
this IL back into one or more .NET languages.
decoMPilers
One of the most widely used decompilers is .NET Reflector from Red Gate Software (available for
download at www.red-gate.com/products/reflector/). Reflector can be used to decompile any
.NET assembly into C#, Visual Basic, Managed C11, and even Delphi. In Figure 47-3, the same
assembly you just accessed is opened using IL Dasm, in Reflector.
940 .
chaPter 47 obFuScATion, ApplicATion moniToring, And mAnAgemenT
fiGure 47-3
In the pane on the left of Figure 47 - 3, you can see the namespaces, type, and method information in a
layout similar to IL Dasm. Double - clicking a method should open the Disassembler pane on the right,
which displays the contents of that method in the language specifi ed in the toolbar. In this case, you
can see the C# code that generates the magic number, which is almost identical to the original code.
You may have noticed in Figure 47-3 that some of the .NET Framework base class
library assemblies are listed, including System, System.Data, and System.Web.
Because obfuscation has not been applied to these assemblies, they can be
decompiled just as easily using Reflector. However, Microsoft has made large
portions of the actual .NET Framework source code publicly available, which
means you can browse the original source code of these assemblies including the
inline comments. This is shown in Chapter 43.
If the generation of the magic number were a real secret on which your organization made
money, the ability to decompile this application would pose a significant risk. This is made worse
when you add the Reflector.FileDisassembler add-in, written by Denis Bauer (available at
www.denisbauer.com/NETTools/FileDisassembler.aspx ). With this add - in, an entire assembly
can be decompiled into source fi les, complete with a project fi le.
obfuscatinG your code
So far, this chapter has highlighted the need for better protection for the logic that is embedded
in your applications. Obfuscation is the art of renaming symbols and modifying code paths in an
assembly so that the logic is unintelligible and can ’ t be easily understood if decompiled. Numerous
products can obfuscate your code, each using its own tricks to make the output less likely to
be understood. Visual Studio 2010 ships with the Community Edition of Dotfuscator Software
Services from PreEmptive Solutions, which this chapter uses as an example of how you can apply
obfuscation to your code.
Obfuscation does not prevent your code from being decompiled; it simply
makes it more diffi cult for a programmer to understand the source code if it
is decompiled. Using obfuscation also has some consequences that need to be
considered if you need to use refl ection or strong - name your application.
dotfuscator software services
Although Dotfuscator can be launched from the Tools menu within Visual Studio 2010, it is a
separate product with its own licensing. The Community Edition (CE) contains only a subset of
the functionality of the commercial edition of the product, the Dotfuscator Suite. If you are serious
about trying to hide the functionality embedded in your application, you should consider upgrading.
You can fi nd more information on the commercial version of Dotfuscator at www.preemptive.
com/products/dotfuscator/compare - editions .
Dotfuscator CE uses its own project format to keep track of which assemblies you are obfuscating
and any options that you specify. After starting Dotfuscator from the Tools menu, it opens with
a new unsaved project. Select the Input Assemblies node in the navigation tree, and then click the
button with an ellipsis (...) under the Assembly Name listing to add the .NET assemblies that you
want to obfuscate. Figure 47 - 4 shows a new Dotfuscator project into which has been added the
assembly for the application from earlier in this chapter.
Unlike other build activities that are typically executed based on source fi les,
obfuscation is a post - build activity that works with an already compiled set
of assemblies. Dotfuscator takes an existing set of assemblies, applies the
obfuscation algorithms to the IL, and generates a set of new assemblies.
obfuscating Your Code . 941
942 .
chaPter 47 obFuScATion, ApplicATion moniToring, And mAnAgemenT
fiGure 47-4
Without needing to adjust any other settings, you can select Build Project from the Build menu, or click
the “play” button (fourth from the left) on the toolbar, to obfuscate this application. If you have saved the
Dotfuscator project, the obfuscated assemblies will be added to a Dotfuscated folder under the folder
where the project was saved. If the project has not been saved, the output is written to c:\Dotfuscated.
If you open the generated assembly using Reflector, as shown in Figure 47-5, you will notice that the
GenerateMagicNumber method has been renamed, along with the input parameters. In addition,
the namespace hierarchy has been removed and classes have been renamed. Although this is a rather
simple example, you can see how numerous methods with similar, non-intuitive names could cause
confusion and make the source code very difficult to understand when decompiled.
fiGure 47-5
obfuscating Your Code .
943
The free version of Dotfuscator only obfuscates assemblies by renaming classes,
variables, and functions. The commercial version employs several additional
methods to obfuscate assemblies, such as modifying the control flow of the
assembly and performing string encryption. In many cases, control flow will
actually trigger an unrecoverable exception inside decompilers, effectively
preventing automated decompilation.
The previous example obfuscated the public method of a class, which is fine if the method will only
be called from assemblies obfuscated along with the one containing the class definition. However,
if this was a class library or API that will be referenced by other, unobfuscated applications, you
would see a list of classes that have no apparent structure, relationship, or even naming convention.
This would make working with this assembly very difficult. Luckily, Dotfuscator enables you to
control what is renamed during obfuscation. Before going ahead, you will need to refactor the code
slightly to pull the functionality out of the public method. If you didn’t do this and you excluded
this method from being renamed, your secret algorithm would not be obfuscated. By separating the
logic into another method, you can obfuscate that while keeping the public interface unchanged.
The refactored code would look like the following:
c#
namespace ObfuscationSample
{
public class MathematicalGenius
{
public static Int32 GenerateMagicNumber(Int32 age, Int32 height)
{
return SecretGenerateMagicNumber(age, height);
}
private static Int32 SecretGenerateMagicNumber(Int32 age, Int32 height)
{
return (age * height) + DateTime.Now.DayOfYear;
}
}
}
Code Snippet MathematicalGenius.cs
Vb
Namespace ObfuscationSample
Public Class MathematicalGenius
Public Shared Function GenerateMagicNumber(ByVal age As Integer, _
ByVal height As Integer) As Integer
Return SecretGenerateMagicNumber(age, height)
End Function
Private Shared Function SecretGenerateMagicNumber(ByVal age As Integer, _
944 .
chaPter 47 obFuScATion, ApplicATion moniToring, And mAnAgemenT
ByVal height As Integer) As Integer
Return (age * height) + Today.DayOfWeek
End Function
End Class
End Namespace
Code Snippet MathematicalGenius.vb
After rebuilding the application, you will need to reopen the Dotfuscator project by selecting it from the
Recent Projects list. You have several different ways of selectively applying obfuscation to an assembly.
First, you can enable Library mode on specific assemblies by selecting the appropriate checkbox on the
Input Assemblies screen (see Figure 47-4). This has the effect of keeping the namespace, class name,
and all public properties and methods intact, while renaming all private methods and variables. Second,
you can manually select which elements should not be renamed from within Dotfuscator. To do this,
open the Renaming item from the navigation tree, shown in Figure 47-6.
fiGure 47-6
The Renaming dialog opens on the Exclusions tab where you can see the familiar tree view of
your assembly, with the attributes, namespaces, types, and methods listed. As the name of the tab
suggests, this tree enables you to exclude certain elements from being renamed. In Figure 47-6, the
GenerateMagicNumber method, as well as the class that it is contained in, is excluded (otherwise,
you would have ended up with something like b.GenerateMagicNumber, where b is the renamed
class). In addition to explicitly choosing which elements will be excluded, you can also define custom
rules that can include regular expressions.
After you build the Dotfuscator project, click the Results item in the navigation tree. This screen
shows the actions that Dotfuscator performed during obfuscation. The new name of each class,
property, and method is displayed as a sub-node under each renamed element in the tree. You will see
that the MathematicalGenius class and the GenerateMagicNumber method have not been renamed,
as shown in Figure 47-7.
Prepared for SUSAN ROERS/ email0 Susan_Krentz@aol.com Order number0 64627890 This PDF is for the purchaser’s personal use in accordance with the Wrox Terms of
Service and under US copyright as stated on this book’s copyright page. If you did not purchase this copy/ please visit www.wrox.com to purchase your own copy.
obfuscating Your Code .
945
fiGure 47-7
The SecretGenerateMagicNumber method has been renamed to a, as indicated by the sub-node
with the Dotfuscator icon.
obfuscation attributes
In the previous example you saw how to choose which types and methods to obfuscate within
Dotfuscator. Of course, if you were to start using a different obfuscating product you would have to
configure it to exclude the public members. It would be more convenient to be able to annotate your
code with attributes indicating whether a symbol should be obfuscated. You can do this by using
the Obfuscation and ObfuscationAssemblyAttribute attributes from the System.Reflection
namespace.
The default behavior in Dotfuscator is to override exclusions specified in the project with the
settings specified by any obfuscation attributes. In Figure 47-4 is a series of checkboxes for each
assembly added to the project, of which one is Honor Obfuscation Attributes. You can change the
default behavior so that any exclusions set within the project take precedence by unchecking
the Honor Obfuscation Attributes option on a per-assembly basis.
obfuscationassemblyattribute
The ObfuscationAssemblyAttribute attribute can be applied to an assembly to control whether
it should be treated as a class library or as a private assembly. The distinction is that with a class
library it is expected that other assemblies will be referencing the public types and methods
it exposes. As such, the obfuscation tool needs to ensure that these symbols are not renamed.
946 .
chaPter 47 obFuScATion, ApplicATion moniToring, And mAnAgemenT
Alternatively, as a private assembly, every symbol can be potentially renamed. The following is the
syntax for ObfuscationAssemblyAttribute:
c#
[assembly: ObfuscateAssemblyAttribute(false, StripAfterObfuscation=true)]
Vb
<Assembly: ObfuscateAssemblyAttribute(False, StripAfterObfuscation:=True)>
The two arguments that this attribute takes indicate whether it is a private assembly and whether
to strip the attribute off after obfuscation. The preceding snippet indicates that this is not a private
assembly, and that public symbols should not be renamed. In addition, the snippet indicates that
the obfuscation attribute should be stripped off after obfuscation — after all, the less information
available to anyone wishing to decompile the assembly, the better.
Adding this attribute to the AssemblyInfo.cs or AssemblyInfo.vb file will automatically preserve
the names of all public symbols in the ObfuscationSample application. This means that you can
remove the exclusion you created earlier for the GenerateMagicNumber method.
obfuscationattribute
The downside of the ObfuscationAssemblyAttribute attribute is that it exposes all the public
types and methods regardless of whether they existed for internal use only. On the other hand, the
ObfuscationAttribute attribute can be applied to individual types and methods, so it provides a
much finer level of control over what is obfuscated. To illustrate the use of this attribute, refactor the
example to include an additional public method, EvaluatePerson, and place the logic into another
class, HiddenGenius:
c#
namespace ObfuscationSample
{
[System.Reflection.ObfuscationAttribute(ApplyToMembers=true, Exclude=true)]
public class MathematicalGenius
{
public static Int32 GenerateMagicNumber(Int32 age, Int32 height)
{
return HiddenGenius.GenerateMagicNumber(age, height);
}
public static Boolean EvaluatePerson(Int32 age, Int32 height)
{
return HiddenGenius.EvaluatePerson(age, height);
}
}
[System.Reflection.ObfuscationAttribute(ApplyToMembers=false, Exclude=true)]