Obfuscation is the hiding of intended meaning. And within the context of programming, it’s a necessary evil for different classes of programs.
Programs written against the .NET Framework are compiled into intermediate code called Microsoft Intermediate Language (MSIL). MSIL preserves the structure and many of the details of your code. This makes decompiling trivial.
Obfuscation isn’t something you think of as adding value to your program. It’s something you may be forced to do when you’re working on software where privacy and security of your code is critical. This includes but is not limited to anti-virus software, protection of proprietary algorithms, or when protecting licensed software.
Enter Crypto Obfuscator
LogicNP Software bridged the gap between necessity and value with their Crypto Obfuscator software. Crypto Obfuscator combines industry leading obfuscation and tamper protection with various optimization features.
Everything you’d expect to see is here including symbol and overload renaming, string encryption, and tamper protection. Crypto Obfuscator also does fake renaming, method call hiding, control flow obfuscation, et al.
The webpage for Crypto Obfuscator has a complete list of all the features. But, it’s worth looking at how some of these features work to help you protect your applications.
The tried-and-true Hello World program is always a good place to start. A simple program that we all know and understand provides a good reference to how obfuscation benefits your more complicated programs.
static void Main(string args)
It’s probably no surprise that a reflection-based decompiling program such as Telerik Decompile gets you back to the original code with no problem. That is a problem.
Enter obfuscation. Crypto Obfuscator is a stand-alone program that performs obfuscation and tamper prevention operations on your compiled .NET programs. It works against the MSIL in the EXE output. It doesn’t touch your code or PDBs.
The default options when you run Crypto Obfuscator are to use a cryptographic renaming scheme, encrypt strings, rename symbols, encrypt and compress resources, and to protect against ILDASM (IL disassembler included with the .NET Framework).
The process couldn’t be more simple. You add the assembly or assemblies and click Obfuscate. Now, using ILDASM greets you with the error, “Protected module – cannot disassemble”. Telerik Decompile has no problem opening the executable, but what it gives isn’t all that useful (I’ve decreased the length of the encrypted names for display purposes).
internal class c4167e05…6c3cfa
private static void c3e60d23…f0a7(string ce9e0…b82c)
Most people would agree that the result is pretty unintelligible. But, I think it can do better. We still have a call to an external method from the mscorlib. I’d like to see that go away.
Crypto Obfuscator has a method call hiding features just for this situation. To resolve it is as simple as selecting Hide Calls to External Methods. Changing the renaming scheme to Normal produces short names like A and B. The following is the decompiled resulting from these two changes.
internal class A
private static void A(string A)
string str = C.B(1);
Much better! And therein is the benefit of method call hiding. At this point, a simple Hello World program is unreadable. Imagine what this obfuscation will do for your special collision or malware detection algorithms.
The question of performance is a logical one anytime you talk about making changes to a compiled application. Features like encryption of method bodies and higher levels of control flow obfuscation will affect your performance. But, how much?
To get a good feeling of this, I created a test program. The test program performs some calculations, does some string allocations, throws some exceptions, and calls methods several levels deep. Each sets up a stack and performs various operations.
The following settings were used in this test (the items with an asterisk are ones added since the Hello World example):
- Normal Renaming
- Adv. Method Overload Renaming*
- Encrypt Strings
- Encrypt Method Bodies*
- Protect Against ILDASM
- Rename Symbols (Non-Public)
- Hide Calls to External Methods
- Encrypt/Compress Assembly Resources
- Control Flow Obfuscation – Max*
- Code Masking Level – Max*
* Control Flow Obfuscation was (Medium) and Code Masking Level was (Off)
I used a simple
Stopwatch object to get my readings. As I increased the iterations, my program was O(N) as designed. The obfuscated code performed at roughly a O(mN).
Getting an accelerated linear increase is expected. The obfuscation works by setting up delegates and masking many things with more method calls. Essentially, it turns your MSIL into spaghetti code.
To prove the point, I ran the same tests using the more basic, default settings that I used with the Hello World example. The obfuscated program now ran at a consistent O(N) at about 3% slower than the non-obfuscated program.
Clearly, you will need to reconcile your need for obfuscation with your need for performance. Luckily, Crypto Obfuscator gives you control over the obfuscation at a granular level.
You can manage types and methods that are obfuscated and to what degree. The exclusion and inclusion rules contain a dozen settings including renaming, control flow, encryption, and overloading. This means you can tweak the obfuscation so that methods that need to perform well will continue to do so.
Although there’s no guidance on what you should focus on, the interface makes it simple and fast to perform trial-and-error obfuscations.
Excluding a type or method can be required for different reasons. Performance is one possibility. But, there are things like certain reflection methods that will fail if obfuscated.
The exclusion rules aforementioned can be configured easily on the Exclusion/Inclusion Rules tab within the UI. However, you also have the option of defining them within your code using attributes.
I prefer the code method. And LogicNP did this the right way. They could have rolled their own solution requiring you to add an assembly reference. Instead they take advantage of the ObfuscationAttribute that is located in the
The obfuscation process immediately warns you that it found code that could cause the obfuscated assemblies to work incorrectly. And it follows up by pointing you to the Warnings tab in the UI which lists exactly the method that it expects to fail.
The following code shows a method that has been excluded from obfuscation, because the call to GetProperty can cause problems.
[Obfuscation(Exclude = true, Feature = "all")]
private static void SetProperty(object obj, string name, object value)
var prop = obj.GetType().GetProperty(name);
In this case I explicitly set the Feature property to “all”. But, you can use one of the many values specified in the documentation to exclude individual features from obfuscation.
Debugging an exception in a production environment can be tricky. It’s unlikely you can hook up a debugger. And more often than not you’re restricted to what you can install to assist in the troubleshooting.
The stack trace from an exception is invaluable to troubleshooting. And often times it’s all you need to point you at the problem. Unfortunately, obfuscation renders your stack trace worthless.
This is a sample stack trace from a test program that happens to throw an exception in certain scenarios.
at A.A.I(Double A, Double B) in File0:line 55
at A.A.E() in File0:line 34
at A.B.A(String A) in File1:line 12
Crypto Obfuscator doesn’t leave you out in the cold. It contains a feature called the Stack Trace Deobfuscator (available in the Project menu) that will take your obfuscated stack trace and return the original stack trace.
This process is simple. It uses a special map file that is created as part of your obfuscation project. You copy and paste the stack trace, click the deobfuscate button and you’re done.
The deobfuscation doesn’t need to be a manual process. You have the ability to use a special class in the LogicNP.Deobfuscator.dll to do this automatically; for example, as part of automated bug reporting.
There’s a lot more within Crypto Obfuscator including the automatic exception reporting capabilities and a slew of other obfuscation features. Crypto Obfuscator is one of the more feature-rich obfuscation products.
Additionally, I haven’t touched on the Visual Studio integration which allows integration with MSBuild. The Visual Studio integration cuts out the manual step, but it’s a necessity when you’re dealing with certain projects like when using ClickOnce.
Good and the Bad
I include a list of my top likes and dislikes on anything I review.
- Keeping it simple (KISS)
- Exclusion rules
- User interface
- Guidance on performance effects
- Metrics on what was obfuscated
My biggest like is just how simple it is to use. It took no effort to do the basic obfuscation of my test program when I started using it. To me that is a huge plus, because I’d rather not spend a lot of time getting a tool to work the way I want. A hammer is designed in such a way that it’s obvious how to use. Crypto Obfuscator is pretty close.
Things start to get a little more complicated when you expound on the basic settings. Logic NP did a great job of balancing real-time, unobtrusive help when you hover over the settings. Some features require additional information though. In those cases, I found the documentation good if not formatted and organized as well as it could be.
Finally, the implementation of exclusion/inclusion rules is worth its own bullet point. I’m very happy that Logic NP used the built-in attribute from the
System.Reflection namespace instead of creating their own solution. It may seem like a minor point, but it means no dependencies and it’s a nice level of separation that makes my border-line obsessive-self happy.
I really cannot complain much about the interface, because it is easy to use. But, from a user experience (UX) perspective it’s convoluted and could use the benefit of some TLC. However, for a developer it works and that ultimately is the audience for the product.
The remaining points would be hard to define as real negatives. Rather I’d define them as minor deficiencies.
First, I’d like to see some guidance on the performance effects of some of the options. Every program is different, but there is clearly some weight that can be assigned to guide the user. This would narrow the amount of tuning and save a lot of time.
Second, although some things are obvious. It would be nice to see some metrics on what exactly has occurred with the obfuscation. For instance, the increase in IL instructions, size of executable increase, et al. This can be beneficial in determining if you’ve gone too far in your obfuscation.
I came into this evaluation admittedly with little knowledge of obfuscation. Crypto Obfuscator is fast and reliable and stays out of my way. There’s no complicated setup and no annoying dependencies to bloat your projects. LogicNP did a great job of making obfuscation accessible and easy.
The surface area of programs that benefit from obfuscation is larger than I realized before my evaluation. I would be happy to continue using Crypto Obfuscator for any such projects.