Making an Image Easier to Debug

I am doing security review for a managed application which is obfuscated. So I am doing a lot of   disassembling code at runtime using Windbg. One of the issues is that code gets JIT optimized because of the retail build. This makes it harder for me debug when mapping it back. Realized  that I could turnoff  JIT Optimization’s using the ini file.

[.NET Framework Debugging Control]

Another use of feature which I guess wasn’t really intended for.

Piracy in .NET Code – Part 3 – Even when the code is obfuscated

Continuing with my series on Piracy, in this post I am going to be  exploring how someone with little advanced knowledge in CLR / Windows can bypass important function calls like  license validation.

Most of the developers assume just because the code is obfuscated nobody can bypass the licensing logic. I am going to be demonstrating how to bypass certain function call,this is very similar to “Set Next Statement ” in VS. I am not going to be discussing on how to fix this problem.

Here is a sample code.

using System;
namespace Conosole
{   class Program
 static void Main(string[] args)

The code has only two instructions. First one writes to console and the next to reads from the console. I would want to bypass the call to that WriteLine function.

Loaded the assembly within windbg and then issued the command

sxe ld: clrjit

When the break-point hits ,issued the command to set a break-point on the Main Method

.loadby sos clr;.load sosex;!mbm *Program.Main;g

Then when the break-point hits for the Main method , issued the following command to disassemble the Main method.

!u ($ip)

0:000> !u ($ip)
Normal JIT generated code
Begin 00230070, size 2d

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 6:
00230070 55              push    ebp
00230071 8bec            mov     ebp,esp
00230073 50              push    eax
00230074 894dfc          mov     dword ptr [ebp-4],ecx
00230077 833d3c31180000  cmp     dword ptr ds:[18313Ch],0
0023007e 7405            je      00230085
00230080 e8ca5a6962      call    clr!JIT_DbgIsJustMyCode (628c5b4f)
>>> 00230085 90              nop

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 7:
00230086 8b0d30204a03    mov     ecx,dword ptr ds:[34A2030h] (“Test”)
0023008c e81b707a61      call    mscorlib_ni+0x2570ac (619d70ac) (System.Console.WriteLine(System.String), mdToken: 06000919)
00230091 90              nop

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 8:
00230092 e8cdc1d761      call    mscorlib_ni+0x82c264 (61fac264) (System.Console.Read(), mdToken: 0600090a)
00230097 90              nop

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 9:
00230098 90              nop
00230099 8be5            mov     esp,ebp
0023009b 5d              pop     ebp
0023009c c3              ret
0:000> bp 0023008c
0:000> g

Because I have private symbols the line information is shown. So the function I want to bypass is “0023008c e81b707a61      call    mscorlib_ni+0x2570ac (619d70ac) (System.Console.WriteLine(System.String), mdToken: 06000919)” and the ip for this is 0023008c , so went ahead and set a break-point on the address

bp 0023008c

When the break-point hits on 0023008c, I move pointer to the next instruction that I am interested in ,which is “00230092 e8cdc1d761      call    mscorlib_ni+0x82c264 (61fac264) (System.Console.Read(), mdToken: 0600090a)” to avoid the function being invoked and here is the command

r eip=00230092

0:000> g
Breakpoint 1 hit
eax=001837f0 ebx=00000000 ecx=024abb50 edx=0041efd0 esi=008196c0 edi=0041ef20
eip=0023008c esp=0041eef0 ebp=0041eef4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
0023008c e81b707a61      call    mscorlib_ni+0x2570ac (619d70ac)
0:000> r eip=00230092

Now we have managed to bypass the call to Console.WriteLine and here is my output .

So the key takeaway is to understand the working  of the platform closer to the metal, which can help us write better and secure code.

Piracy in .NET Code – Part 2 – Even when the code is obfuscated

Continuing with my previous post, one of the biggest security holes I have noticed in certain application is using unsecure Network I/O communication, especially when activating license. I have seen software where they have used the best tool to obfuscate the code, it is extremely hard to disassemble this. But lose out by invoking a web service with plaintext xml for registration and communication. Like I mentioned in my previous post, I am not going to be discussing on how to solve this problem.

I have seen applications which would have a trial period after which the application pops-up a dialog box for activating license. Only if license text matches the format criteria (like xxx-xx-xxxx-xx) the send button enables. To circumvent the disabled button, some of the smart developers could enable the button within the debugger (windbg /cdb) or using a something like HawkEye . This is the first line of defense for the application.

The next thing is launching something like Fiddler and checking out the web service request /response for the activation request. I am sure most of us have dealt with fiddler, and if you are not aware, fiddler gives an option to have a break-point on response from the server. So it is easy to do a Man-in-the-middle attack by injecting your own response or someone could even hack the lmhost file to act as a server.

And just because it is plain text, I have usually seen a bool variable in the response being activated or not. And it is not hard for someone to update the response text and pass it as a response back from the server.

The updated xml might not comply with the response expected by the application, it might throw an exception like this

0:020> !pe

Exception object: 045b4314

Exception type:   System.Xml.XmlException

Message:          Unexpected end of file while parsing Test  has occurred. Line 13, position 81.

InnerException:   <none>

StackTrace (generated):


StackTraceString: <none>

HResult: 80131940

Because it is xml, usually these applications end up using XmlTextReader to parse it and from the exception someone could figure out what element is expected and build the xml out of this. FYI the XmlTextReader library is usually never obfuscated.

The idea behind this post is to let the dev’s understand the security risks and someone with little advanced knowledge can exploit the holes. This is not just applicable for software piracy, but in general, especially with the RIA growing rapidly we would want to consider the security aspect also.

Piracy in .NET /Silverlight Code – Part 1 – Even when the code is obfuscated

This is going to be a series of posts where I am going to demonstrating how someone with little advanced knowledge in .NET can hack in to the code, and circumventing licensing logic. I know there are other ways to prevent this ,which I am not going to be discussing about.

The usual assumption is that, if the code is obfuscated and signed , then it is close to impossible for someone to hack in to the code because the method names and variables are jumbled up, which would prevent someone from figuring what is happening.

In this post I am going to demonstrate a simple application which would prevent the user from updating the DataGrid because the user had downloaded only a trial version of the software. The software was supposed to disable grid and prevent the consumer from adding or updating the existing data, in a trial version. This was customer case, whom I helped in figuring out the vulnerability.

Here is the list of things the software did to prevent users from not accessing features that they were not entitled to

