Anti-Reverse Engineering (Assembly Obfuscation)

This article illustrates the following underlying contents in detail:
  • Abstract
  • Obfuscation
  • The .NET Application
  • Anti-Reversing Implementation
  • Obfuscated Code Analysis
  • MSIL Code Analysis
  • Final Note
The modus operandi of this paper is to demystify the .NET assembly obfuscation as a way to deter Reverse Engineering. The primary concern for organizations is typically, thwarting their source code (as intellectual property) from reverse engineering. Obfuscation is a tactic that provides unified retitling of symbols in assemblies as well as other tricks to foil decompiles. Properly applied obfuscation increases protection against disassembling and de-compilation by orders of magnitude, while leaving the application undamaged. A cracker can utilize the sensitive information from decompiled or disassembled code easily so this article given a detail analysis of both obfuscated and de-obfuscated code.
It is supposed to install the subsequent software and tools on the security researcher machine:
  • Dotfuscator Professional Edition
  • Red Gate Reflector
  • Ildasm.exe
  • Visual Studio 2010 or later version IDE
  • .NET built assembly ( The target Software)
Obfuscation is a distinctive anti-reverse engineering approach to cipher the actual source code by making it harder to comprehend because, as we have seen in earlier reverse engineering papers, the .NET written assembly could be decompiled or disassembled to full source code very easily by employing various tools, such as Reflector, ILSPY and ILDASM. The .NET code that executes under the CLR is typically, compiled into platform-independent intermediate code and such MSIL code is observable using reverse engineering. The hacker or reverse engineer can analyze nearly the entire software mechanism and subvert the built-in security mechanism by obtaining sensitive information such as copy protection mechanisms, exposing software licensing code, proprietary business logic and much more. Attackers can inspect the details of a software application for security flaws to exploit, unique ideas to steal or crack the program information.
The intention of obfuscation is to create a misperception over the actual source during de-compilation. It declines the interpretation analysis skill as the confusion builds. The source code is replaced with symbols and bizarre specification in an obfuscated code while the executable is preserved. As a result, attempts to reverse-engineer the instructions fail because the translation is ambiguous. Thus, we can say that any obfuscator that confuses a decompiler poses even more deterrence to a human attempting the reverse engineering undertaking.
The .NET Application (Target)
This paper is committed to demonstrate or implement obfuscation, usually referred to an Anti-Reverse Engineering approaches. Hence, we have developed a custom C# Windows Forms application that requires that a license be registered by the user before activation. Such mechanism, however, can be defeated using de-compilation and disassembling and sensitive information serial keys (here in this scenario) can be revealed easily.
Figure 1.1
So, we shall execute the obfuscation tactics on the aforesaid application to make it secure so that the hacker or reverse engineer could not easily comprehend the source code of this application.
Anti- Reversing (Obfuscation) Implementation
Dotbfuscation analyzes applications and makes them smaller, faster and harder to reverse-engineer and is a post-development recompilation system for .NET applications. The following section explains the merits of obfuscation in synopsis view:
  • Dotfuscator decreased the .NET executable program size by removing unneeded segments of an assembly. It is proven to be a performance oriented process.
  • It protects the .NET application from reverse engineering by securing the source code thereby protecting intellectual property.
  • Dotfuscator dramatically increase the execution speed of the program by removing superfluous elements.
  • It can integrate many assemblies into one by linking them and can implement the watermarking over source code to claim proprietary.
  • Dotfuscator verifies your application integrity at run time. If it detects some kind of tempering then one of its temper notification services inform the corresponding organization about this misdeed.
  • All the assembly name, classes, methods and fields rename via overload induction (patented algorithm) mechanism in obfuscated code.
  • The Dotfuscator allows us to hide user sensitive strings that are present in your assembly so that the reverse engineer could not utilize them for cracking.
  • It detaches senseless metadata or not used code, compression, duplicate elimination.
Getting Started
Dotfuscator comes in a community edition and a professional edition. Some of the features are enabled and disabled in both versions. There are typically three ways to run the dotfuscator projects as in the following.
  • Standalone GUI
  • Visual Studio Integrated GUI
  • Command Line
