For part 1 of this series, please click here.

Introduction

We covered in the first part the very basics regarding dot NET programs, how they are compiled (which we will see again a little bit more in depth) and how we can see inside them using Reflector. We saw how easy is to bypass protections based on hardcoded serials or passwords, this was really very basic and almost always we have to do more to go in depth of real programs protections.

Practicing reverse engineering in general, and not only when we talk about reversing dot NET programs, is not only about getting serials or passwords; reverse engineering is the art of playing with bytes, it’s about changing bytes to alter functionalities, to disable or to enable some of them, in some cases it’s used to add some entire functionalities, and this is not always a simple task. To do this, mastering assembly is a must, and not only this, but finding in the program the right place and figuring out what bytes to change. This is not usually a simple thing.

In this paper and the upcoming one, we will try to practice some byte changing (commonly called “patching”) practices using different homemade targets. We will reuse the first file, which is “CrackMe#1-InfoSecInstitute-dotNET-Reversing”, and a second target that will be “ReverseMe#1-InfoSecInstitute-dotNET-Reversing”.

Compiling dot NET

As seen in the first part, every dot NET program is coded using some high level dot NET programming language (vb.NET, C#…) and when compiling, this high level programming language is taken to a low level one which is Microsoft Intermediate Language (MSIL) and can be considered as the lowest common denominator for dot NET. We can build a full application using nothing but only MSIL, and though this is not interesting from a point of view of a “dot NET developer”, it may be more helpful as it gives an insight into how Common Language Runtime (CLR) actually works and runs our high level code.

Just like Java and Java Virtual Machine, any dot NET program is first compiled (if we can permit saying this) to a IL or MSIL language and is executed in a runtime environment: Common Language Runtime (CLR), and then is recompiled or converted on its execution to local native instructions like x86 or x86-64, which are set depending on what type of processor is currently used. This 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 the dot NET code is portable.

The figure below demonstrates this process:

Understanding MSIL

The aim of this paper is to introduce you to some new IL instructions. 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 program’s security systems weakness.

Before going ahead, it’s wise to say that CLR executes the IL code. Allowing this way of making operations and manipulating data, the 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.

Well, let’s return back to our “CrackMe#1-InfoSecInstitute-dotNET-Reversing”. By typing in a wrong password, the Crack ME shows us this message box:

We got in the first part of “Demystifying dot NET reverse engineering” the hard coded password, now we will see how to force this Crack Me into accepting all wrong passwords by only changing some bytes.

Let’s go back to our Crack ME #1 opened into Reflector. We have the original source code that checks the password typed:

private
void
btn.Chk.Click(object sender, EventArgs e)

{


if (this.txt.Pwd.Text == “p@55w0rd!”)

{


Interaction.MsgBox(“Congratulations !”, MsgBoxStyle.Information, “Correct!”);

}


else

{


Interaction.MsgBox(“Invalid password”, MsgBoxStyle.Critical, “Error!”);

}

}

And by switching to IL code view we get this:

.method
private
instance
void
btn.Chk.Click(object sender, class [mscorlib]System.EventArgs e) cil managed

{


.maxstack 3

L.0000: ldarg.0

L.0001: callvirt instance
class [System.Windows.Forms]System.Windows.Forms.TextBox
InfoSecInstitute.dotNET.Reversing.Form1::get.txt.Pwd()

L.0006: callvirt instance
string [System.Windows.Forms]System.Windows.Forms.TextBox::get.Text()

L.000b: ldstr “p@55w0rd!”

L.0010: ldc.i4.0

L.0011: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Operators::CompareString(string, string, bool)

L.0016: ldc.i4.0

L.0017: bne.un.s L.002d

L.0019: ldstr “Congratulations !”

L.001e: ldc.i4.s 0×40

L.0020: ldstr “Correct!”

L.0025: call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object, valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle, object)

L.002a: pop

L.002b: br.s L.003f

L.002d: ldstr “Invalid password”

L.0032: ldc.i4.s 0×10

L.0034: ldstr “Error!”

L.0039: call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object, valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle, object)

L.003e: pop

L.003f: ret

}

This is the direct representation of the internal Intermediate Language, and this is the level we will be prompted to deal with to make changes. As said above, dot NET is essentially a stack machine, and we will need some references to understand what this IL code means. We can find a listing of all IL assembly instructions and their use. I’ll try to expose and explain the most important ones relative to reverse engineering uses.

IL Instructions start right after the “.maxstack #” line, the first line is L.0000: ldarg.0 which loads argument 0 onto the stack, and this may be easily compared to NOP instruction in traditional assembly code, but its actual byte code is “00″, not “90″ as in x86 assembly. If we open any program using the hexadecimal editor, we will find a series of byte codes from the first line to the last one, which is byte code representing every IL instruction composing our program, and this is what we can change to let the program do things which are not “supposed” to be done when we want to invert some tests or jumps or alter any part of the code.

The use of a hexadecimal editor is in some ways the traditional “dirty” way to replace actual bytes; we will discover how to do it this way and we’ll see how other more “clean” ways work. To locate the offset of bytes we want to change in our hexadecimal editor, we have to look for these bytes to locate the series of bytes we are willing to change.

Every IL instruction has its specific byte representation. I’ll try to introduce to 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:

IL Instruction Function Byte representation
And Computes the bitwise AND of two values and pushes the result onto the evaluation stack. 5F
Beq Transfers control to a target instruction if two values are equal. 3B
Beq.s Transfers control to a target instruction (short form) if two values are equal. 2E
Bge Transfers control to a target instruction if the first value is greater than or equal to the second value. 3C
Bge.s Transfers control to a target instruction (short form) if the first value is greater than or equal to the second value. 2F
Bge.Un Transfers control to a target instruction if the first value is greater than the second value, when comparing unsigned integer values or unordered float values. 41
Bge.Un.s Transfers control to a target instruction (short form) if the first value is greater than the second value, when comparing unsigned integer values or unordered float values. 34
Bgt Transfers control to a target instruction if the first value is greater than the second value. 3D
Bgt.s Transfers control to a target instruction (short form) if the first value is greater than the second value. 30
Bgt.Un Transfers control to a target instruction if the first value is greater than the second value, when comparing unsigned integer values or unordered float values. 42
Bgt.Un.s Transfers control to a target instruction (short form) if the first value is greater than the second value, when comparing unsigned integer values or unordered float values. 35
Ble Transfers control to a target instruction if the first value is less than or equal to the second value. 3E
Ble.s Transfers control to a target instruction (short form) if the first value is less than or equal to the second value. 31
Ble.Un Transfers control to a target instruction if the first value is less than or equal to the second value, when comparing unsigned integer values or unordered float values. 43
Ble.Un.s Transfers control to a target instruction (short form) if the first value is less than or equal to the second value, when comparing unsigned integer values or unordered float values. 36
Blt Transfers control to a target instruction if the first value is less than the second value. 3F
Blt.s Transfers control to a target instruction (short form) if the first value is less than the second value. 32
Blt.Un Transfers control to a target instruction if the first value is less than the second value, when comparing unsigned integer values or unordered float values. 44
Blt.Un.s Transfers control to a target instruction (short form) if the first value is less than the second value, when comparing unsigned integer values or unordered float values. 37
Bne.Un Transfers control to a target instruction when two unsigned integer values or unordered float values are not equal. 40
Bne.Un.s Transfers control to a target instruction (short form) when two unsigned integer values or unordered float values are not equal. 33
Br Unconditionally transfers control to a target instruction. 38
Brfalse Transfers control to a target instruction if value is false, a null reference (Nothing in Visual Basic), or zero. 39
Brfalse.s Transfers control to a target instruction if value is false, a null reference, or zero. 2C
Brtrue Transfers control to a target instruction if value is true, not null, or non-zero. 3A
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
Call Calls the method indicated by the passed method descriptor. 28
Clt Compares two values. If the first value is less than the second, the integer value 1 (int32) is pushed onto the evaluation stack; otherwise 0 (int32) is pushed onto the evaluation stack. FE 04
Clt.Un Compares the unsigned or unordered values value1 and value2. If value1 is less than value2, then the integer value 1 (int32) is pushed onto the evaluation stack; otherwise 0 (int32) is pushed onto the evaluation stack. FE 03
Jmp Exits current method and jumps to specified method. 27
Ldarg Loads an argument (referenced by a specified index value) onto the stack. FE 09
Ldarga Load an argument address onto the evaluation stack. FE 0A
Ldarga.s Load an argument address, in short form, onto the evaluation stack. 0F
Ldarg.0 Loads the argument at index 0 onto the evaluation stack. 02
Ldarg.1 Loads the argument at index 1 onto the evaluation stack. 03
Ldarg.2 Loads the argument at index 2 onto the evaluation stack. 04
Ldarg.3 Loads the argument at index 3 onto the evaluation stack. 05
Ldarg.s Loads the argument (referenced by a specified short form index) onto the evaluation stack. 0E
Ldc.I4 Pushes a supplied value of type int32 onto the evaluation stack as an int32. 20
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
Ldc.I4.M1 Pushes the integer value of -1 onto the evaluation stack as an int32. 15
Ldc.I4.s Pushes the supplied int8 value onto the evaluation stack as an int32, short form. 1F
Ldstr Pushes a new object reference to a string literal stored in the metadata. 72
Leave Exits a protected region of code, unconditionally transferring control to a specific target instruction. DD
Leave.s Exits a protected region of code, unconditionally transferring control to a target instruction (short form). DE
Mul Multiplies two values and pushes the result on the evaluation stack. 5A
Mul.Ovf Multiplies two integer values, performs an overflow check, and pushes the result onto the evaluation stack. D8
Mul.Ovf.Un Multiplies two unsigned integer values, performs an overflow check, and pushes the result onto the evaluation stack. D9
Neg Negates a value and pushes the result onto the evaluation stack. 65
Newobj Creates a new object or a new instance of a value type, pushing an object reference (type O) onto the evaluation stack. 73
Not Computes the bitwise complement of the integer value on top of the stack and pushes the result onto the evaluation stack as the same type. 66
Or Compute the bitwise complement of the two integer values on top of the stack and pushes the result onto the evaluation stack. 60
Pop Removes the value currently on top of the evaluation stack. 26
Rem Divides two values and pushes the remainder onto the evaluation stack. 5D
Rem.Un Divides two unsigned values and pushes the remainder onto the evaluation stack. 5E
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
Rethrow Re throws the current exception. FE 1A
Stind.I1 Stores a value of type int8 at a supplied address. 52
Stind.I2 Stores a value of type int16 at a supplied address. 53
Stind.I4 Stores a value of type int32 at a supplied address. 54
Stloc Pops the current value from the top of theevaluation stack and stores it in a the local variable list at a specified index. FE 0E
Sub Subtracts one value from another and pushes the result onto the evaluation stack. 59
Sub.Ovf Subtracts one integer value from another, performs an overflow check, and pushes the result onto the evaluation stack. DA
Sub.Ovf.Un Subtracts one unsigned integer value from another, performs an overflow check, and pushes the result onto the evaluation stack. DB
Switch Implements a jump table. 45
Throw Throws the exception object currently on the evaluation stack. 7A
Xor Computes the bitwise XOR of the top two values on the evaluation stack, pushing the result onto the evaluation stack. 61

