How to protect an assembly from decompiling

When I was newbie in .NET, it was a shocked for me to learn that .NET assemblies can be decompile. But today I know that this is a normal situation for a language which is compiling to an intermediate language. As we know that, after compiling a .NET project the output will be in MSIL (Microsoft Intermediate Language). And during the application lifecycle this codes will be compiled to Native Codes by the JIT (Just-In-Time Compiler) when they first used and cached in that computer for the future usages. Anyway this is another subject for us how JIT works, but here the problem is our assemblies are in MSIL format which has about 229 opcodes and all these opcodes has a standard working procedure. So if we know these opcodes and how they are working, we can produce the .NET codes by reverse engineering. This is simply what decompilers do. But why there are no stable decompilers for the native exes? The answer is simple, because there are no metadata for the native exes. In the .NET I can say that, metadata of that assembly is really very important to resolve the Type and Member structures of that assembly. Anyway there are a lot of decompilers for the .NET which can decompile codes as same as with the original codes. Xenocode Fox Decompiler and Reflector are the most know ones.
Below is an example which shows us what a decompiler can do.

Original Code Decompiled Code
private void btnRun_Click(object sender, EventArgs e)
{
    Stopwatch watch = new Stopwatch();
   
    int iteration = 0;
    if (!int.TryParse(txtNumIteration.Text, out iteration))
        iteration = 10000000;
    int value = 0;
    if (!int.TryParse(txtNumParam.Text, out value))
        value = 50;
    IExample ex = null;
    int caseCount;
    switch (cmbCases.SelectedIndex)
    {
        case 0:
            caseCount = 5;
            ex = new Example5();
            break;
        case 1:
            caseCount = 10;
            ex = new Example10();
            break;
        case 2:
            caseCount = 20;
            ex = new Example20();
            break;
        case 3:
            caseCount = 50;
            ex = new Example50();
            break;
        default:
            caseCount = 99;
            ex = new Example99();
            break;
    }
    int r = 0;
    watch.Start();
    for (int i = 0; i < iteration; i++)
    {
        r = ex.GetSwitchResult(value);
    }
    watch.Stop();
    long swResult = watch.ElapsedMilliseconds;
    watch.Start();
    for (int i = 0; i < iteration; i++)
    {
        r = ex.GetIfResult(value);
    }
    watch.Stop();
    AppendItem(iteration, caseCount, value, swResult, watch.ElapsedMilliseconds);
}
private void btnRun_Click (object sender, EventArgs e)
{
     int caseCount;
     Stopwatch watch = new Stopwatch();
     int iteration = 0;
     if (!int.TryParse(txtNumIteration.Text, out iteration))
     {
          iteration = 10000000;
     }
     int value = 0;
     if (!int.TryParse(txtNumParam.Text, out value))
     {
          value = 50;
     }
     IExample ex = null;
     switch (cmbCases.SelectedIndex)
     {
          case 0:
          {
               caseCount = 5;
               ex = new Example5();
               break;
          }
          case 1:
          {
               caseCount = 10;
               ex = new Example10();
               break;
          }
          case 2:
          {
               caseCount = 20;
               ex = new Example20();
               break;
          }
          case 3:
          {
               caseCount = 50;
               ex = new Example50();
               break;
          }
          default:
          {
               caseCount = 99;
               ex = new Example99();
               break;
          }
     }
     int r = 0;
     watch.Start();
     for(int i = 0; i < iteration < i++)
     {
          r = ex.GetSwitchResult(value);
     }
     watch.Stop();
     long swResult = watch.ElapsedMilliseconds;
     watch.Start();
     for (int i = 0;i < iteration; i++)
     {
          r = ex.GetIfResult(value);
     }
     watch.Stop();
     AppendItem(iteration, caseCount, value, swResult, watch.ElapsedMilliseconds);
}
Decompiled code was generated by using Xenocode Fox



As you see here, the output is nearly as same as the original code when we decompile the assembly with Xenocode Fox.

So the question is how we can protect our assemblies from decompiling. Actually the answers is simple, except we don’t make our .net assemblies native, we cannot. By one way or other decompilers can decompile our codes back to the source codes. But against the decompilers there are Obfuscators which helps us to protect our codes from decompiling. If we have to explain what an obfuscator do, simply, Obfuscators changes the names of our Types and Members (properties, methods, fields, events etc.) to unreadable or unmeaning full names in the metadata of our assembly. By this way when someone decompiles our codes they can only see the unmeaning full class and member names which is really hard to understand the code. Generally we make the type and member names as understandable. For example if we write a class which indicates a User’s information, we will probably make the class name as User. So after decompiling the person who decompiles the code, sees this User class and he/she understands that this class is for the user information. So if we change the name of this class to an understandable name (for ex: xbc3453vbgf345) then it will be really hard to understand what this class is for. Except from changing the type and member names some obfuscators also can make .net assemblies as native, or some of them can injects useless codes to our methods which cause our methods hard to understand.

