Hacking — Best OF Reverse Engineering — Part 15
How to Reverse Engineer dot NET Assemblies?
The concept of dot NET can be easily compared to the concept of JAVA and Java Virtual
Machine, at least when talking about compilation.
Unlike most of traditional programming languages like C/C++, application were developed using dot NET frameworks are compiled to a Common Intermediate Language (CIL or Microsoft Common Intermediate Language MSIL) — which can be compared to bytecode when talking about Java programs — instead of being compiled directly to the native machine executable code, the Dot Net Common Language Runtime (CLR) will translate the CIL to the machine code at runtime. This will definitely increase execution speed but has some advantages since every dot NET program will keep all classes’ names, functions’ names variables and routines’ names in the compiled program. And this, from a programmer’s point of view, is such a great thing since we can make different parts of a program using different programming languages available and supported by frameworks.
Just like Java and Java Virtual Machine, any dot NET program firstly compiled (if we can permit saying this) to a IL or MSIL language and is executed in a runtime environment: Common Language Runtime (CLR) then is secondly recompiled or converted on its execution, to a local native instructions like x86 or x86–64… which are set depending on what type of processor is currently used, thing is done by Just In Time (JIT) compilation used by the CLR.
To recapitulate, the CRL uses a JIT compiler to compile the IL (or MSIL) code which is stored in a Portable Executable (our compiled dot NET high level code) into platform specific code, and then the native code is executed. This means that dot NET is never interpreted, and the use of IL and JIT is to ensure dot NET code is portable.
Basically, every compiled dot NET application is not more than its Common Intermediate Language
representation which stills has all the pre coded identifiers just the way they were typed by the programmer.
Technically, knowing this Common Intermediate Language will simply lead to identifying high level
language instructions and structure, which means that from a compiled dot NET program we can
reconstitute back the original dot NET source code, with even the possibility of choosing to which dot NET programming language you want this translation to be made. And this is a pretty annoying thing!
When talking about dot NET applications, we talk about “reflection” rather than “decompilation”, this is a technique which lets us discover class information or assembly at runtime. This way we can get all properties, methods, functions… with all parameters and arguments, we can also get all interfaces, structures …
Before starting the analysis of our target (not yet presented) I will clarify and in depth some dot NET
aspects starting by the Common Language Runtime.
Common Language Runtime is a layer between dot NET assemblies and the operating system in which it’s supposed to run; as you know now (hopefully) every dot NET assembly is “translated” into a low level intermediate language (Common Intermediate Language — CIL which was earlier called Microsoft Intermediate Language — MSIL) despite of the high level language in which it was developed with; and independent of the target platform, this kind of “abstraction” lead to the possibility of interoperation between different development languages.
The Common Intermediate Language is based on a set of specifications guaranteeing the interoperation; this set of specifications is known as the Common Language Specification — CLS as defined in the Common Language Infrastructure standard of Ecma International and the International Organization for Standardization — ISO (link to download Partition I is listed in references section).
Dot NET assemblies and modules which are designed to run under the Common Language Runtime -
CLR are composed essentially by Metadata and Managed Code.
Managed code is the set of instructions that makes the “core” of the assembly / module functionality,
and represents the application’s functions, methods … encoded into the abstract and standardized form known as MSIL or CIL, and this is a Microsoft’s nomination to identify the managed source code running exclusively under CLR.
On the other side, Metadata is a way too ambiguous term, and can be called to simplify things “data
that describes data” and in our context, very simply, metadata is a system of descriptors concerning the “content” of the assembly, and refers to a data structure embedded within the low level CIL and describing the high level structure of the code. It describes the relationship between classes, their members, the return types, global items, methods parameters and so on… To generalize (and always consider the context of the common language runtime), metadata describes all items that are declared or referenced in a module.
Basing on this we can say that the two principal components of a module are metadata and IL code; the CLR system is subdivided to two major subsystems which are “loader” and the just-in-time compiler.
The loader parses the metadata and makes in memory a kind of layout / pattern representation of the inner structure of the module, then depending on the result of this last, the just-in-time compiler (also called jitter) compiles the Intermediate Language code into the native code of the concerned platform.
The Figure 1 describes how a managed module is created and executed.
Beyond the obvious curiosity factor, understanding IL and how to manipulate it will just open the doors of playing around with any dot NET programs and in our case, figuring out our programs security systems weakness.
Before going ahead, it’s wise to say that CLR executes the IL code allowing this way making operations and manipulating data, CRL does not handle directly the memory, it uses instead a stack, which is an abstract data structure which works according to the “last in first out” basis, we can do two important things when talking about the stack: pushing and pulling data, by pushing data or items into the stack, any already present items just go further down in this stack, by pulling data or items from the stack, all present items move upward toward the beginning of it. We can handle only the topmost element of the stack.
Every IL instruction has its specific byte representation, I’ll try to introduce you a non exhaustive list of most important IL instructions, their functions and the actual bytes representation, and you are not supposed to learn them but use this list as a kind of reference: Table 1.
Table 1. Non-exhaustive IL instruction list
What does this Mean to a Reverse Engineer?
Nowadays there are plenty of tools that can “reflect” the source code of a dot NET compiled executable; agood and really widely used one is “Reflector” with which you can browse classes, decompile and analyze dot NET programs and components, it allows browsing and searching CIL instructions, resources and XML documentation stored in a dot NET assembly. But this is not the only tool we will need when reversing dot NET applications and we will need more than one article to cover all of them.
What Will you Learn From this First Article?
This first essay will show you how to deal with Reflector to reverse a simple practice oriented crack , did the basic way, so tried to simulate in this Crack Me a “real” software protection with disabled button, disabled feature and license check protection (Figure 2).
So basically we have to enable the first button having “Enable Me” caption, by clicking it we will get the “Save as…” button enabled which will let us simulate a file saving feature, we will see where the license check protection is trigged later in this article.
Open up Reflector, at this point we can configure Reflector via the language’s drop down box in the main toolbar, and select whatever language you may be familiar with, choose Visual Basic but the decision is up to you of course (Figure 3).
Load this Crack Me up into it (File > Open menu) and look at anything that would be interesting to us. Technically, the crack me is analyzed and placed in a tree structure, we will develop nodes that interest us: Figure 4. You can expand the target by clicking the “+” sign: Figure 5.
Keep on developing tree and see what is inside of this Crack Me: Figure 6.
Now we can see that our Crack Me is composed by References, Code and Resources.
* Code: this part contains the interesting things and everything we will need at this point is inside
of HiddenNAME _ dotNET _ Reversing (which is a Namespace)
* References: is similar to “imports”, “includes” used in other PE files.
* Resources: for now this is irrelevant to us, but it this is similar to ones in other windows programs.
By expanding the code node we will see the following tree: Figure 8.
We can already clearly see some interesting methods with their original names which is great, we have only one form in this practice so let’s see what Form1_Load(object, EventArgs): void has to say, we can see actual code just by clicking on the method’s name the way we get this: Figure 7.
If you have any coding background you can guess with ease that “this.btnEnableMe.Enabled = false;”
is responsible of disabling the component “btnEnableMe” which is in our case a button. At this point it’s important to see the IL and the byte representation of the code we are seeing, let’s switch to IL view and see: Listing 1. In the code above we can see some IL instruction worth of being explained (in the order they appear):
* ldarg.0 Pushes the value 0 to the method onto the evaluation stack.
* callvirt Calls the method get() associated with the object btnEnableMe.
* ldc.i4.0 Pushes 0 onto the stack as 32bits integer.
* callvirt Calls the method set() associated with the object btnEnableMe.
Listing 1. IL code
instance void Form1_Load (
class [mscorlib]System.EventArgs e
) cil managed
// Method begins at RVA 0x1b44c
// Code size 29 (0x1d)
.locals init (
 valuetype [System.Drawing]System.Drawing.Color
IL_0001: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Button CrackMe2_
IL_0007: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_
IL_000d: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_
IL_0012: call valuetype [System.Drawing]System.Drawing.Color [System.Drawing]System.Dra
IL_0017: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set
} // end of method MainForm::Form1_Load
This says that the stack got the value 0 before calling the method set_Enabled(bool), 0 is in general associated to “False” when programming, we will have to change this 0 to 1 in order to pass “True” as parameter to the method set_Enabled(bool); the IL instruction that pushes 1 onto the stack is ldc.i4.1.
In a section above we knew that byte representation is important in order to know the exact location of the IL instruction to change and by what changing it, so by referring to the IL byte representation reference we have: Table 2.
We have to make a big sequence of bytes to search the IL instruction we want to change; we have to translate ldc.i4.0, callvirt, ldarg.0 and callvirt to their respective byte representation and make a byte search in a hexadecimal editor.
Referring the list above we get: 166F??026F??, the “??” means that we do not know neither instance void [System.Windows.Forms]System.Windows.Forms.
Control::set_Enabled(bool) (at IL_0007) bytes representation nor bytes representation of
instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_HiddenName_dotNET_Reversing.
MainForm::get_LblStat() (at IL_000d).
Things are getting more complicated and we will use some extra tools, I’m calling ILDasm! This tool is provided with dot NET Framework SDK, if you have installed Microsoft Visual Studio, you can find it in Microsoft Windows SDK folder, in my system ILDasm is located at C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin (Figure 9). ILDasm can be easily an alternative tool to Reflector or ILSpy except the fact of having a bit less user friendly interface and no high level code translation feature. Anyway, once located open it and load our Crack Me into it (File -> Open) and expand trees as following: Figure 10.
ILDasm does not show byte representation by default, to show IL corresponding bytes you have to select View -> Show Bytes: Figure 11. Then double click on our concerned method (Form1_Load…) to get the IL code and corresponding bytes: Figure 12.
We have more information about IL instructions and their Bytes representations now, in order to use this amount of new information, you have to know that after “|” the low order byte of the number is stored in the PE file at the lowest address, and the high order byte is stored at the highest address, this order is called “Little Endian”.
What Does this Mean?
When looking inside Form1_Load( ) method using ILDasm, we have this:
IL_0006: /* 16 |
IL_0007: /* 6F | (0A)000040
IL_000c: /* 02 |
IL_000d: /* 6F | (06)000022
These Bytes are stored in the file this way: 166F4000000A026F22000006.
Back to Our Target
This sequence of bytes is quite good for making a byte search in a hexadecimal editor, in a real situation study; we may face an annoying problem which is finding more than one occurrence of our sequence. In this situation, instead of searching for bytes sequence we search for (or to better say “go to”) an offset which can be calculated.
An offset, also called relative address, is used to get to a specific absolute address. We have to calculate an offset where the instruction we want to change is located, referring to Figure 1, ILDasm and ILSpy indicate the Relative Virtual Address (RVA) at the line // Method begins at RVA 0x1b44c and in order to translate this to an offset or file location, we have to determine the layout of our target to see different sections and different offsets / sizes, we can use PEiD or any other PE Tool, but I prefer to introduce you a tool that comes with Microsoft Visual C++ to view PE sections called “dumpbin” (If you do not have it, please refer to links on “References” section).
Dumpbin is a command line utility, so via the command line type “dumpbin -headers target_name.exe” (Figure 13).
By scrolling down we find interesting information:
SECTION HEADER #1
1C024 virtual size
2000 virtual address
1C200 size of raw data
400 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
Notice that the method Form1_Load() begins at RVA 0x1b44c (refer to Figure 1) and here the .text section has a virtual size of 0x1c024 with a virtual address indicated as 0x2000 so our method must be within this section,the section containing our method starts from 0X400 in the main executable file, using these addresses and sizes we can calculate the offset of our method this way:
(Method RVA — Section Virtual Address) + File pointer to raw data; all values are in hexadecimal so using the Windows calculator or any other calculator that support hexadecimal operations we get: (1B44C — 2000) + 400 = 1984C (Figure 14).
So 0x1984C is the offset of the start of our method in our main executable, using any hexadecimal editor we can go directly to this location and what we want to change is a few bytes after this offset considering the method header. Going back to the sequence of bytes we got a bit ago 166F4000000A026F22000006 and going to the offset calculated before we get: Figure 15.
We want to change ldc.i4.0 which is equal to 16 by ldc.i4.1 which is equal to 17, let’s make this change and see what it reproduces (before doing any byte changes think always to make a backup of the original file) (Figure 16).
And yes our first problem is solved; we still have “Unregistered Crack Me” caption and still not tested “Save as…” button. Once we click on the button “Enable Me” we get the second one enabled which is supposed to be the main program feature. By giving it a try something bad happened: Figure 17.
Before saving, the program checks for a license, if not found it disables everything and aborts the saving process.
Protecting a program always depends on the developer’s way of thinking, there are as many ways to protect software as there are ways to break them. We can nevertheless store protections in “types” or “kinds” of protections, among them, there are what we call “license check” protections. Depending on how the developer imagined how the protection must behave and how the license must be checked, the protection’s difficulty changes.
Let’s see again inside our target: Figure 18.
The method btn_EnableMe_Click _1()is triggered when we press the button “Enable Me” we saw this, btn_About_Click() is for showing the message box when clicking on “About” button, then we still have two methods: btn_EnableMe_Click () and checkLicence() which seems to be interesting.
Let’s go inside the method btn_EnableMe_Click() and see what it has to tell: Figure 19.
By clicking on the button save, instead of saving directly, the Crack Me checks the “registration stat”
of the program, this may be a kind of “extra protection”, which means, the main feature which is
“saving file” is protected against “forced clicks”;The Crack Me checks if it is correctly registered before saving even if the “Save as…” button is enabled when the button “Enable Me” is clicked, well click on checkRegStat() to see its content: Figure 20.
Here it is clear that there is a Boolean variable that changes, which is isRegistered and till now we made no changes regarding this. So if isRgeistered is false (if (!this.isRegistered)…) the Crack Me makes a call to the checkLicense() method, we can see how isRegistered is initialized by clicking on .ctor() method: Figure 21.
.ctor() is the default constructor where any member variables are initialized to their default values.
Let’s go back and see what the method checkLicense() does exactly: Figure 22.
This is for sure a simple simulation of software “license check” protection, the Crack Me checks for the presence of a “lic.dat” file in the same directory of the application startup path, in other words, the Crack Me verifies if there is any “lic.dat” file in the same directory as the main executable file. Well, technically at this point, we can figure out many solutions to make our program run fully, if we remove the call to the checkLicense() method, we will remove the same way the main feature which is saving, since it is done only once the checking is done (Figure 2).
If we force the isRegistered variable to take the value True by changing its initialization (Figure 3),
we will lose the call to checkLicense() method that itself calls the main feature (“saving”) as it’s only
called if isRegistered is equal to false as seen here (refer to Figure 2):
public void checkRegStat()
this.LblStat.ForeColor = Color.Green;
this.LblStat.Text = “Saving…”;
We can alter the branch statement (if… else… endif, Figure 4) the way we can save only if the license file is not found.
We saw how to perform byte patching the “classical” way using offsets and hexadecimal editor. I’ll
introduce you to an easy way which is less technical and can save us considerable time.
We will switch again to Reflector (please refer to previous parts of this series for further information). This tool can be extended using plug-ins; we will use Reflexil, a Reflector add-in that will allow us to edit and manipulate IL code then saving the modifications to disk. After downloading Reflexil you need to install it; open Reflector and go to Tools -> Add-ins (in some versions View -> Add-ins), a window will appear, click on “Add…” and select “Reflexil.Reflector.dll”. Once you are done, you can see your plug-in added to the Add-ins window, which you can close.
Basically, we want to modify the Crack Me in such a way that we get “File saved!” Switch the view to see IL code representation of this C# code: Listing 2.
Listing 2. checkLicence() IL code
.method public instance void checkLicence() cil managed
.locals init (
 string str,
 valuetype [System.Drawing]System.Drawing.Color color)
L_0000: call string [System.Windows.Forms]System.Windows.Forms.Application::get_StartupPath()
L_0005: ldstr “\\lic.dat”
L_000a: call string [mscorlib]System.String::Concat(string, string)
L_0011: call bool [mscorlib]System.IO.File::Exists(string)
L_0016: brtrue.s L_006b
L_0018: ldstr “license file missing. Cannot save file.”
L_001d: ldc.i4.s 0x10
L_001f: ldstr “License not found”
L_0024: call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.
VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object, valuetype [Microsoft.VisualBasic]
L_002c: stfld bool CrackMe2_HidenName_dotNET_Reversing.MainForm::isRegistered
L_0032: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_
L_0037: call valuetype [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.
L_003c: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_
L_0042: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_
L_0047: ldstr “Unregistered Crack Me”
L_004c: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Label::set_
L_0052: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Button CrackMe2_
L_0058: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_
L_005e: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Button CrackMe2_
L_0064: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_
L_0069: br.s L_0092
L_006c: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_
L_0071: call valuetype [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.
L_0076: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_
L_007c: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_
L_0081: ldstr “File saved !”
L_0086: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Label::set_
L_008d: stfld bool CrackMe2_HidenName_dotNET_Reversing.MainForm::isRegistered
Instructions that need some explanations, so basically we have this:
By referring to our IL instructions reference we have: Table 3.
The Crack Me makes a Boolean test regarding the license file presence (Figure 4), if file found it returns True, which means brtrue.s will jump to the line L_006b and the Crack Me will load “File saved!” string, otherwise it will go to the unconditional transfer control br.s that will transfer control to the instruction ret to get out from the whole method.
Remember, we want our Crack Me to check for license file absence the way it returns True if file not found so it loads “File saved!” string. Let’s get back to reflector, now we have found the section of code we want to change (Figure 5), here comes the role of our add-in Reflexil, on the menu go to Tool -> Reflexil v1.x; This way you can get Reflexil panel under the source code or IL code shown by Reflector: Figure 23.
This is the IL code instruction panel of Reflexil as you can see, there are two ways you can make changes using this add-in but I’ll introduce for now only one, we will see how to edit instructions using IL code.
After analyzing the IL code above we know that we have to change the “if not found” by “if found” which means changing brtrue.s (Table 1) by its opposite, by returning to the IL code reference we find, brfalse.s: Branch to target if value is zero (false), short form. This said, on Reflexil’s panel; find out where is the line we want to change: Figure 24.
Right click on the selected line -> Edit…, now you get a window that looks like: Figure 25.
Remove “brtrue.s” and type the new instruction “brfalse.s” then click “Update”, you see your modification done. To save “physically” this change, right click on the root of the disassembled Crack Me select Reflexilv1.x then Save as… (Figure 26).
This way we have a modified copy of our Crack Me, we have the “Enable Me” button enabled, by clicking on it we enable “Save as…” button and by clicking on this last we get our “File Saved!” message: Figure 27.
This article is at his end, it takes more time with more complex algorithms and protections but if you are able to get the IL code and can read it clearly you will with no doubt be able to bypass software protection.
Originally published at https://learncybersec.blogspot.com.