  1. Obfuscated the code, that prevented the code from being disassembled.
  2. The code that validated the consumers role either paid / trial alone was maintained in a separate assembly .So that for trial consumer’s, assembly version always returned false, for key features and for the paid subscribers got the assembly version that would return true. The organization assumption was ,by not even having the assembly ,trial users would never be able to circumvent the licensing logic. The customer could not maintain two versions of entire software, one for the trial another one paid because of the cost involved in maintenance and that’s the driving factor behind having a separate assembly.
  3. The code was signed and this prevented the users from hacking into the code and changing the code.

So here is code for figuring out the user role

public interface ICustomer{
bool HasAccess();}

And the trial assembly version code for figuring the role had

public class TrialCustomer : ICustomer {
public bool HasAccess() {
return false;

And here is the code that actually use the above code

private void Button1Click(object sender, EventArgs e)
if (!this.customer.HasAccess()) {
MessageBox.Show("Available only for paid customer");
else {
this.dataGridView1.Enabled = true;

So the idea behind this was, the trial version user would always get false as response, and would never be able to hack, because consumer does not even have library that would return true.

So I was asked to check for vulnerabilities. The first step was to disassemble the code using reflector and you can imagine the code had all weird names because of obfuscation.

The next step was to run the application and check when I get a dialog box for “Available only for paid customer”. The reason behind this was to get a callstack ,because all the methods names are jumbled and I didn’t know where to start.

When the messagebox popped up I attached the application to windbg and here is callstack

0:000> !clrstack

OS Thread Id: 0×4850 (0)

Child SP IP       Call Site

004ce758 752b438d [InlinedCallFrame: 004ce758]

004ce754 636308ec DomainBoundILStubClass.IL_STUB_PInvoke(System.Runtime.InteropServices.HandleRef, System.String, System.String, Int32)

004ce758 636f4a53 [InlinedCallFrame: 004ce758] System.Windows.Forms.SafeNativeMethods.MessageBox(System.Runtime.InteropServices.HandleRef, System.String, System.String, Int32)

004ce7ac 636f4a53 System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window, System.String, System.String, System.Windows.Forms.MessageBoxButtons, System.Windows.Forms.MessageBoxIcon, System.Windows.Forms.MessageBoxDefaultButton, System.Windows.Forms.MessageBoxOptions, Boolean)

004ce7b0 001b0513 [InlinedCallFrame: 004ce7b0]

004ce84c 001b0513 c.a(System.Object, System.EventArgs)

004ce85c 630bfd6c System.Windows.Forms.Control.OnClick(System.EventArgs)

004ce874 630beb1e System.Windows.Forms.Button.OnClick(System.EventArgs)

004ce88c 636574b8 System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)

004ce8a8 63629639 System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)

004ce93c 639d1a87 System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)

004ce940 639f141d [InlinedCallFrame: 004ce940]

004ce994 639f141d System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)

