Log in

View Full Version : Rebuilding native .NET exes into managed .NET exes by Exploiting lefotver IL...


rendari
March 21st, 2008, 23:20
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:
http://www.filesend.net/download.php?f=644ee1dfd1b9246aee11d64b931bd0fb

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

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

Code:
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=1.0.0.0, Culture=neutral, PublicKeyToken=null <debug>

H:\temp>

Then browse to your Global Assembly Cache at H:\WINDOWS\assembly\NativeImages_v2.0.50727_32\UnpackMe,
find UnpackMe.ni.exe, 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 UnpackMe.ni.exe. This is what you would get after a "native" protector
converted your IL code into native code. So, UnpackMe.ni.exe is all a reverser trying to reverse this
target would have availible to him.

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

Load UnpackMe.ni.exe 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:

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

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

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


if filename == null then
return
end

pehandle = OpenFile(filename)

if pehandle == null then
return
end

-- 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


end




-----------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------

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)
else
if SaveFileAs(pehandle, filename) == true then
MsgBox("File successfully saved.", "fast dotnet fix", MB_ICONINFORMATION)
else
MsgBox("Couldn't save file", "fast dotnet fix", MB_ICONEXCLAMATION)
end
end

return

NOTE: If you are working on an executable other than the one I provided, fill in
MethodRVA
OrigMSILRVA
NumberOfMethod
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 "Unpackme.ni.exe" to be processed, and save the processed
file as "fixing1.exe". Remember to save the changes we have done to Unpackme.ni.exe before executing this
script on it.

Now, close Unpackme.ni.exe 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 dump.il. 4 Files should have been generated from ILDasm. These
4 files are:
dump.il
dump.res
UnpackMe.Form1.resources
UnpackMe.Resources.resources

Now, execute ILAsm upon dump.il:

>ilasm dump.il

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.

Peace

-Rendari

Back to working on part 2...

dELTA
March 22nd, 2008, 06:12
Yet again, extremely interesting work rendari! I'm already longing for the next part.

rendari
March 22nd, 2008, 14:09
Cool, I'm glad you like it dELTA.


And, I understand that I don't have the most coherent writing method, and this isn't exactly the simplest of subject matters. So, if anyone has any questions, please feel free to ask them

dELTA
March 22nd, 2008, 14:34
Hehe, all contributions are good contributions, and I think your writing is just fine. Keep up the good work!

ksanket
July 19th, 2011, 04:09
hi rendari i was reading this thread and had some problem hope u will help out

1: ur unpackme isnt available

2: i tried this method using an empty form(compiled) but i didnt found methods in my dump was the dump not proper enough or some thing else went wrong

rendari
July 20th, 2011, 07:03
Hey Ksanket,

Sorry but I lost the target long ago. Can you upload yours somewhere and tell me what you are doing, so I can follow along?

-r

ksanket
July 23rd, 2011, 01:07
Here is what i am getting

Code:
http://www.mediafire.com/?1vr1m3s1zq2m6nl