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
Abstract
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, and decompile. 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 gives a detailed analysis of both obfuscated and de-obfuscated code.
Prerequisite
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
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, and unique ideas to steal or crack the program information.
Obfuscation intends to create a misperception of 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 specifications 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 demonstrating or implementing obfuscation, usually referred to as an Anti-Reverse Engineering approach. 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 cannot 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 the 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 increases 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 informs the corresponding organization about this misdeed.
- All the assembly names, classes, methods, and fields are renamed via the 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 cannot utilize them for cracking.
- It detaches senseless metadata or does not use code, compression, or 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 obfuscator 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 obfuscator software from its vendor website (preemptive), and then install it properly. Therefore, launch the obfuscator. As it runs, it prompts you 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 configurations 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 which 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 the 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 protects 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 to a great extent for 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 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 which is decompiling the actual logic behind the serial key implementation. As we noticed earlier in the original code one can easily obtain the serial key information from there. But as the getValidate() method has undergone obfuscation, it shows 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)
.method private hidebysig instance void getValidate() cil managed
{
// Code size 126 (0x7e)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode
IL_0007: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()
IL_000c: ldstr ""
IL_0011: call bool [mscorlib]System.String::op_Inequality(string, string)
IL_0016: ldc.i4.0
IL_0017: ceq
IL_0019: stloc.0
IL_001a: ldloc.0
IL_001b: brtrue.s IL_0070
IL_001d: nop
IL_001e: ldarg.0
IL_001f: ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode
IL_0024: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()
IL_0029: ldstr "#ABC12@"
IL_002e: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0033: ldc.i4.0
IL_0034: ceq
IL_0036: stloc.0
IL_0037: ldloc.0
IL_0038: brtrue.s IL_004f
IL_003a: nop
IL_003b: ldstr "Registration Successful"
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.
IL_000a: ldarg.0
IL_000b: ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode
IL_0010: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()
IL_0015: ldstr ""
IL_001a: call bool [mscorlib]System.String::op_Inequality(string, string)
IL_001f: ldc.i4.0
IL_0020: ceq
IL_0022: stloc.0
IL_0023: ldloc.0
IL_0024: brtrue.s IL_0094
IL_0026: nop
IL_0027: ldarg.0
IL_0028: ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox obfTarget.RegisterUser::txtKeyCode
IL_002d: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()
IL_0032: ldstr bytearray (F2 D0 F4 B4 F6 B5 F8 BA FA CA FC CF FE BF)
IL_0037: ldloc V_1
IL_003b: call string obfTarget.RegisterUser::b(string, int32)
IL_0040: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0045: ldc.i4.0
IL_0046: ceq
IL_0048: stloc.0
IL_0049: ldloc.0
IL_004a: brtrue.s IL_006a
IL_004c: nop
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)
IL_0052: ldloc V_1
IL_0056: call string obfTarget.RegisterUser::b(string, int32)
As we can see this time the sensitive strings are not showing in the MSIL and the hard-coded Serial key in the source code is encrypted in the byte array which 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 the obfuscator 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 be disassembled in any editor, such as ILDASM, or Reflector. After obfuscating the targets with the Suppress Ildasm option enabled, 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 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.