rendari

Some Quick Insights Into Native .NET exe's (part 1 of?)

Rating: 3 votes, 2.00 average.
So, what are natively compiled .NET exes? Well, to answer that question we have
to go back to how the .NET runtime works.

All your VB.NET and C# code is translated during compilation from the language
in question to MSIL (Microsoft Intermediate Language). This is a sort of stack
based assembler variant that, in combination with metadata present in the executable,
can be translated back into high level code (VB.net and C#). This is why tools
such as reflector and dotDecompile exist on the .NET platform; the same effect
is much harder to achieve with native code.

Basically, the new possibilities for decompilation introduced in the .NET
architecture have caused a lot of companies to shit themselves. Intellectual
property (IP) is the lifeblood of the company. If someone can rip the idea and
or source out of your product, and implement it in theirs, there is very little
you can do to prevent them. This is becoming a major issue in the .NET dev circles
(along with the fact that decompilation = easier cracking for malicous reverse
engineers bent upon pirating goods to disrupt the global economy and plunge the
world into deep recession).

So, .NET devs are a lot more willing to invest time and money in securing their
products. Frankly, given how shitty 99% of the "security" products on the market
are, they're wasting their time.

One of the security products that in my opinion falls into the "not well thought
out" category is anything that boasts security through native code. Look at it this
way: hackers have been cracking native code for how long? 2 decades? 3 decades?
What makes you think that all of a sudden, in the grand year of 2008, they will
start having problems hacking native code? Maybe it will be even EASIER for them
because they no longer have to adapt to the foreign .NET architecture, but now
simply have to load the exe into Olly and go at it the "old fashioned way".

So, back to the question at hand. What are natively compiled .NET exes? Basically,
they are exe's containing platform specific code that has been produced by the JIT
instead of the aforementioned MSIL code. This makes it impossible to decompile the
.NET code the old fashioned way (with Reflector).

Producing .NET exes isn't really that simple. To natively compile a .NET exe into a
native exe, you must use ngen.exe (comes with the SDK). Ngen is a native code
generator provided by M$, and it's purpose is to increase performance. An application
that demands performance might use Ngen to compile itself during installation.

The newly produced native executables are not runnable. In fact, they are not even
valid Win32 applications. They may be located in the Global Assembly Cache (GAC) of
the local machine where Ngen was executed. If you do not know what the GAC is, read
up on it here:

Demystifying the .NET Global Assembly Cache
By Jeremiah Talkar
http://www.codeproject.com/KB/dotnet/demystifygac.aspx

Now, I guess you're anxious to see the native exes I was just talking about. Desafortunadamente,
it is not possible for you to access the GAC with Windows explorer right away. Thankfuly,
a quick registry fix from the article quoted above will lets us browse the GAC from
Windows Explorer. I quote:

"If you want to disable the Assembly Cache Viewer and see the GAC in all its naked glory
within Windows Explorer, you can set HKLM\Software\Microsoft\Fusion\DisableCacheViewer [DWORD] to 1."

Simple, eh? Now, take any exe .NET exe (for example helloworld.exe) and execute Ngen upon it:


C:\Windows\system32>cd C:\temp

C:\temp>ngen HelloWorld.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50727.1433
Copyright (c) Microsoft Corporation. All rights reserved.
Installing assembly C:\temp\HelloWorld.exe
Compiling assembly C:\temp\HelloWorld.exe ...
HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null <debug>

C:\temp>pause
Press any key to continue . . .

There, we Ngen'd HelloWorld.exe. Now, whenever we execute HelloWorld.exe, the JIT will load
the native HelloWorld.exe from the GAC and execute it, instead of compiling the IL in the
HelloWorld.exe we are double clicking on. Like I said, this is intended to improve performance.

To find the native compiled HelloWorld.exe on our hard drive, browse to

C:\Windows\assembly\NativeImages_v2.0.50727_32\HelloWorld\355240658e5a51e36767993bef4ed510\
^
This is different on each computer.

You should see one file, and that is: HelloWorld.ni.exe. As I mentioned before, it is this file
that is loaded as a module when the JIT executes.

As you can see by double clicking on HelloWorld.ni.exe, it is not a valid image. This means that
you cannot distribute it. That is one of the downfalls of Ngen.exe. You must have a valid exe
with MSIL in it to generate an Ngen image. You cannot simply distribute the Ngen'd image as it
is invalid.

Salamander .NET Protector changes all this. This is one of the protectors that takes a somewhat
amusing approach towards solving the problem of invalid Ngen'd images. I took a quick look at it
by downloading the Scribble Demo from their homepage:
(no reversing involved, reversing is illegal you know :P )

http://www.remotesoft.com/linker/intro.html

Just look at the file paths in the link above:

\scribble-native\mdeployed\C\WINDOWS\assembly\NativeImages_v2.0.50727_32\Scribble\eccb67b11447c9488a7a35bab51a a59b

It will become quite obvious that all its doing is
emulating the GAC. It has its own GAC in there, with all the components scribble (native compiled)
needs. When you run scribble.exe, it initializes the .NET Jit and points it towards its own GAC. .NET
then grabs everything it needs from there (including native compiled scribble(Scribble.ni.exe))), and executes it.

Now that's just a hypothesis on how it works. Without real reversing I cannot verify my results.
And since reversing it would be illegal, I guess I'll have to pass up the opportunity this time :P

Now, how secure is salamander .net protector? Well, I guess compared to MSIL code native code is 10x more secure, but
does that mean that its really SECURE? Not necessarily.

First of all, there is a small bug in the protector where it makes it preserve the IL code from the original exe. Ntoskrnl
described it in his article that he did on salamander .net protector. You can find it here:

http://www.pmode.net/USERS/116/Files/salamander.htm

But lets just ignore the IL code in memory for now, and focus on attacking the native code. Remember that HelloWorld.exe from
earlier in the chapter? Let's try Ngenning that, and then having some fun with it :>

First off, we need both need to be looking at the same HelloWorld.exe. Here is the code that I used, just paste it into an
empty VB.net project and compile. For button1 you can put any button, so long as it executes the code below
BTW, for the purposes of this project I used VB.net express 2008

Code:
Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MessageBox.Show("Greetings")

    End Sub
End Class
Simple piece of work, eh? Will just pop out a messagebox saying Greetings, and that's that. Compile it, run it, and then
proceed to the next paragraph of this article.

Ok now, first of all lets generate a valid native image for HelloWorld.exe. Just do:

> ngen HelloWorld.exe

Now go to the global assembly cache

> cd C:\Windows\assembly\NativeImages_v2.0.50727_32\HelloWorld\355240658e5a51e36767993bef4ed510

, find HelloWorld.ni.exe, and copy it to some temporary folder.

Now do:

> ngen uninstall HelloWorld

I know this might not make sense to you, but just bear with me.

Now, take Olly, and open the original HelloWorld.exe (the one which we execute ngen upon). Find the IL code in the hex view
window (IL code is easy to recognize from a hex editor once you get used to looking at it; if you're having trouble finding
it in the exe gets its RVA with CFF Explorer orjust search for 2A (2A is the bytecode for the IL_Ret instruction)). Here is
how the beginning of the IL code looks for me from Olly:

Code:
00E221C0  13 30 02 00 3B 00 00 00  0.;...
00E221C8  1B 00 00 11 00 7E 0D 00  ...~..
00E221D0  00 04 14 28 58 00 00 0A  .(X...
00E221D8  0C 08 2C 20 72 0B 01 00  ., r.
00E221E0  70 D0 09 00 00 02 28 25  p...(%
00E221E8  00 00 0A 6F 59 00 00 0A  ...oY...
00E221F0  73 5A 00 00 0A 0B 07 80  sZ...
00E221F8  0D 00 00 04 00 7E 0D 00  ....~..
00E22200  00 04 0A 2B 00 06 2A 00  ..+.*.
...

end of IL code:

(scroll down)

Code:
00E22A10  00 00 04 02 7B 0C 00 00  ..{...
00E22A18  04 14 FE 01 16 FE 01 0B  
00E22A20  07 2C 0D 02 7B 0C 00 00  ,.{...
00E22A28  04 06 6F 54 00 00 0A 00  oT....
00E22A30  00 00 2A 00 3A 00 72 F7  ..*.:.r
00E22A38  00 00 70 28 55 00 00 0A  ..p(U...
00E22A40  26 00 2A 00 42 53 4A 42  &.*.BSJB
00E22A42h = end of IL code.

So, select everything from 00E221C0 - 00E22A42,
Right Click -> Binary -> Fill With 00's.

...and then...

Right Click -> Copy to executable file -> Save File -> HelloWorld.exe

... and overwrite the old HelloWorld.exe when prompted. Now close Olly and execute
Ngen upon the HelloWorld.exe we just saved:

> ngen HelloWorld.exe

You will get something like this outputted in the console window:

Code:
C:\Windows\system32>cd C:\temp

C:\temp>ngen HelloWorld.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50727.1433
Copyright (c) Microsoft Corporation.  All rights reserved.
Installing assembly C:\temp\HelloWorld.exe
    Compiling assembly C:\temp\HelloWorld.exe ...
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000001
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000002
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000003
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000004
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000005
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000006
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000007
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000008
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000009
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600000a
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600000b
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600000c
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600000d
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600000e
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600000f
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000010
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000011
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000012
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000013
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000014
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000015
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000016
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000017
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000018
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000019
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600001a
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600001b
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600001c
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600001d
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600001e
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600001f
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000020
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000021
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000022
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000023
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000025
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000026
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000027
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000028
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x6000029
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600002a
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600002b
Code size is zero. (Exception from HRESULT: 0x80131896) while compiling method t
oken 0x600002c
HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null <debug>

C:\temp>pause
Press any key to continue . . .
Don't Panic!

Just go look in the global assembly cache:

> cd C:\Windows\assembly\NativeImages_v2.0.50727_32

and you will see that Ngen installed HelloWorld.exe despite all the errors! Purty cool, eh?
Now take the original HelloWorld.ni.exe that I told you to save from before, and write it
over the HelloWorld.ni.exe that is currently in the global assembly cache at
C:\Windows\assembly\NativeImages_v2.0.50727_32

Now, execute the HelloWorld.exe that we just ran Ngen upon. (the one that made Ngen generate
all those errors). You will see that the application runs just fine!.

Wait a minute! How can this application be working if we just replaced all its IL code with
00 bytes? Simple, I sez, the .NET JIT is smart. It looks in the global assembly cache, and
sees that there is a native compiled exe already there, so it loads and executes that exe
instead of the exe we clicked on. It is completely independant of IL right now!

NOTE: If you look at HelloWorld.ni.exe that is in the GAC right now with a hex editor, you
will be able to find IL code in its .IL section. However, you can just replace that IL code
with 00's in the hex editor, save the file, and you will see that it still runs with no IL
code at all!. Don't believe me? Search the process memory with Winhex for the IL code, and
tell me if you find any. I certainly didn't.

So, we now have a .NET executable (HelloWorld.exe on the disc, HelloWorld.ni.exe in the GAC)
that has no IL code, and yet it still manages to execute. The question facing us now is, how
difficult is this to hack?

Well, allow me to demonstrate. Lets open up HelloWorld.exe in Olly. Tick "Break On New Module",
and start pressing F9 to run the application. Keep an eye on each module being loaded. We want
to stop when HelloWorld.ni.exe has been loaded from the GAC.

When it finally does load, take a look at its relevant info:

Code:
Executable modules, item 2
 Base=30000000
 Size=00018000 (98304.)
 Name=HelloW_1
 File version=1.0.0.0
 Path=C:\Windows\assembly\NativeImages_v2.0.50727_32\HelloWorld\545313cb7765a1766ec21df7d7fa0d49\HelloWorld.ni.exe
And looking at the Memory Map we see:

Code:
30000000   00001000   HelloW_1              PE header     Imag   R         RWE
30002000   00003000   HelloW_1   .text      code          Imag   R E       RWE
30006000   00001000   HelloW_1   .extrel    code          Imag   R         RWE
30008000   00004000   HelloW_1   .data      data          Imag   RW        RWE
3000C000   00001000   HelloW_1   .xdata                   Imag   RW        RWE
3000E000   00001000   HelloW_1   .dbgmap                  Imag   R         RWE
30010000   00003000   HelloW_1   .il                      Imag   R         RWE
30014000   00001000   HelloW_1   .rsrc      resources     Imag   R         RWE
30016000   00001000   HelloW_1   .reloc     relocations   Imag   R         RWE
Cool. So we now know where the native exe is loaded in memory. You can continue to browse around the module to get a
"feel" for it, and when you are ready you can continue reading this article.

Ok, so, let's decide what we're going to do to this guy. For the purposes of this demonstration I think I'll just hijack
the MessageBox to display my own message with code that I inject myself. Well, that's a bit of a problem, eh? .NET calls
its messageboxes through its own API, just like in VB6. So, breaking on MessageBoxA won't do you any good. But what we can
do is set a hardware bp on access on the "Greetings" unicode string, and see where it is accessed from. Chances are, that
it will be the VB.net messagebox API we are looking for.

You will find the "Greetings" Unicode in the .il section. Here is how it looks like for me:

Code:
30011B94  47 00 72 00 65 00 65 00  G.r.e.e.
30011B9C  74 00 69 00 6E 00 67 00  t.i.n.g.
30011BA4  73                       s
Set a harware breakpoint on the first 4 bytes, and press F9 to run. Click on the button in HelloWorld.exe to prompt
the displayal of the messagebox we are trying to hack, and watch as this triggers Olly's hardware BP that we set. It
should have shot off at some code that looks like this:

Code:
71E15160   > 8B448E F0      MOV EAX,DWORD PTR DS:[ESI+ECX*4-10]
71E15164   . 89448F F0      MOV DWORD PTR DS:[EDI+ECX*4-10],EAX
71E15168   > 8B448E F4      MOV EAX,DWORD PTR DS:[ESI+ECX*4-C]
71E1516C   . 89448F F4      MOV DWORD PTR DS:[EDI+ECX*4-C],EAX
71E15170   > 8B448E F8      MOV EAX,DWORD PTR DS:[ESI+ECX*4-8]
71E15174   . 89448F F8      MOV DWORD PTR DS:[EDI+ECX*4-8],EAX
71E15178   > 8B448E FC      MOV EAX,DWORD PTR DS:[ESI+ECX*4-4]
71E1517C   . 89448F FC      MOV DWORD PTR DS:[EDI+ECX*4-4],EAX
71E15180   . 8D048D 0000000>LEA EAX,DWORD PTR DS:[ECX*4]
71E15187   . 03F0           ADD ESI,EAX
71E15189   . 03F8           ADD EDI,EAX
71E1518B   > FF2495 9451E17>JMP DWORD PTR DS:[EDX*4+71E15194]
Now, start tracing by holding down F8. What we want to do is trace out of the VB.net API and back into HelloWorld.ni.exe
Do not use CTRL+F9, as it will just confuse Olly. After a large amount of tracing, you will eventually arrive to some native
code that looks like this:


Code:
30003B7C   . 83EC 08        SUB ESP,8
30003B7F   . 890C24         MOV DWORD PTR SS:[ESP],ECX
30003B82   . 895424 04      MOV DWORD PTR SS:[ESP+4],EDX
30003B86   . 8B05 0CC00030  MOV EAX,DWORD PTR DS:[3000C00C]          ;  HelloW_1.300081CC
30003B8C   . 8338 00        CMP DWORD PTR DS:[EAX],0
30003B8F   . 74 05          JE SHORT HelloW_1.30003B96
30003B91   . E8 B147124A    CALL mscorwks.7A128347
30003B96   > 90             NOP
30003B97   . 8B05 40BD0030  MOV EAX,DWORD PTR DS:[3000BD40]
30003B9D   . 8B08           MOV ECX,DWORD PTR DS:[EAX]
30003B9F   . FF15 D4BD0030  CALL DWORD PTR DS:[3000BDD4]             ;  System_W.7B285938
30003BA5   . 90             NOP
30003BA6   . 90             NOP
30003BA7   . 90             NOP
30003BA8   . 83C4 08        ADD ESP,8
30003BAB   . C2 0400        RETN 4
Note the CALL DWORD PTR at 30003B9F. This is where the VB.NET API for messageboxes is called. It will then continue its execution.

Now, at this point in the tutorial I was planning on showing you guys how to inject your own code here that shows your own
messagebox. But, alas its getting late and my fingers are cold. And besides, now that you know where and what to hook, it should be
simple to do

I don't feel like continuing to write this article. My main
purpose here is done, and that is to show you that with a bit of common sense native .NET code is nothing. Using hardware
breakpoints, and the Method names of functions from mscorwks.dll which you get by disassembling mscorwks.dll in IDA, you should
be able to figure out how a native .NET program functions. Maybe next week I will show you how to crack a simple Keygenme reading
the native code from Olly, and making a loader for it. But for now I am tired, I have no motivation, and I leave you to your
own explorations.

In conclusion:

.NET native code isn't so godlike. It won't prevent cracking.
Crackers have been cracking native code for years, and they'll keep on doing it. I suppose that this is an effective way to
safeguard your intellectual property, but don't be too quick to draw conclusions. Even the native images of these exes have
the original IL and metadata left in them, and if they're unremoved you've done nothing to deter hackers.

The part of the article that I hope interests others is where I make my own .NET native exe. I think that it is possible to
automate this process, and thus make it possible for other people to make their own .NET native exe protection. Hell, I am
thinking about implementing it as an option in a future release of Cryxenet (my .NET protector). Which reminds me, an update
of Cryxenet is long overdue... meh, I'll upload a new ver to crackmes.de this weekend, after I've improved the crypto a bit more.

As for now, sleeeeeep

Peace all!

-Rendari

Submit "Some Quick Insights Into Native .NET exe's (part 1 of?)" to Digg Submit "Some Quick Insights Into Native .NET exe's (part 1 of?)" to del.icio.us Submit "Some Quick Insights Into Native .NET exe's (part 1 of?)" to StumbleUpon Submit "Some Quick Insights Into Native .NET exe's (part 1 of?)" to Google

Categories
Uncategorized

Comments