Symbol obfuscation
The
metadata in a .NET assembly contains the original source language symbolic
names and types for all your classes, interfaces, methods, fields, properties
and events. In fact, there are only two types of source file information that aren't
also present in the binary assembly, your comment statements and the names of
your local variables.
To someone trying to steal your intellectual property, these
symbolic names are the most useful and valuable information present in the
assembly. Removing as much of this symbolic information as possible greatly
increases the effort (and therefore cost) required to reverse engineer your
application. If you've ever tried to debug a program without debug symbols,
you've seen this difficulty first hand. However, there are two crucial aspects
to symbol obfuscation that many obfuscator vendors seem not to understand.
First, an obfuscator must discard the original
symbols. Some first-generation obfuscators compute the replacement symbol using
the original symbol. For example, such an obfuscator might simply encode or
permute the original characters in the symbol. This technique provide zero
intellectual property protection. A decompiler targeting that obfuscator can
simply retrieve the original symbolic names by reversing the transformation. By
discarding the original name and selecting an independent replacement name, a
decompiler cannot retrieve the original name. Another way to state this is that
the symbol transformation must be lossy.
Second, given a lossy symbol transformation, any replacement
symbol choice is as good as any other choice with respect to
its obfuscation value. Specifically, you get no more obfuscation by assigning
as many items as possible the same name than you get from assigning every item
a different name. In other words, there is no obfuscation benefit to symbol
overloading.
The goal of obfuscation is to modify an application so that it
is harder to reverse engineer. This requires the obfuscator to make more
difficult both human and automated attacks. An obfuscation difficult for a
human to understand is often easy for an automated tool to handle, and
vice-versa. A quality obfuscator should use techniques difficult for both kinds
of attacks.
Symbol overloading is a trivial obfuscation. A decompiler can
undo the overloading by simply assigning new names to all symbols during
decompilation. Note therefore that it doesn't matter what name an obfuscator
gives a symbol once it has discarded the original symbol. Any choice is as good
as any other choice because a decompiler gets to choose last.
That said, symbol overloading isn't a bad technique. It's just
no better than any other replacement symbol algorithm in terms of its obfuscation
benefit. However, there is a reason to overload names as much
as possible. It minimizes the number of names in the metadata tables thus
allows a .NET assembly to be smaller.
It's ironic that some second-generation obfuscators overload
symbol names for the wrong reason and get a positive benefit as an accidental
result. These obfuscators take advantage of the fact that many languages let
you overload method names. For example, in C# and VB.NET, you can give multiple
methods the same name as long as the method signatures are different. Most
programmers only overload related methods. Second-generation obfuscators
overload unrelated methods in a misguided attempt to increase the confusion
factor of the code and in a valid attempt to reduce the number of symbols.
Here's an example of how such an obfuscator might overload method names in two
classes:
internal class d { internal class e {
private void a() { } private void a() { }
private void a(int a) { } private void a(string a) { }
private int b; private int b;
private double c; private double c;
} }
However, Demeanor for .NET far out-performs such
simplistic name overloading algorithms. We at Wise Owl have devising a
unique Name-Signature overloading algorithm that allows far more
overloading of all symbol names - types, fields and methods - than
method overloading. This, in turn, means that Demeanor for .NET greatly
reduces the number of symbols in the metadata of your application and that
reduces your application's size. For example, Demeanor for .NET will
obfuscate the above example as follows. Note the following isn't legal C# but
is a faithful representation, using C# syntax, of the obfuscated metadata
declarations.
internal class a { internal class b {
private void a() { } private void a() { }
private void a(int a) { } private void a(string a) { }
private int a; private int a;
private double a; private double a;
} }
Even in this simple case, the original example has five symbols
while the Demeanor for .NET technique only requires two symbols. As a
more influential example, when Demeanor for .NET obfuscates
the Microsoft assembly System.Windows.Forms.dll, it throws away ~10,000 symbols
and replaces many of them with a single symbol.
Metadata obfuscation
Demeanor also obfuscates
your application's metadata. Some of the metadata present in a .NET application
is not used by the Command Language Runtime during program execution. It is
used only during development and by programming tools, like Visual Studio .NET,
and compilers such as C# and VB.NET.
For example, the runtime understands fields and methods, but in
many senses, knows nothing about properties and events. Properties and events
are higher-level abstractions used by programmers and tools but which are
actually implemented in terms of methods. Demeanor for .NET removes the
metadata that describes your properties and events, renames the property and
event methods to meaningly symbols, and removes the association that relates a
property's or event's to one another.
For example, assume that your application contains a single
obfuscatable property called 'AccountBalance' that has two related accessor
methods called 'get_AccountBalance' and 'set_AccountBalance'. After
obfuscation, your application contains no properties and two methods with
meaningless names like 'a' and 'b'. This is a lossy transformation. A
decompiler cannot regenerate the property and it cannot determine that the two
methods have related functionality.
Demeanor for .NET also performs many other metadata
obfuscations. For example, in many cases, it can discard the original
accessibility of fields and methods. A decompiler no longer knows how
accessible to declare your members. Generally, it has no alternative other than
to declare the members using public accessibility. Lots of public fields and
methods makes code harder for a human to understand.
String literal encryption
The string literals in an application often provide extremely
useful information to someone atempting to reverse engineer the application.
For example, a developer could disassemble or decompile an application, search
for all uses of the string "password", and concentrate on understanding only
the code that uses the literal.
Demeanor for .NET can encrypt all your string
literals and inject the necessary code to decrypt the strings at runtime. You
do not need to make any changes to your source code to support this
functionality. Demeanor for .NET 's string encryption
technique works with compilers, such as some Microsoft compilers, that
occasionally use identity tests to determine string equality. This
user-controllable feature slightly increases the size of your application and
the generated code runs slightly more slowly due to the need to decrypt the
literals during execution.
Directly obfuscates your binary assembly
Does not try to round-trip using ILDASM and ILASM
Demeanor for .NET makes a copy of all modules of your
original binary assembly, then directly obfuscates the binary modules. Because
Demeanor for .NET directly modifies the assembly's .DLL and .EXE
(portable-executable) files, it can improve the memory layout of your
application. In fact, Demeanor for .NET reorganizes the contents of your
assembly to be considerably more memory-efficient than the assemblies currently
produced by all Microsoft tools. This causes your assembly to load more
quickly, execute faster and use a smaller virtual memory working set.
Other less capable tools do not have the ability to perform
such optimizations to your assemblies. Some disassemble your assembly using
Microsoft's ILDASM utility, then obfuscate the IL source file, then attempt to
compile the IL source file using ILASM. There are constructs that can be
present in an assembly that will not survive a disassemble, then reassemble,
round-trip. Obfuscators using this approach can potentially break your
application. In addition, such tools have no ability to optimize the structure
of the assembly like Demeanor for .NET does.
Selectable symbol exclusion
You may need to exclude certain types and members from
obfuscation. For example, when explicitly using Reflection or when using late
binding in VB.NET (which internally uses Reflection), you access a type or
member by its original, unobfuscated name. Because Reflection occurs at runtime
after you've obfuscated the assembly, it's critical that you not obfuscate the
name of any members accessed via Reflection.
Demeanor for .NET allows you complete control over
the obfuscation process. You can specify that a particular type, method, field,
event or property is excluded from obfuscation. Alternatively, you can use one
or more regular expressions to describe the names of the types and members that
Demeanor should not obfuscate.
Control over obfuscation via configuration file or custom
attributes
Some developers prefer to leave their source code unchanged and
specify all obfuscation options, such as excluding a symbol from obfuscation or
the specific name to use when obfuscating a particular type or member, via a
separate configuration file. Demeanor for .NET allows you to
control all obfuscation options via a separate configuration file.
Other developers prefer to place obfuscation directives
directly in their source file. Demeanor for .NET allows you to
provide a custom attribute attached to a type or type member that specifies
whether to obfuscate the type or member and, when obfuscating it, various
obfuscation options.
|