sexta-feira, 3 de fevereiro de 2012

(.Net) EP! (.Net) EP! Hurra!!



The subject of this post will be about the different paths we can opt to find the EP in a .Net process. As I’m very lazy, instead of creating my own demo program, I'll be working with a sample program called 02simple.exe that came with Advanced .Net debugging book, and available with source from its online site http://advanceddotnetdebugging.com/. In hope that Mario Hewardt forgives my "unauthorized" use of his software, let me give him my endorsement, by saying that the book is great. Be it for those who debug or reverse engineer .Net applications, be it for those who program in .Net and are trying to delve into the under layers of the CLR engine.
 
So, Let's first find out what we're trying to hit. Using Ildasm, available on the .NET SDK:

  
Dumping the metainfo in ildasm:

===========================================================
ScopeName : 02Simple.exe
MVID      : {39294D5C-77D0-4D3C-8BBC-18B40FFDE70A}
===========================================================
Global functions
-------------------------------------------------------
Global fields
------------------------------------------------------
Global MemberRefs
-------------------------------------------------------
TypeDef #1 (02000002)
-------------------------------------------------------
      TypDefName: Advanced.NET.Debugging.Chapter2.Simple  (02000002)
      Flags     : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100000)
      Extends   : 01000001 [TypeRef] System.Object
      Method #1 (06000001) [ENTRYPOINT]
      -------------------------------------------------------
            MethodName: Main (06000001)
            Flags     : [Private] [Static] [HideBySig] [ReuseSlot]  (00000091)
            RVA       : 0x00002050
            ImplFlags : [IL] [Managed]  (00000000)
            CallCnvntn: [DEFAULT]
            ReturnType: Void
            1 Arguments
                  Argument #1:  SZArray String
            1 Parameters
                  (1) ParamToken : (08000001) Name : args flags: [none] (00000000)
      Method #2 (06000002)
      -------------------------------------------------------
            MethodName: .ctor (06000002)
            Flags     : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor]  (00001886)
            RVA       : 0x0000205e
            ImplFlags : [IL] [Managed]  (00000000)
            CallCnvntn: [DEFAULT]
            hasThis
            ReturnType: Void
            No arguments.
...
...
  
The entry point method is called Main as it is marked with a couple of special flags: STATIC and ENTRYPOINT. This is all information that we need to confirm our future findings, so let's start with windbg.
Open executable in windbg, first break is fired at:

ntdll!DbgBreakPoint:
7c90120e cc              int     3
7c90120f c3              ret

There is nothing here yet:

0:000> ~
.  0  Id: e28.91c Suspend: 1 Teb: 7ffdd000 Unfrozen

If we step through, we can see that we stopped during the loading phase:
0:000> t
eax=00241eb4 ebx=7ffde000 ecx=00000000 edx=00000001 esi=00241f48 edi=00241eb4
eip=7c90120f esp=0012fb20 ebp=0012fc94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint+0x1:
7c90120f c3              ret
0:000> t

eax=00241eb4 ebx=7ffde000 ecx=00000000 edx=00000001 esi=00241f48 edi=00241eb4
eip=7c940442 esp=0012fb24 ebp=0012fc94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!LdrpInitializeProcess+0xffa:
7c940442 8b4368          mov     eax,dword ptr [ebx+68h] ds:0023:7ffde068=00000470

If we try to load sos:

.loadby sos mscorwks
Unable to find module 'mscorwks'

We're presented with an error message.
Let's try to load psscor2:

.load psscor2

It seems that it was successful, lets run some commands:

!help
-------------------------------------------------------------------------------
PSSCOR is a debugger extension DLL designed to aid in the debugging of managed
programs. Functions are listed by category, then roughly in order of
importance. Shortcut names for popular functions are listed i...
...

Ok, some more:

!dumpdomain
Failed to find runtime DLL (mscorwks.dll), 0x80004005
Extension commands need mscorwks.dll in order to have something to do.

Yes, psscor2.dll is much friendlier than sos.dll, maybe because it is used by Microsoft employees. Both errors relate to the same situation, mscorwks.dll isn't yet loaded, so we need to wait until this module is loaded before trying to use any of the debugger metadata interpreter extensions commands.
So, first approach is to:

0:000> sxe ld mscorwks.dll
0:000> g
ModLoad: 79e70000 7a400000   C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
eax=00000000 ebx=00000000 ecx=008f0000 edx=7c90e514 esi=00000000 edi=00000000
eip=7c90e514 esp=0012f1bc ebp=0012f2b0 iopl=0         nv up ei ng nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000296
ntdll!KiFastSystemCallRet:
7c90e514 c3              ret
0:000> k
ChildEBP RetAddr 
0012f1b8 7c90d52a ntdll!KiFastSystemCallRet
0012f1bc 7c91bd23 ntdll!NtMapViewOfSection+0xc
0012f2b0 7c91626a ntdll!LdrpMapDll+0x330
0012f570 7c9164d3 ntdll!LdrpLoadDll+0x1e9
0012f818 7c801bbd ntdll!LdrLoadDll+0x230
0012f880 7900921b KERNEL32!LoadLibraryExW+0x18e
0012f8a4 7900923e mscoree!WszLoadLibraryEx+0x75
0012f8bc 79007b5a mscoree!LoadLibraryWrapperForEE+0x10
0012ffa4 79007c02 mscoree!GetInstallation+0x1cc
0012ffc0 7c817077 mscoree!_CorExeMain+0x12
0012fff0 00000000 KERNEL32!BaseProcessStart+0x23

Lets try it again:

0:000> .loadby sos mscorwks

No error, so the load was successful. Let's try running some commands:

0:000> !dumpdomain
--------------------------------------
System Domain: 00000000
Unable to get system domain info

Yey!! It worked. But we've got only the system domain, and even the system domain doesn't seem to be fully setup. Let's try to resolve the main entry to a valid IP address:

0:000> !name2ee 02simple Advanced.NET.Debugging.Chapter2.Simple.Main

Nothing happened. Damn. We can't breakpoint anything yet, because nothing has been setup yet. So let's resort to sosex.dll by Steve Johnson:

0:000> !load sosex
0:000> !mbm Advanced.NET.Debugging.Chapter2.Simple.Main
The breakpoint could not be resolved immediately.
Further attempts will be made as modules are loaded.
0:000> g
(a64.2c8): CLR notification exception - code e0444143 (first chance)
(a64.2c8): CLR notification exception - code e0444143 (first chance)
Breakpoint: Matching method Advanced.NET.Debugging.Chapter2.Simple.Main resolved, but not yet jitted. Setting JIT notification...
(a64.2c8): CLR notification exception - code e0444143 (first chance)
Breakpoint: JIT notification received for method Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[]).
Breakpoint set at Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[]).
Breakpoint 0 hit
eax=00922ff0 ebx=0012f4ac ecx=01292e14 edx=00000000 esi=00181718 edi=00000000
eip=00c70085 esp=0012f47c ebp=0012f480 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
00c70085 90              nop

We broke at 00c70085. Let's see what we've got there:

0:000> !u @eip
Normal JIT generated code
Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[])
Begin 00c70070, size 27
00c70070 55              push    ebp
00c70071 8bec            mov     ebp,esp
00c70073 50              push    eax
00c70074 894dfc          mov     dword ptr [ebp-4],ecx
00c70077 833d142e920000  cmp     dword ptr ds:[922E14h],0
00c7007e 7405            je      00c70085
00c70080 e83ca64579      call    mscorwks!JIT_DbgIsJustMyCode (7a0ca6c1)
>>> 00c70085 90              nop
00c70086 8b0d30202902    mov     ecx,dword ptr ds:[2292030h] ("Welcome to Advanced .NET Debugging!")
00c7008c e88738b278      call    mscorlib_ni+0x6d3918 (79793918) (System.Console.WriteLine(System.String), mdToken: 060007c8)
00c70091 90              nop
00c70092 90              nop
00c70093 8be5            mov     esp,ebp
00c70095 5d              pop     ebp
00c70096 c3              ret

We're in!! But the code is already Jitted, and we still needed to know the function name we're looking for, in order to set a breakpoint on it. Although we were able to find the EP in memory, why should we need to resort to ildasm?
Let's try it another way. From the beginning again:

0:000> bu mscorwks!ClassLoader::RunMain
0:000> g
*** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\7124a40b9998f7b63c86bd1a2125ce26\mscorlib.ni.dll
Breakpoint 1 hit
eax=0012f81c ebx=00000000 ecx=79f54ea1 edx=80000001 esi=00922ff0 edi=00000000
eip=79f4088d esp=0012f7e4 ebp=0012fa48 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mscorwks!ClassLoader::RunMain:
79f4088d 6838010000      push    138h
0:000> .load psscor2
0:000> !dumpdomain
--------------------------------------
System Domain: 7a3bd058
LowFrequencyHeap: 7a3bd07c
HighFrequencyHeap: 7a3bd0c8
StubHeap: 7a3bd114
Stage: OPEN
Name: System Domain
--------------------------------------
Shared Domain: 7a3bc9a8
LowFrequencyHeap: 7a3bc9cc
HighFrequencyHeap: 7a3bca18
StubHeap: 7a3bca64
Stage: OPEN
Name: Shared Domain
Assembly: 0018f638
--------------------------------------
Domain 1: 0014c488
LowFrequencyHeap: 0014c4ac
HighFrequencyHeap: 0014c4f8
StubHeap: 0014c544
Stage: OPEN
SecurityDescriptor: 0014d7b0
Name: 02Simple.exe
Assembly: 0018f638 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 0018f6b8
SecurityDescriptor: 0018d310
  Module Name
790c1000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
Assembly: 00193508 [D:\development\programming\debuggingdotnet\02Simple.exe]
ClassLoader: 00197f30
SecurityDescriptor: 001933d0
  Module Name
00922c5c D:\development\programming\debuggingdotnet\02Simple.exe