There are a lot of Obfuscators like Xenocode PostBuild, CodeVeil, Salamander etc. I’m using PostBuild to obfuscate my codes and I can say that it is really enough to protect my assemblies. Not only changing the type and member names but also we can make our assemblies Native by using PostBuild, or we can allow it to injects some codes which protects our methods to be decompiled (by injecting some untargeted branches) etc. etc. Making our assemblies as native will stops cares about decompiling. Code injection will obstruct Reflector and Fox to decompile that methods. PostBuild also can make our assemblies smaller by compressing and removing unused metadata by dead code elimination. This will increase our assemblies’ performance. Anyway you can check PostBuild by downloading its trial version. More About Xenocode Postbuild | Download PostBuild Evaluation

As conclusion, I can say that using a good obfuscator is enough to protect our codes. There is no need to care about decompilers. Even I develop a decompiler, I still continue to develop my other projects by using .NET and I have no care about this.

Related posts

Comments

June 9. 2008 02:34 PM

This reminds me your speaking in the .mp3 file "Decompiling ve Reverse Engineering" on http://www.csharpnedir.com/videoindir.asp?id=64. Apart from using an obfuscator, you also said that you had sped up one of your project 10-15 times by removing the unused part in the system .dlls with Fox. If this is different than opening a .dll with Fox, compiling the generated code as a .dll and referencing it in the program; could you tell us how you do this?
Ada

Ada
June 9. 2008 02:45 PM

No in that speech, i dont mean that, decompile system dll's and remove some codes on it and re-compile. This can not be done. Actually in theory it can be done by Fox, but when you decompile a dll like System.dll, there will be some mistakes. Anyway in that speech i mean that, i just look at a code block in the System.dll and according to that i wrote my codes which cause that part of the codes in the System.dll's wont be worked. This makes my codes faster. Fox ex; in the 1.1 version of the TreeView, whenever you give a TreeNode, BackColor, Font etc.etc, this cause treeview Invalidate all its Client region. So you can see this by using Fox, and that you can write your own codes which just Invalidates the needed region, this was make my TreeView(in .net 1.1) faster.
Ozcan DEGIRMENCI
June 9. 2008 03:58 PM

Thanks. I didn't tried to decompile and then recompile. What you said in that audio makes us think as I told. To be sure I re-listened to it. I send this post to verify the solution, to learn your trick.
Thanks again.

Ada
June 9. 2008 04:19 PM

Your welcome...
Thing which i want to say was what i explained in my previous post. So may be there was a mistake at my sentences. Sory about that ...

Ozcan DEGIRMENCI
June 10. 2008 12:01 PM

If this trick is still valid for the latest .NET Framework and you write an article on it, I'm sure that it'll be the first and unique on that point, and will arise too much interest.
Ada
June 10. 2008 02:11 PM

Nop. It was fixed in the .NET ramework 2.0. I was used that in the first version of the Fox which was written by using .NET Framework 1.1.

Anyway, you can still use that way. Let me explain it simply ...

First there is a propBag field in the TreeNode class which is Type is OwnerDrawPropertyBag and that field holds the BackColor, Font and ForeColor of that node.
You can get that private field information (propBag) by using Reflection.

Than you can create a new instance of it, again by using Reflection, and set that instances values to you custom values.

Than set node's propBag fields value to thi custom instance of the OwnerDrawPropertyBag. Because you directly change the field's value by using reflection, it wont be invoked automatically. So call the Invalidate method of the treeview by giving the bounds of the treenode as parameter...

Invalidate(myNode.Bounds);

This will just invalidates that node's area.

In .NET 1.1 whenever you change one of the values of the property bag (for ex; ForeColor, Font and BackColor) it was automatically invalidates the whole treeview. By this way you can make it more faster so there wont be any flicking ...

Ozcan DEGIRMENCI

Add comment


 

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

October 11. 2008 04:48 PM


Search

Calendar

<<  October 2008  >>
MonTueWedThuFriSatSun
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789
View posts in large calendar

Disclaimer

© 2007 - 2008
Ozcan DEGIRMENCI
All rights reserved. The content can be used elsewhere given that the source is properly acknowledged.