Cracking by example: .Net

Disclaimer:

The information presented in this article is for educational and research purposes only. Any actions and/or activities related to the material contained within this article are solely your responsibility. The misuse of the information from this website can result in criminal charges brought against the persons in question. The authors will not be held responsible in the event any criminal charges be brought against any individuals misusing the information in this website to break the law.

Some time ago I cracked a simple .Net application called DiffEngineX (version 2.26.0.0) which basically is a diff for Excel, mostly for fun. I didn't release this crack but I thought I could share the process with those interested, so here we go.

The demo of this application has all the features but is limited to 30 days.
Simply by bypassing this check we could have directly the full application without the annoying 30-day reminder or having to change the system clock (which by the way works).

So to crack this I'll be using Windows XP and 2 more tools: the ILSpy Debugger and an hexadecimal editor (for example HxD).

So we install the DiffEngineX demo and open the DiffEngineX.exe executable with ILSpy Debugger (Debugger -> Debug).
For now we will stop debugging (Debugger -> Detach) and explore its code by expanding its tree entry.
One of the missing features in ILSpy is that we can't search the whole code. So for this we're going to export the whole code in a single file: File -> Save Code... -> C# single file. We can now open it in any text editor and search for the keyword "register".
This is obviouslt the "Register Now" button shown in the registration window. The program of course has to check first if the program is registered to know if it has to show this window, so the checking part must be right before the call to show this window. As we can see, this button is initialized in the method a() of the class Y that subclasses Form. To check from where this is called, we use Analyze in ILSpy.
We go on until p.a(string[]):int, which definitely looks like a main() and most likely the entry point of the whole application. Here we can see that p.i() is the call that shows the registration window. We can also quickly guess that if s.m() returns false, then the registration window will show up. If we check the code we will also see that it seems that p.i() is only called from here. So now we have 2 immediate methods: patch this method so it doesn't call s.m() or patch s.m() so it always returns true. I will go for the second method.

The code for s.m() is quite simple:

public static bool m()
{
    string text;
    bool flag;
    DateTime dateTime;
    return s.a(out text, out flag, out dateTime);
}

Now if we click in the C# dropdown we can select IL and we can see the assembly:

.method public static hidebysig
    bool m () cil managed
{
    // Method begins at RVA 0x1e6f8
    // Code size 12 (0xc)
    .maxstack 3
    .locals init (
        [0] string
        [1] bool
        [2] valuetype [mscorlib]System.DateTime
    )
    IL_0000: ldloca.s 0
    IL_0002: ldloca.s 1
    IL_0004: ldloca.s 2
    IL_0006: call bool s::a(string&, bool&, valuetype [mscorlib]System.DateTime&)
    IL_000b: ret
}

The returned value in MSIL is the value on top of the stack, so if we push a true on the stack before the ret call, this method will always return true. So we want to change this method like this:

.method public static hidebysig
    bool m () cil managed
{
    // Method begins at RVA 0x1e6f8
    // Code size 12 (0xc)
    .maxstack 3
    .locals init (
        [0] string
        [1] bool
        [2] valuetype [mscorlib]System.DateTime
    )
    IL_0000: nop      // We remove ldloca.s because they mess up with the stack
    IL_0001: nop
    IL_0002: nop
    IL_0003: nop
    IL_0004: nop
    IL_0005: nop
    IL_0006: nop
    IL_0007: nop
    IL_0008: nop
    IL_0009: nop
    IL_000a: ldc.i4.1 // Push 1, "true" (0 is false, any other value is true)
    IL_000b: ret      // Return true
}

[MSIL instructions reference]

All the nops are to keep the method size intact. So we now have to modify the old s.m() method with the new one in the executable file. For this we're going to search the s.m() instructions, which are:

1200 (ldloca.s 0)
1201 (ldloca.s 1)
1202 (ldloca.s 2)
28 (call beginning)

So we open DiffEngineX.exe in our hexadecimal editor and we search for the hexadecimal value 12001201120228. We will find 2 results:

The one we're searching for is the first result (offset 1D704) because after the call (28 FB 03 00 06), you will find a 28 (ret) which is not present in the second result.

Now we modify the old code 12 00 12 01 12 02 28 FB 03 00 06 with our code 00 00 00 00 00 00 00 00 00 00 17 and save the executable.

If we run it we can effectively see that it no longer asks for any kind of registration, but if we go to Help -> About, it still shows unregistered. I also managed to have it show as registered, but this I leave to the reader as an exercise ;)

Have fun!

Comments

Post a Comment

Comment, motherf*cker

Popular Posts