We demonstrate obfuscation implementation via the standalone GUI method. So first download the dotfuscator software from its vendor website (preemptive), and then install it properly. Therefore, launch the dotfuscator. As it runs, it prompts to select the Project Type as in the following:
Figure 1.2
After selecting New Project, just give a meaningful name to the project and go to the Input tab. Here open the .NET target file as in the following:
Figure 1.3
After loading the .NET assembly, the dotfuscator loads the corresponding files of the executable into the IDE and displays a couple of obfuscation options.
Here choose the contents to be protected as in the following:
Figure 1.4
Now, go to the Settings tab, which makes some significant configuration to the impending obfuscated code. Here, we need to enable the control flow, smart obfuscation, and instrumentation options and leave the rest as the default. But one of the essential options is String Encryption that is enabled or disabled depending on the encryption of the in-built strings in the assembly code as in the following:
Figure 1.5
Apart from such aforesaid setting, configure the Rename and Control Flow configuration as in the following:
Figure 1.6
After enabling the string encryption option from the general settings earlier. Finally, configure the rest of String Encryption related settings because an attacker can locate sensitive code sections by looking for string references inside the binary. As per this assembly reference, the attackers can search for a Serial key inside the disassembled or decompiled output. Dotfuscator addresses this problem by allowing you to encrypt strings in these critical segments of the .NET binary, providing an effective barrier against this type of attack as in the following.
Figure 1.7
After finishing with the necessary settings, build the projects to obfuscate the code by pressing the Ctrl +B button or hitting the build button as in the following:
Figure 1.8
The build process usually takes some time. After successfully obfuscating, you can see the build output in the form of logs, of what exactly happened behind the scenes as in the following:
Figure 1.9
You can refer to the Output tab to confirm whether the source code sensitive segments are encrypted or not. You can see in the output tab that the entire critical members such as method name, property, and fields are renamed with a strange string name (eval) as in the following:
Figure 1.10
So the previous figure proves that the assembly code is obfuscated and it is relatively hard to analyze the source code even if you apply to disassemble via reflector.
When the build process is over, a new version of this assembly is created in the project directory under the Dotfuscated folder as in the following figure. The amazing thing about obfuscation is that the new version of this target executable is still intact and produces all the functionality as the previous version does.
Figure 1.11
Obfuscated code Analysis
It is time to confirm to what extent the obfuscated code provides protection to the existed sensitive information in the assembly code. We can accomplish this objective using Reflector because it decompiles the entire source code as the reverse engineer usually did, to obtain or analyze critical data from the target executable.
So, launch the Reflector IDE and first open the original assembly (deobfuscated code). Here, it produces raw C# code but as we know the method getValidate is our prime interest because it has the serial key validation implementation. We can observe that this application will be registered if we enter code #ABC12@ as the serial key.
Figure 1.12
Now, consider the effect of an obfuscated source by opening the executable in the Reflector from the previously created Obfuscated folder. Here, the following figure first displays the encrypted code that has some strange name as members of the RegisterUser class and later in the second column it's deobfuscated counterpart code as in the following:
Figure 1.13
The obfuscated code in the RegisterUser class creates confusion at a great extent to the reverse engineer because they could not easily figure out the members of this class due to having the members in encrypted form. Take a another sample code from the button Register that is basically calling the getValidate() method as defined in the original assembly but in the obfuscated code it has a different name and calls a strange method that does not exist in the assembly indeed, as in the following.
Figure 1.14
Finally, move on to the most sensitive segment of the source code that is decompiling the actual logic behind the serial key implementation. As we notice earlier in the original code and one can easily obtain the serial key information from there. But as the getValidate() method has undergone obfuscation, it shows each and every sensitive string in a bizarre form that is almost impossible to guess by a reverse engineer as in the following:
Figure 1.15
So, we conclude that the obfuscation process makes the reverse engineer's job very hard in terms of guessing or obtaining sensitive information from the aforesaid decompiled figures, both in an obfuscated and de-obfuscated form. By applying proper obfuscation techniques, we can thwart the critical information being revealed and justify the anti-reverse engineering.
MSIL Code Analysis
This section demonstrates the disassembled MSIL code of the corresponding source code in ILDASM. As we know ILDASM is the key interest for reverse engineers in terms of getting sensitive information from the IL code itself and even a target executable can be crafted to a new specification by modifying the bytes rather than rely on the actual source code. So, we have done much research on obfuscated code by decompiling it using a reflector and firmly confirm that this binary is not revealing any sensitive information. All in all, the binary intellectual property is protected from the hacker.
But the reverse engineer can be advantageous with MSIL code as we have seen in the previous reverse engineering papers. So, we need to also ensure that whether the obfuscation is implemented on the MSIL code or not, using dotfuscator. Either such .NET byte code is exposing critical data or obfuscation is applied collectively on both the source code and MSIL code. Thus first examine the de-obfuscated MSIL code by ILDASM.EXE as in the following:
Original Code (De-obfuscated)
  1. .method private hidebysig instance void  getValidate() cil managed  
  2.   {  
  3.     // Code size       126 (0x7e)  
  4.     .maxstack  2  
  5.     .locals init (bool V_0)  
  6.     IL_0000:  nop  
  7.     IL_0001:  ldarg.0  
  8.     IL_0002:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode  
  9.     IL_0007:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()  
  10.     IL_000c:  ldstr      ""  
  11.     IL_0011:  call       bool [mscorlib]System.String::op_Inequality(string,  string)  
  12.     IL_0016:  ldc.i4.0  
  13.     IL_0017:  ceq  
  14.     IL_0019:  stloc.0  
  15.     IL_001a:  ldloc.0  
  16.     IL_001b:  brtrue.s   IL_0070  
  18.     IL_001d:  nop  
  19.     IL_001e:  ldarg.0  
  20.     IL_001f:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode  
  21.     IL_0024:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()  
  22.     IL_0029:  ldstr      "#ABC12@"  
  23.     IL_002e:  call       bool [mscorlib]System.String::op_Equality(stringstring)  
  24.     IL_0033:  ldc.i4.0  
  25.     IL_0034:  ceq  
  26.     IL_0036:  stloc.0  
  27.     IL_0037:  ldloc.0  
  28.     IL_0038:  brtrue.s   IL_004f  
  30.     IL_003a:  nop  
  31.     IL_003b:  ldstr      "Registration Successful"  
  32.     IL_0040:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult  
As we can see from the aforesaid MSIL especially in the colored line, it reveals the serial key along with other reference string values.
IL_0029: ldstr "#ABC12@"
So the reverse engineer could easily manipulate cracking on behalf of such critical information. It is not secure yet. Now open the obfuscated executable and analyze the effects as in the following:
  1. IL_000a:  ldarg.0  
  2.   IL_000b:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode  
  3.   IL_0010:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()  
  4.   IL_0015:  ldstr      ""  
  5.   IL_001a:  call       bool [mscorlib]System.String::op_Inequality(string,  string)  
  6.   IL_001f:  ldc.i4.0  
  7.   IL_0020:  ceq  
  8.   IL_0022:  stloc.0  
  9.   IL_0023:  ldloc.0  
  10.   IL_0024:  brtrue.s   IL_0094  
  12.   IL_0026:  nop  
  13.   IL_0027:  ldarg.0  
  14.   IL_0028:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode  
  15.   IL_002d:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()  
  16.   IL_0032:  ldstr      bytearray (F2 D0 F4 B4 F6 B5 F8 BA FA CA FC CF FE BF )   
  17.   IL_0037:  ldloc      V_1  
  18.   IL_003b:  call       string obfTarget.RegisterUser::b(string, int32)  
  19.   IL_0040:  call       bool [mscorlib]System.String::op_Equality(string,  string)  
  20.   IL_0045:  ldc.i4.0  
  21.   IL_0046:  ceq  
  22.   IL_0048:  stloc.0  
  23.   IL_0049:  ldloc.0  
  24.   IL_004a:  brtrue.s   IL_006a  
  26.   IL_004c:  nop  
  27.   IL_004d:  ldstr      bytearray (F2 A1 F4 90 F6 90 F8 90 FA 88 FC 89 FE 8D 00 60  
  28.                                                 02 77 04 6C 06 68 08 67 0A 2B 0C 5E 0E 7A 10 72     
  29.                                                12 70 14 70 16 64 18 6A 1A 7D 1C 68 1E 73 )         
  30.   IL_0052:  ldloc      V_1  
  31.   IL_0056:  call       string obfTarget.RegisterUser::b(string, int32)  
As we can view that this time the sensitive strings is not showing in the MSIL and especial the hard coded Serial key in the source code is encrypted in the bytearray that is making the reverse engineer's job in terms of cracking as in the following:
IL_004d: ldstr bytearray (F2 A1 F4 90 F6 90 F8 90 FA 88 FC 89 FE 8D 00 60
02 77 04 6C 06 68 08 67 0A 2B 0C 5E 0E 7A 10 72
12 70 14 70 16 64 18 6A 1A 7D 1C 68 1E 73 )
One of the splendid features of dotfuscator is to even stop the disassembling of the MSIL in ILDASM so that the cracker could not view any .NET byte code. To do so, just enable the Suppress IIdasm feature in the settings tab as in the following:
Figure 1.16
This special option would not let the MSIL to be disassembled in any editor, such as ILDASM, Reflector. After obfuscating the targets with the Suppress Ildasm option enable, open the target in the ILDASM, this time, we can observe that the ILDASM is denying the opening of the MSIL code as in the following.
Figure 1.17
Obfuscation Drawbacks
It is no doubt that the obfuscated code thwarts reverse engineering but this mechanism is not reliable in the developer community because it has some pitfalls. The Obfuscated code is especially subject to depend on the platform and compiler characteristics. This creates many problems if in case either the platform or the compiler is changed. Code obfuscation just makes the process of reverse engineering a time-consuming and resource-consuming exercise, the cracker can even bypass the application from the obfuscated code by employing special approaches and obfuscated code might make debugging more difficult. So finally, obfuscation makes the job more complicated for the cracker and reverse engineer but it does not claim 100% full-proof protection. There are many ways to bypass the application from the obfuscated code and we conform with these tactics in the next papers soon.
Final Note
This paper illustrates the protection mechanism for intellectual property by implementing the obfuscation techniques over the .NET source code. We have achieved this process using Dotfuscator software and explain its various features such as assembly members renaming, linking, pruning, and string encryption. This piece applies the obfuscation in a step-by-step fashion over a simple .NET application that asks for a serial key to be registered for the software so that the cracker cannot easily manipulate the disassembled or decompiled code using Reflector or the ILDASM utility. Finally, this article analyzed the obfuscated code with its counterpart original code and taught the special feature of dotfuscator of not allowing the disassembling of the MSIL code in the ILDASM.