Now that we have a quite good IL instructions reference, we can get back to Reflector and our Crack Me to start imagining how we can get rid of its protection:

Want to learn more?? The InfoSec Institute Reverse Engineering course teaches you everything from reverse engineering malware to discovering vulnerabilities in binaries. These skills are required in order to properly secure an organization from today's ever evolving threats. In this 5 day hands-on course, you will gain the necessary binary analysis skills to discover the true nature of any Windows binary. You will learn how to recognize the high level language constructs (such as branching statements, looping functions and network socket code) critical to performing a thorough and professional reverse engineering analysis of a binary. Some features of this course include:

  • CREA Certification
  • 5 days of Intensive Hands-On Labs
  • Hostile Code & Malware analysis, including: Worms, Viruses, Trojans, Rootkits and Bots
  • Binary obfuscation schemes, used by: Hackers, Trojan writers and copy protection algorithms
  • Learn the methodologies, tools, and manual reversing techniques used real world situations in our reversing lab.

We can see from the picture above that the portion of code:

private
void
btn_Chk_Click(object sender, EventArgs e)

{


if (this.txt_Pwd.Text == “p@55w0rd!”)

{


Interaction.MsgBox(“Congratulations !”, MsgBoxStyle.Information, “Correct!”);

Is just translated to this:

L_0010: ldc.i4.0

L_0011: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Operators::CompareString(string, string, bool)

L_0016: ldc.i4.0

L_0017: bne.un.s L_002d

L_0019: ldstr “Congratulations !”

L_001e: ldc.i4.s 0×40

L_0020: ldstr “Correct!”

L_0025: call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object, valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle, object)

Using the IL instructions reference we get:

  1. Call: Calls the method indicated by the passed method descriptor, which in this case calls the string comparison method
  2. ldc.i4.0: Pushes the integer value of 0 onto the evaluation stack as an int32.
  3. bne.un.s: Transfers control to a target instruction (short form) when two unsigned integer values or unordered float values are not equal.
  4. Ldstr: Pushes a new object reference to a string literal stored in the metadata.

At this point only bne.un.s seems interesting and worth more explanation. If statements are in Intermediate Language, they are translated to a branch instruction, so bne stands for brach if not equal (BranchNotEqual) and it’s used if the two values on the top of a stack are not equal. Then it jumps to line L_002d as you can see:

Want to learn more?? The InfoSec Institute Reverse Engineering course teaches you everything from reverse engineering malware to discovering vulnerabilities in binaries. These skills are required in order to properly secure an organization from today's ever evolving threats. In this 5 day hands-on course, you will gain the necessary binary analysis skills to discover the true nature of any Windows binary. You will learn how to recognize the high level language constructs (such as branching statements, looping functions and network socket code) critical to performing a thorough and professional reverse engineering analysis of a binary. Some features of this course include:

  • CREA Certification
  • 5 days of Intensive Hands-On Labs
  • Hostile Code & Malware analysis, including: Worms, Viruses, Trojans, Rootkits and Bots
  • Binary obfuscation schemes, used by: Hackers, Trojan writers and copy protection algorithms
  • Learn the methodologies, tools, and manual reversing techniques used real world situations in our reversing lab.

L_0016: ldc.i4.0

L_0017: bne.un.s L_002d

L_0019: ldstr “Congratulations !”

This starts making sense, and lets us think about how we can bypass typing in a valid password. Instead of showing “Congratulations!” if the password is correct, we can reverse it and force the program into showing us this message if a password is not correct. The instruction that does this is Beq.s which is, “Transfers control to a target instruction (short form) if two values are equal” (refer to the reference list above).

Problems:

Technically we have two problems. First, we need to find out byte representation of the IL instruction we want to change. Second we do not have an actual offset of the instruction to go there directly in a hexadecimal editor.

Solutions:

Referring the list above we see that bne.un.s = 0×33 and Beq.s = 0x2E; to find the location in file where we have to make changes, we have to translate a few instructions to make a long enough searching string to find what we are looking for.

Always referring to the list above we get:

ldc.i4.0 = 0×16, bne.un.s L_002d = 0×33 and 0x?? Value representing the L_002d and ldstr = 0×72

So our searching string will look like 1633??72. Of course, using “??” means the use of regular expressions when doing search and means the use of a wildcard, and this depends on which hexadecimal editor you use. I’ll use WinHex but you are free to use whatever hexadecimal editor you want:

Here we have to edit 16331472 to 162E1472. Always think about making backups before doing any changes just in case.

Testing our change

Let’s now run our modified / patched version of our Crack Me and see if what we did is right:

Seems all right, but let’s just see what our byte changing actually looks like inside Reflector:

And by switching to IL view mode we can see:

This still is a basic dot NET byte patching process, but is necessary to start from basics before dealing with many more relatively complicated things. We will see in the next chapter some advanced byte patching techniques and how to deal with them.

References