004ce9d8 6312f8e0 System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)

004ce9e4 630ce493 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)

004ce9ec 630ce411 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)

004cea00 630ce356 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)

004ceba4 007a09e5 [InlinedCallFrame: 004ceba4]

004ceba0 631347dc DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)

004ceba4 630de59f [InlinedCallFrame: 004ceba4] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)

004cebe8 630de59f System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)

004cebec 630de1cc [InlinedCallFrame: 004cebec]

004cec84 630de1cc System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)

004cecdc 630de021 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)

004ced0c 630c5ecd System.Windows.Forms.Application.Run(System.Windows.Forms.Form)

004ced20 001b009a e.a()

004cef58 6d1d213b [GCFrame: 004cef58]

Notice that there is a method c.a(System.Object, System.EventArgs) on the top of stack the before the framework code. This was my starting point, and I looked up for it in the reflector

private void a(object A_0, EventArgs A_1)
if (!this.d.a())
MessageBox.Show("Available only for paid customer");
this.b.Enabled = true;

So my next step was to disassemble the call to “this.d.a()” and here is output from reflector

public class d : b
// Methods
public bool a()
return false;

And I was hoping there would be a class variable that I could update inside the debugger to activate the feature.  Like I mentioned I before ,I couldn’t update the assembly using ILASM or anything, because the assemblies were signed and there wasn’t any class variable to update. This assembly was for the trial version consumers which were supposed to always return false.

But there was one trick that I had. I could update the register on the function return, So what I mean is, when the function “a” is invoked by the button click to validate the consumer , I would update the return register  from false to true. By doing this my grid would be enabled and I circumvent the logic with the existing constraints.

So here are the steps to do it

  • So I looked for the type “d”  !dumpheap -type d, remember the TrialCustomer class  was the renamed  to d by obfuscator .
  • Go the method table as 00146b9c from the above command , using  the method table  I had to get the entry address for the function public bool a() because the buttonclick  was invoking the function.
  • I used the command  !dumpmt -md 00146b9c   to get the entry address for   d.a()  and here was the entry address 001b0540  .

0:005> !dumpmt -md 00146b9c

EEClass:      0020054c

Module:       00142e9c

Name:         d

mdToken:      02000009

File:         C:\Users\naveen\Documents\Visual Studio 2010\Projects\SecureApplication\bin\Debug\Dotfuscated\SecureApplication.exe

BaseSize:        0xc

ComponentSize:   0×0

Slots in VTable: 6

Number of IFaces in IFaceMap: 1


MethodDesc Table

Entry MethodDesc      JIT Name

649f5b34   64795750   PreJIT System.Object.ToString()

649c8be0   64795758   PreJIT System.Object.Equals(System.Object)

649c8af0   64795778   PreJIT System.Object.GetHashCode()

649e8aa0   6479578c   PreJIT System.Object.Finalize()

001b0540   00146b8c      JIT d.a()

0014c085   00146b94     NONE d..ctor()

  • The idea behind getting the address was to set a break-point on 001b0540  . I used the command bp 001b0540 . So when the break-point hits I would create another break-point for the function return, which  is stored in the register @esp  bp poi(@esp). So what bp poi(@esp) essentially means is,   create a break-point on the return of function  , it is almost like having a break-point on the last line inside a function within VS.NET.
  • So when the break-point hits for the function return ,the return value is stored in the @eax register and here is the output

0:000> g

Breakpoint 1 hit

eax=00000000 ebx=0230e6f8 ecx=0230cfcc edx=02328884 esi=0230ce7c edi=02328884

eip=001b04f2 esp=004ce8a0 ebp=004ce8a4 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

001b04f2 85c0            test    eax,eax

Now the trick is to update the @eax register from 00000000 to 00000001, by doing this we are changing the value from false to true, using the command  “r eax=00000001” and here is the output after updating the register

0:000> r

eax=00000001 ebx=0230e6f8 ecx=0230cfcc edx=02328884 esi=0230ce7c edi=02328884

eip=001b04f2 esp=004ce8a0 ebp=004ce8a4 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

001b04f2 85c0            test    eax,eax

Now we have managed to update return of the function, which in turn has let us update the grid ,which was supposed to be available only to the paid customers.

In the forth coming posts I will continue to post of few more things that I figured out.


Get every new post delivered to your Inbox.

%d bloggers like this: