Sunday, October 14, 2012

Cracking .NET applications - a simple tutorial

For this tutorial, I will be making use of Reflector and a plugin for Reflector called Reflexil, and also Gray.Wolf to partially view unobfuscated C# code.

Patching the CIL with Reflexil

Here is a very trivial example of how a simple application can validate a license key:
static void Main(string[] args)
{
    var invalidKey = "48011147";

    if (LicenseManager.Verify(invalidKey))
    {
        Console.WriteLine("Thank you for buying our product!");
    } 
    else
    {
        Console.WriteLine("Invalid license key; continue evaluation.");
    }
}
static class LicenseManager
{
    private static string validKey = "112358";

    public static bool Verify(string key)
    {
        return key == validKey;
    }
}
From the code, it's fairly easy to see that to bypass the license key validation logic in our application, we simply need to modify the LicenseManager.Verify method to just return true rather than check for the valid license key.

Running the unmodified application yields the following, since the equality check obviously fails:


So now let's start patching the application by first doing some reconnaissance. Fire up Reflector, add our compiled exe file and navigate to the LicenseManager.Verify method:


Now there are couple of ways we can modify the code with Reflexil...but let's start with the harder way, by modifying the CIL code directly.

If you look at the current CIL, we see that we have four operations: ldarg.0, ldsfld, call and ret:


Here's what's going on:
  1. ldarg.0 loads and pushes the actual argument at position 0 (string key) to the stack.
  2. ldsfld loads and pushes the value of the static field validKey ("112358") to the stack.
  3. call invokes the System.String::op_Equality(string, string) method by popping the last two values from the stack, comparing them and then popping the returned boolean value on the stack.
  4. ret pops and returns the value that the op_Equality last pushed on the stack.

Now that we know what's going on, we need to change these instructions to simply return true rather than comparing the strings.

This means that our code should instead do:
  1. ldc.i4.1 to push the constant value of 1 onto the stack.
  2. ret to pop and return that value.

Note that if we didn't know how to actually write a method which just returns true in CIL, we could have simply wrote the method in C#, compiled it and then view it's corresponding CIL with Reflector:


As for why you see a .maxstack in the above examples, you can find a pretty good explanation here. It seems that this is used by analysis tools.

Using Reflexil, we can now remove the existing operations and replace them with our desired code:


Once that's done, we should now save our modified executable:


Running our newly patched compilation now shows:


Which is exactly what we strived for, since the validation logic has now been eliminated!

Opening our patched application in Reflector, we can now see the new code:


Patching by modifying the C# code directly

Using Reflexil, there's an even easier way of patching our code...by actually writing C# code rather than CIL!

Using a modified version of our first application from the previous example, we will now patch the code by writing C# code.

This is our new, slightly more "complex" licensing code:
static void Main(string[] args)
{
    var invalidKey = "48011147";

    if (LicenseManager.Verify(invalidKey) )
    {
        Console.WriteLine("Purchased license: {0}", LicenseManager.LicenseType);
    } 
    else
    {
        Console.WriteLine("Invalid license key; continue evaluation.");
    }
}
static class LicenseManager
{
    private static string validKey = "112358";
    public static string LicenseType { get; set; }

    public static bool Verify(string key)
    {
        if (key == validKey)
        {
            LicenseType = "Enterprise";
            return true;
        }

        return false;
    }
}

Notice how now, for the validation process to properly succeed, we need to set the LicenseKey property to "Enterprise" before returning true from the method.

Now of course, we can do this in CIL again, but this time we'll use a much easier method.

Open up the application with Reflector, but this time, instead of changing the CIL opcodes directly, right click the Reflexil window and choose "Replace all with code...":


This will bring up this window:


From here, we now need to type in the code that will set the LicenseType property to "Enterprise" and then return true from the method. After you input the code and press the Compile button, you should end up with the following:


Looking at the generated CIL code, we see the opcodes from before:
  1. ldstr to load the "Enterprise" string onto the stack.
  2. call which invokes the set_LicenseType method (Properties in C# are just syntactic sugar for fields and their backing get/set methods) by popping the "Enterprise" string and using it as an actual argument.
  3. ldc.i4.1 to push the integer 1 on the stack.
  4. ret for popping and returning our boolean.

Saving and running our patched application, we now get:


What if the assembly is obfuscated!?

Sometimes, when you try to open a .NET assembly with Reflector, you will be slapped in the face with this message:


Or, even worse, when I obfuscated the last example program we wrote with a trial version of babelfor.NET, Reflector crashed when I tried to open our LicenseManager.Verify method:


Note though that Reflexil still shows us the CIL code, so we don't have any problems when we need to work with CIL code directly...although it will be a bit harder to understand the code by looking at the CIL code only.

And also, if you change the language in Reflector to IL, you will prevent it from crashing but of course you still won't be able to view the raw C# code:


But what we want to do for now is to find a way to view unobfuscated C# code, and that's where an application such as Gray.Wolf comes to the rescue!

Opening our application in Gray.Wolf and navigating to the Verify method, we get:


As you can see from the right pane, it couldn't completely deobfuscate all of our code for this example but it's at least giving us a very good hint as to what's going on without studying the CIL code.

From the C# code, we can see that there is an if-statement and although Gray.Wolf couldn't deobfuscate the two operands used in the statement, we can make out what they are from the CIL code because the two values that are pushed on the stack before the op_Equality is called are done using ldarg.0 which loads the first only actual argument in the method and ldsfld which loads a value from a static field and since the only possible static that we have which makes sense to compare to is validKey, we can be pretty certain that it's using that value.