Demystifying dot NET reverse engineering – PART 3: Advanced Byte Patching
In the first two parts, we saw some basics that will let you reverse-engineer some dot NET applications; we covered the concepts of dot NET compilation, we presented Microsoft Common Intermediate Language, we analyzed some low-level code using code reflection, and we saw a nonexhaustive list of IL instructions with their functions and their byte representations.
We also covered the basics of byte patching, how to find the location of bytes we want to change in a hexadecimal editor, and how to change them.
In this part of our dot NET reverse engineering series I will introduce you to some advanced byte patching as well as a concept of a basic aspect of license check protection and how we can reverse-engineer it (as requested by readers). We will introduce new tools as well, and see how we can deal with them.
In this practice, I have tried to respond to a few questions and suggestions regarding the two first parts. I tried to simulate in this second Crack Me a “real” software protection with disabled button, disabled feature, and license check protection; our third practice looks like this:
So basically we have to enable the first button, which has the “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 triggered later in this article.
Let’s disassemble our target using ILSpy, which is very similar to Reflector so we will not need to present it (a link to download it is in the References). Once analyzed, our Crack Me is placed on a tree structure very similar to Reflector’s. By expanding nodes we see the following:
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, and we get this:
If you have any coding background you can easily guess that “this.btnEnableMe.Enabled = false;” is responsible for disabling the component “btnEnableMe” which is a button, in our case. 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:
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_InfoSecInstitute_dotNET_Reversing.MainForm::get_btnEnableMe()
IL_0007: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Enabled(bool)
IL_000d: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_InfoSecInstitute_dotNET_Reversing.MainForm::get_LblStat()
IL_0012: call valuetype [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_Red()
IL_0017: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_ForeColor(valuetype [System.Drawing]System.Drawing.Color)
} // end of method MainForm::Form1_Load
In the code above we can see some IL instructions that should be explained (in the order in which they appear):
- ldarg.0 Pushes the value 0 onto the evaluation stack.
- callvirt Calls the method get() associated with the object btnEnableMe.
- ldc.i4.0 Pushes 0 onto the stack as a 32-bit integer.
- callvirt Calls the method set() associated with the object btnEnableMe.
This says that the stack got the value 0 before calling the method set_Enabled(bool); 0 is in general associated with “False” when programming, so 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 the previous chapter, we saw that byte representation is important in order to know the exact location of the IL instruction to change and to what it should be changed, so by referring to the IL byte representation reference we have:
|IL Instruction||Function||Byte representation|
|Ldc.I4.0||Pushes the integer value of 0 onto the evaluation stack as an int32.||16|
|Ldc.I4.1||Pushes the integer value of 1 onto the evaluation stack as an int32.||17|
|Callvirt||Calls a method associated with an object.||6F|
|Ldarg.0||Loads argument 0 onto the stack.||02|
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,
and callvirt to their respective byte representations and make a byte search in a hexadecimal editor.
Referring the list above, we get: 166F??026F??; the “??” means that we do not know either the instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Enabled(bool) (at IL_0007) bytes representation or the bytes representation of instance class [System.Windows.Forms]System.Windows.Forms.Label CrackMe2_InfoSecInstitute_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 FilesMicrosoft Visual Studio 8SDKv2.0Bin
ILDasm can easily be an alternative tool to Reflector or ILSpy except for the fact that its interface is a bit less user-friendly and it has no high-level code translation feature. Anyway, once you’ve located it, open it and load our Crack Me into it (File -> Open) and expand trees as follows:
ILDasm does not show byte representation by default. To show the IL corresponding bytes you have to select View -> Show Bytes:
Then double click on our concerned method (Form1_Load…) to get the IL code and corresponding bytes:
We now have more information about the IL instructions and their bytes representations now. In order to use this 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 the 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, e may face the annoying problem of finding more than one occurrence of our sequence. In this situation, instead of searching for a bytes sequence we search for (or, better, 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. 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 to a tool that comes with Microsoft Visual C++ to view PE sections called “dumpbin” (If you do not have it, please refer to links in the “References” section).
Dumpbin is a command line utility, so we use the command line type “dumpbin -headers target_name.exe”
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 supports hexadecimal operations, we get: (1B44C – 2000) + 400 = 1984C
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. 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 little while ago, 166F4000000A026F22000006, and going to the offset calculated before, we get:
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 produces (before doing any byte changes remember always to make a backup of the original file).
And yes, our first problem is solved; we still have the “Unregistered Crack Me” caption and the 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.
But, in giving it a try, something bad happen:
Before saving, the program checks for a license; if the license is not found, it disables everything and aborts the saving process.
Protecting a program depends always on the developer’s way of thinking. There are as many ways to protect software as there are to break it. We can nevertheless store protections in “types” or “kinds” of protections. Among these is what we call “license check” protections. Depending on how the developer imagined that the protection must behave and how the license must be checked, the protection’s difficulty changes.
Let’s see again inside our target:
The method btn_EnableMe_Click _1() is triggered when we press the button “Enable Me,” as we saw this; btn_About_Click() is for showing the message box when clicking on the “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:
By clicking on the “Save” button, instead of saving directly, the Crack Me checks the “registration status” of the program. This may be a kind of “extra protection,” which means that 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. Click on “checkRegStat()” to see its content:
It is clear that there is a Boolean variable that changes, which is isRegistered, and we have made no changes to this. So if isRegistered 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:
.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:
This is surely 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 that 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 also remove 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 is 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). This 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 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 editing 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:
Let’s load our modified Crack-Me on Reflector, and go to checkLicence() method:
Basically, we want to modify the Crack Me so that we get “File saved!” Switch the view to see the IL code representation of this C# code:
L_0005: ldstr “lic.dat”
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]Microsoft.VisualBasic.MsgBoxStyle, object)
L_002c: stfld bool
L_0032: callvirt instance
L_0042: callvirt instance
L_0047: ldstr “Unregistered Crack Me”
L_0052: callvirt instance
L_005e: callvirt instance
L_0069: br.s L_0092
L_006c: callvirt instance
L_007c: callvirt instance
L_0081: ldstr “File saved !”
L_008d: stfld bool
I marked interesting instructions that need some explanations. Basically we have this:
L_0016: brtrue.s L_006b
L_0018: ldstr “license file missing. Cannot save file.”
L_0069: br.s L_0092
L_0081: ldstr “File saved !”
By referring to our IL instructions reference we have:
|IL Instruction||Function||Byte representation|
|Call||Calls the method indicated by the passed method descriptor.||28|
|Brtrue.s||Transfers control to a target instruction (short form) if value is true, not null, or non-zero.||2D|
|Br.s||Unconditionally transfers control to a target instruction (short form).||2B|
|Ret||Returns from the current method, pushing a return value (if present) from the caller’s evaluation stack onto the caller’s evaluation stack.||2A|
Table 1 IL Instructions
The Crack Me makes a Boolean test on the license file presence (Figure 4); if the file is 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; that way, it returns True if file not found, so it loads “File saved!” string.
This way you can get the Reflexil panel under the source code or IL code shown by Reflector.
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 for now I’ll introduce 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” to “if found” which means changing brtrue.s (Table 1) to 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:
Right click on the selected line -> Edit… Now you get a window that looks like this:
Remove “brtrue.s” and type the new instruction “brfalse.s,” then click “Update.” You see your modification has been made:
To “physically “save this change, right click on the root of the disassembled Crack Me select Reflexilv1.x, then Save as…
This way we have a modified copy of our Crack Me. We have the “Enable Me” button enabled; by clicking on it, we enable the “Save as…” button and by clicking on that, we get our “File Saved!” message:
This is the end of the third part. 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 undoubtedly be able to bypass the software protection. In upcoming parts I’ll introduce you disassembling and reassembling dot NET application and see how this can be exploitable in reversing dot NET software.
- Reflexi l : http://sourceforge.net/projects/reflexil/
- Dumpbin: ftp://www.fpc.org/fpc32/VS6Disk1/VC98/BIN/DUMPBIN.EXE
- LINK.exe: ftp://www.fpc.org/fpc32/VS6Disk1/VC98/BIN/LINK.EXE
- Crack ME #2: http://www.mediafire.com/?42vml4flc6yj097
- IL Code reference : https://resources.infosecinstitute.com/dot-net-reverse-engineering-part-2/
We've encountered a new and totally unexpected error.
Get instant boot camp pricing
A new tab for your requested boot camp pricing will open in 5 seconds. If it doesn't open, click here.