As seen, the domains have been setup, and ClassLoader::RunMain is about to run the method we want having ESI pointing to it. esi=00922ff0. Let's confirm this:

!dumpmd @esi
Method Name: Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[])
Class: 0092125c
MethodTable: 00923004
mdToken: 06000001
Module: 00922c5c
IsJitted: no
m_CodeOrIL: ffffffff

Yes. We reached EP, fast and simple without having to reverse or decode anything. Just using windbg. Let's what more we can get from here:

0:000> !name2ee 02simple.exe Advanced.NET.Debugging.Chapter2.Simple.Main
Module: 00922c5c (02Simple.exe)
Token: 0x06000001
MethodDesc: 00922ff0
Name: Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[])
Not JITTED yet. Use !bpmd -md 00922ff0 to break on run.

Everything checks. Notice something cool: the code is not jitted yet. So we can check it against the ildasm output:


0:000> !DumpIL @esi
ilAddr = 00402050
IL_0000: nop
IL_0001: ldstr "Welcome to Advanced .NET Debugging!"
IL_0006: call System.Console::WriteLine
IL_000b: nop
IL_000c: ret

How cool is this? Can we change the IL on runtime? Perhaps with some sort of hooking?
Now, let the crl jit the code and watch the result:

!bpmd -md 00922ff0
MethodDesc = 00922ff0
Adding pending breakpoints...
0:000> g
(c34.7cc): CLR notification exception - code e0444143 (first chance)
JITTED 02Simple!Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[])
Setting breakpoint: bp 00C70070 [Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[])]
Breakpoint 2 hit
eax=00922ff0 ebx=0012f4ac ecx=01292e14 edx=00000000 esi=00181718 edi=00000000
eip=00c70070 esp=0012f484 ebp=0012f490 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
00c70070 55              push    ebp

We're back to the same point before:

!u @eip
Normal JIT generated code
Advanced.NET.Debugging.Chapter2.Simple.Main(System.String[])
Begin 00c70070, size 27
>>> 00c70070 55              push    ebp
00c70071 8bec            mov     ebp,esp
00c70073 50              push    eax
00c70074 894dfc          mov     dword ptr [ebp-4],ecx
00c70077 833d142e920000  cmp     dword ptr ds:[922E14h],0
00c7007e 7405            je      00c70085
00c70080 e83ca64579      call    mscorwks!JIT_DbgIsJustMyCode (7a0ca6c1)
00c70085 90              nop
00c70086 8b0d30202902    mov     ecx,dword ptr ds:[2292030h]
00c7008c e88738b278      call    mscorlib_ni+0x6d3918 (79793918) (System.Console.WriteLine(System.String), mdToken: 060007c8)
00c70091 90              nop
00c70092 90              nop
00c70093 8be5            mov     esp,ebp
00c70095 5d              pop     ebp
00c70096 c3              ret

Hope you enjoyed it.

terça-feira, 3 de janeiro de 2012

Beware of stress. Don't (.Net) suppress.

.Net disassembly protection or not?

Since ILdasm.exe version 1 was born it has become one of the best tools for reversing .Net assemblies. As ILdasm is provided by Microsoft I would like to say: thank you, Microsoft. But somewhere along the way, Microsoft must have reflected on the dangers that posed for their clients (and perhaps for the framework usage) having a tool (built by Microsoft itself) lying around that empowered the user with the ability to reverse .NET code. So ILdasm.exe version 2.0 appeared. In version 2 Microsoft decided that some kind of protection was in order, so they armored ILdasm with a safety protection to limit its usage. Yet, it is the software author that, if he wants it to be protected from disassembly, needs to enforce this protective policy on his code. The protection is added with a simple assembly attribute:

using System;
using System.Runtime.CompilerServices;

[assembly: SuppressIldasmAttribute()]
namespace HelloWorld {
       class Hello {
              static void Main(string[] args) {
                     Console.WriteLine("Hello World!");
              }
       }
}

The corresponding MSIL is:

.assembly sample
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.SuppressIldasmAttribute::.ctor() = ( 01 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}

Compile this code and then try to visualize it in ILdasm 2.0. You'll get the following error:

Now comes the funny part. If you use the ILdasm version 1, this is useless as it is only enforced by ILdasm code in version 2, as seen here:

So, this is easily bypassed. You can use any other IL decompiler like ILdasm version 1. You can edit the binary file, look for the key ascii string “SuppressIldasmAttribute”, like in the following picture, and replace its content by NULL bytes.

Before edit

After edit

If editing the application isn’t your thing and you really, really, really want to use ILdasm 2.0, fear not, you can patch the “call HasSuppressingAttribute” by a “xor eax, eax” and all will be well again.

But wait, there’s yet another bypass provided by Microsoft. In their documented SuppressIldasmAttribute Class  (http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.suppressildasmattribute.aspx) they state the following: “This attribute does not prevent an assembly from being viewed using reflection.”.

In conclusion, as I still keep seeing some people relying exclusively on this to protect their Intellectual property, I’d like to say to them: please don’t use this on your protection schemes. It will save us all time.