3.2. Optimization Techniques
Eazfuscator.NET performs several code and metadata optimizations during obfuscation.
3.2.1. Merging of String Literal Duplicates
Merging of string literal duplicates is a complementary optimization method to string encryption obfuscation technique. Formally, this optimization ensures that all encrypted string values are unique in one .NET assembly. Such kind of optimization also widely known as string pooling.
Example 3.11. Hypothetical string table before encryption
String Values |
---|
Abracadabra |
Siam |
Foo |
Abracadabra |
Foo |
Bar |
Example 3.12. Hypothetical string table after encryption with duplicates merging
Original String Values | Encrypted String Values |
---|---|
Abracadabra | }S£tP)€_9€[] |
Siam | @!€NayfI* |
Foo | !>@"buY] |
Bar | E)€a£J |
3.2.2. Sealing of Terminal Classes
A sealed class is a specially marked class that cannot be inherited. .NET runtime uses a knowledge about such classes to perform some highly-efficient optimizations on virtual method calls. This has a considerable positive impact on overall application performance. Therefore, Eazfuscator.NET analyzes the types defined in a .NET assembly and automatically marks all non-inherited classes as sealed whenever possible.
Example 3.13. Source code retrieved with decompiler before sealing of terminal classes
class Shape
{
…
}
class Circle : Shape
{
…
}
Example 3.14. Source code retrieved with decompiler after sealing of terminal classes
class Shape
{
…
}
sealed class Circle : Shape
{
…
}
In some situations, you may want to disable the automatic sealing of terminal classes. Please use the following directive to achieve that (C#):
Example 3.15. Disable the automatic sealing of a terminal class
[System.Reflection.ObfuscationAttribute(Feature = "sealing", Exclude = true)]
class Circle
{
…
}
3.2.3. String Compression
String compression is an automatic size optimization which results in smaller assemblies. Large strings are compressed during obfuscation. The compressed strings are uncompressed on demand during the run time. The decompression algorithm is incredibly fast, so there is no performance penalty observed — the actual decompression speed comes very close to the speed of a simple memory copy.
String compression is a part of string encryption technique and is on by default.
3.2.4. Code Optimizations
Eazfuscator.NET applies code optimizations to deliver the best performance to your applications. .NET compilers such as C#, VB.NET and JIT already do a pretty decent job in this area. But what they all do not do is high-level optimizations.
High-level optimization is a fresh trend in optimization technology and Eazfuscator.NET establishes itself as the first tool to deliver this technology to the wide .NET user base. The best way to briefly describe high-level optimization is to start thinking as developer thinks: we all know that there are some methods and code patterns which are faster than others. What Eazfuscator.NET does is this: it finds the slow code and swaps it with faster one. Eazfuscator.NET uses a preciously brewed knowledge base of common and efficient code patterns that you can meet in every .NET application.
At first glance, high-level optimization is very similar to a well-known peephole optimization approach. But the main difference is that the classical peephole optimization works only on a small window of target machine instructions, while high-level optimization works at the application-wide level and considers control and data flows as well as the sacred knowledge about specific frameworks such as LINQ, MEF and others.
Let's take a look at example.
Example 3.16. The slow code
[Flags]
enum RunOptions
{
None = 0x00,
PrepareDatabase = 0x01,
SkipPlugins = 0x02
}
class Engine
{
public void Run(RunOptions options)
{
if (options.HasFlag(RunOptions.PrepareDatabase))
InitializeDatabase();
…
}
…
}
The code above uses Enum.HasFlag
method to check whether PrepareDatabase
flag is set. Being sweet in syntax, the code has astonishingly bad performance due to boxing operations that are invisibly generated by C# compiler.
Let's see how the slow code is transformed after obfuscation:
Example 3.17. The fast code. Produced by Eazfuscator.NET after optimizing the slow code
public void Run(RunOptions options)
{
if ((options & RunOptions.PrepareDatabase) == RunOptions.PrepareDatabase)
InitializeDatabase();
…
}
As you can see, Eazfuscator.NET emitted functionally equivalent code. The result of optimization is 500x speed improvement of condition evaluation over original slow code.
The optimizer is on by default and works behind the scenes during obfuscation.
To get information about advanced obfuscation algorithms please read the next chapter.