For a work engagement, I had to study how to hack .NET application. I am glad i had to do this. No really worth to say it; this post will go through some of the techniques I have learn from this experience.
I found two approaches of hacking .NET applications. The first one is by using a debugger and retrieve as many information as I could and possibly utilise the power of the debugger to perform changes to the run-time code.
The second is to disassemble the application to retrieve IL code, study the application and potentially hard-code changes on the IL that will be re-assembled into a new hacked version of the application.
However, what are .NET applications?
The big heads at Microsoft had (15 or more years ago) accepted a very tough challenge: “How can we provide a framework that allows developers to create their application for different type of Windows regardless CPU, OS type/model?”
Deliver portability was the target but to compile software for different CPU and OS models would mean that the source code has to be compiled with a wide range of explicit compilers.
Here is where JIT come. JIT stands for Just-In-Time compilation and it’s an implicit-type compilation, which means that the compilation is divided into two phases:
- conversion of source code (high-level language, such as C#) to IL – Intermediate Language
- conversion of IL code to machine language
Each target platform has a JIT compiler deployed and as long as the IL can be interpreted the program can execute. The initial compiler does not need to know all of the places where the software might run.
If you are a developer or you have some knowledge of programming, you may have already noticed that this same design is being used in another very popular framework. This last framework is called Java.
The following is a picture from the ’90 that illustrate the concept:
Two more words. There are three types of JIT compilation:
- Normal JIT compilation: The code is compiled when called at run-time and IL code will be kept in memory
- Econo JIT compilation: The code is compiled when called at run-time and deleted afterwards
- Pre-JIT compilation: The code is compiled before the call. There are a set of instruction that usually are compiled in pre-JIT. Generally, code that is required to be compiled before the start-up.
The first approach utilise a debugger to analyse the .NET application. The debugger i will be using is Windbg. Please don’t try to use native-code only debugger – I’ve seen people posting on reddit “My OllyDbg is broken, why it doesn’t open the app?”.
For this blog post I have chosen as a target application a .NET application from Crackmes (http://crackmes.de/users/mikkchl/flamingpoop). First thing, attach the debbuger to the running application. Since we are debugging a .NET application, we are going to load debugging extensions.
For .NET Framework > 4.0:
> !loadby sos clr
For .NET Framework < 4.0:
> !loadby sos mscorwks
If not set, load symbols with:
> !symfix <path to symbols>
If you have never done this before, then you should set your symbols server. Note that you can download symbols on you machine. It’s just preferences. The following command set the Microsoft server symbols URL to your Windbg options:
> !sympath SRV*<full_path_to_your_empty_directory>*http://msdl.microsoft.com/download/symbols
I will show a few commands I found interesting for this analysis, however you can find an exhaustive list of commands from the MSDN website and specifically the following URL:
Let’s get some stuff out of the process by dumping the heap. The following command will allows you to dump all objects from garbage collector heap:
> !dumpheap -stat
We received very good information from the above command but It will dump all the garbage collector heap. If we want to dump only information related to our domain, you can use the following command:
> !dumpheap -type <type_name/class_name or method table>
If you don’t know what type name to look for, run the !dumpheap -stat and look for the name in there – or at least this is how I did it. Generally you should discard system classes.
The next command is !DumpObj (or !do, abbreviated version). This command allows to dump the object references. The parameter that you need to pass to the !do command is the address of the object that you can retrieve from the previous !dumpheap command. Find the matching MT address in the Address table and use that address in !do. I will give you an example. From the above command output we want to perform a !do on “WindowsApplication1.Form3”. Retrieve that MT (method table address) from the left column, here we have 000b73fc. Now find the matching MT in the Address table. In our example 000b73fc match the last line as such address is 01c89da4.
In the below screenshot you can see the information gained from this command:
A very important parameter is the MethodTable address in the second line along with MT for each object in the list. This information can be used to retrieve the methods from the method table by using the !DumpMT -md. This command will contain information on the methods such as MethodDescritpion address, whether the method has been JIT’ed, PreJIT’ed or not JIT’ed at all.
Using the MethodDe value we can subsequently dump a description of the single method using !DumpMD <MethodDe address> as show below for the WindowsApplication1.Form3.get_TextBox1:
From the above you can retrieve the virtual address (CodeAddr) that we can use to perform more in-deep hacks such as setting breakpoints and dump the IL code. I am going to repeat this command but against the WindowsApplication1.Form3.Button1_Click method as we will see that this is a function which we want to study to hack the Crackmes challenge:
Using the CodeAddr value we are going to dump some sweet assembly code. Assembly code can be obtained by issuing the !U command as show as follows:
Alternatively we can dump IL code using !dumpIL by passing as a parameter the MethodDe address. However, note that I will show a more practical way of retrieving IL:
I think we covered a fair amount of commands to gain information of the application. At this point, I would like to show you how to set breakpoints and analyse the stack. To set breakpoints you have to use the !BPMD command. An important note, you can only breakpoint methods that have been JIT’ed!
The syntax of the !BPMD is the following:
!BPMD [-nofuturemodule] [<module name> <method name>] [-md <MethodDesc>] -list-clear <pending breakpoint number> -clearall
Windbg does not provide sufficient information in case of breakpoint set failure. To verify that your breakpoint has been set, ensure that the output looks like the following:
Once the breakpoint is set we can go and play with the application. Allow the process to continue by pressing “g”. When you enter the serial and press OK, the debugger will stop as per our breakpoint:
As you do whenever your debugger stops at a breakpoint, you are going to dump the stack trace in order to analyse how did we get to this point and more importantly who called our targeted method. In Windbg-CLR, there are two commands:
These two commands are similar, the difference is that the first command will only provide the stack-trace of managed functions only. For the type of analysis that we have waged, !CLRStack is more appropriate. The following is an example of !CRLStack being run at WindowsApplication1.Form3.Button1_Click:
Here you go! There are a myriad of commands you can execute. I recommend you to have a look at MSDN. Additionally, I found a document which summarise a good number of CLR instructions to use with Windbg available at the following URL: