Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL...

Rating: 2 votes, 2.50 average.
In the last article I wrote about native exe's, I briefly touched upon the issue of IL code being
left over in generated Native EXE's, and how this could be a possible security hole that hackers could
exploit when trying to reverse your .NET app. How big of a hole is this, you might ask? Very big. I
managed to fully rebuild my old managed .NET exe just from the native image provided by Ngen. I aim
to explain the steps I took in this blog post.

First things first, we will be using this .NET exe for out experimentations:

It's just a simple Unpackme I wrote and submitted to If you suspect malicous code, feel
free to decompile it in Reflector.

Anyways, extract the exe anywhere, and execute Ngen upon it

H:\temp>ngen unpackme.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50727.42
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Installing assembly H:\temp\unpackme.exe
Compiling 1 assembly:
    Compiling assembly H:\temp\unpackme.exe ...
UnpackMe, Version=, Culture=neutral, PublicKeyToken=null <debug>

Then browse to your Global Assembly Cache at H:\WINDOWS\assembly\NativeImages_v2.0.50727_32\UnpackMe,
find, and copy it over to some temporary location, where we can "work" on it.

Now, the interesting thing about Ngen is that it does not eliminate the IL or the metadata, because
while the IL code is not needed for execution, the metadata is, because all the strings and other
relevant data that the program needs are contained within the metadata. So, Ngen copies all the
metadata to the .IL section of the native exe, and copies the IL code as an afterthought (I assume
for debugging purposes). Anyways, the point is that native exe's generated with Ngen have the
IL code and metadata still in them, so a wily hacker should be able to use them to rebuild a managed
exe similar to the original exe before Ngen had its way with it. The hacker can then load this
managed exe into Reflector to decompile it, and steal your code or crack it with ease.

NOTE: managed code = bytecode (decompilable)
unmanaged code = native code (I wish we could decompile that =( )

This is a major issue because all protectors that boast converting IL code to native code to protect
your programs all use Ngen, so if the protectors forget to remove the IL you (the reverser) can just
reconstruct the original exe and then have fun with it =)

Anyways, so you have a copy of This is what you would get after a "native" protector
converted your IL code into native code. So, is all a reverser trying to reverse this
target would have availible to him.

Now, let us proceed to convert into a decompilable exe =).

Load into CFF explorer, and browse around a bit. You will see that the exe is not
a valid win32 exe (no entrypoint) and that there appears to be next to no metadata (No Methods metadata
table or any of that other good stuff...). Seems like we have our work cut out for us.

In hindsight: no, not really.

First things first: we want to have valid metadata. Thankfuly, with CFF explorer this is easy to
achieve since Daniel Pistelli (the author) documented the native header of .NET exe's enough for us
so that we don't have to use a hex editor to find the IL code/metadata anymore (btw, thanks Daniel
for this neat lil tool =) )

In CFF Explorer, go to a little tab on the left market "Native header". You will see that the "Native
Header" is mostly populated with unknown RVA's and sizes. However, there are 4 DWORD's that are of interest
to us. They are:
Original metadata RVA - 0x00012000
Original metadata size - 0x00002BC0
Original MSIL code RVA - 0x00014D30
Original MSIL Code Size - 0x00000DBF

Ok, write down these values somewhere. Now, go to the tab in the left of CFF Explorer marked ".NET directory",
and replace the values "Metadata RVA" and "Metadata Size" with the "Original metadata RVA" and
"Original metadata size" values that we got from the native header. Also change the Flags dword from
00000004 (IL Library) to 00000004 (IL only). Cool, now click on the "Tables" tab, so that we can browse the
metadata tables that now became availible to us.

The "Method" tables are of particular interest to us, because these are the ones that tell us where the IL code
is. So, click on the "Method" tables to open the list. There are 56 Methods btw. Note that, since we will need
that later on. Now, click on the first Method you see. In this case, it is cctor. CFF Explorer will then display
all the interesting things about the "cctor" method. If you look at the method RVA, you will see that it is
00000004. That's way too low. So, what is wrong here? Well, that RVA is actually the displacement from the
"Original MSIL code RVA" value in the Native header. So, to get the "real" RVA is actually:

cctor RVA + Original MSIL code RVA = Real RVA.

Our next step, therefore is to replace all the RVA's in these tables with their "Real RVA's", so that reflector
and ILDasm may load them. I wrote a primitive little CFF explorer script for this. Here it is:

-- this functions checks if a flag is set
function IsFlag(value, flag)
    if (value & flag) == flag then
        return true
    return false

-- --------------------------------------------------
-- the main code starts here
-- --------------------------------------------------

filename = GetOpenFile("Open...",  "All\n*.*\nexe\n*.exe\ndll\n*.dll\n")

if filename == null then

pehandle = OpenFile(filename)

if pehandle == null then

-- get dotNet Directory offset if any

dotnetoffset = GetOffset(pehandle, PE_DotNETDirectory)


MethodRVA = 0x0000D406
MethodCounter = 0
OrigMSILRVA = 0x00014D30
NumberOfMethod = 56
junkDWORD = 0

while MethodCounter < NumberOfMethod do

    junkDWORD = ReadDword(pehandle, MethodRVA)
    junkDWORD = junkDWORD + OrigMSILRVA
    WriteDword(pehandle, MethodRVA, junkDWORD)
    MethodRVA = MethodRVA + 0x0E
    MethodCounter = MethodCounter + 1      



    filename = GetSaveFile("Save As...",  "All\n*.*\nexe\n*.exe\ndll\n*.dll\n")

    if filename == null then
        MsgBox("Couldn't save file", "fast dotnet fix", MB_ICONEXCLAMATION)
        if SaveFileAs(pehandle, filename) == true then
            MsgBox("File successfully saved.", "fast dotnet fix", MB_ICONINFORMATION)
            MsgBox("Couldn't save file", "fast dotnet fix", MB_ICONEXCLAMATION)

NOTE: If you are working on an executable other than the one I provided, fill in
with the appropriate values from the exe you are working on.

NOTE: MethodRVA = the RVA of the first Method table entry in the metadata. In this case, the offset of
where the Method table for the "cctor" method starts.

Take the above script, and copy paste it into a new file called *anything*.cff. Then double click on the
script to get CFF explorer to execute it, select "" to be processed, and save the processed
file as "fixing1.exe". Remember to save the changes we have done to before executing this
script on it.

Now, close and open fixing1.exe in CFF explorer. If you look at the Method metadata table, you
will see that all the Method RVA's have been sucessfuly fixed. Now, in the list of all methods find the
"Main" method. This is the entrypoint of the exe. We will see that it as
"2 - (Main)" in the list of Method's. So, from this we can deduce that the entrypoint token for this exe is
0x06000002. If you don't know what a token is, look up the term in any paper on the .NET file format.

So, we know that the entrypoint token is 0x06000002. Go to the ".NET Directory" tab, and you will see there
that the EntryPointToken DWORD is 0. Replace it with 0x06000002 and save the exe.

Finally, go to Optional Header Tab. In the WORD for Subsystem, change it from 0003(CUI) to 0002 (GUI). Also,
again in the optional header change the Imagebase from 30000000 to something normal, such as 00400000.

Hmm, that should be about it. Save all our changes so far, and close CFF Explorer. Now, fire up ILDasm, and
open fixing1.exe in it. Then, in ILDasm do:

File -> Dump -> OK -> and save it somewhere as 4 Files should have been generated from ILDasm. These
4 files are:

Now, execute ILAsm upon


and it will produce dump.exe.

Now, uninstall the unpackme from the GAC, since we dont need it anymore

>ngen uninstall unpackme

Now, execute dump.exe. It works, yay ^^. Load it into reflector, look around and compare it to the unpackme.exe
I provided. Yep, everything looks fine.

In conclusion: .NET devs beware. Be sure to remove IL from your native exe with a hex editor, or else hackers can make it as
if you never protected the exe. That is all.



Back to working on part 2...

Submit "Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL..." to Digg Submit "Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL..." to Submit "Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL..." to StumbleUpon Submit "Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL..." to Google