Log in

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


rendari
March 17th, 2008, 19:50
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\HelloWorld5240658e5a51e36767993bef4ed510\
^
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\HelloWorld5240658e5a51e36767993bef4ed510

, 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\HelloWorld5313cb7765a1766ec21df7d7fa0d49\HelloWo rld.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

Daniel Pistelli
March 21st, 2008, 10:21
Thanks rendari for your work, it's interesting indeed. Also, you reminded me that my article about the salamander slipped someway out of ntcore during a backup/restore operation. I didn't do it on purpose. Maybe, I should re-add it to ntcore.

Btw, I want to add the native format as far as I can (it's undoc) in the next version of the CFF Explorer. If you try one native exe out in the CFF, you should already see some kind of primitive attempt of showing the native table of such an assembly.

rendari
March 21st, 2008, 11:53
Yep, I noticed that right after I wrote this article. I'm currently experimenting with extracting the IL from a native exe if the author forgot to remove it, in such a way that reflector can access the IL and decompile it. Currently I am getting mixed results. However, it did allow me to get more acquainted with CFF explorer, and the new scripting feature, which is quite useful

dELTA
March 22nd, 2008, 06:11
Very nice article rendari, your .NET research and publications are extremely interesting, and we're all looking forward to more!

And yes, CFF Explorer and its recent scripting additions are very useful indeed, I can really recommend people to look into it (not only for .NET stuff, but for all kinds of unpacking and other PE related work!). I'm also looking forward to the next upgrade of the scripting engine, containing the updates I discussed with Daniel the other day...

Daniel Pistelli
March 22nd, 2008, 11:44
Heh, the CFF VIII is a LONG way to go, but the scripting will be hugely amplified, no question about that.

Btw: http://ntcore.com/Files/salamander.htm

this is the correct link to the salamander article, since pmode is officially down. It's nice to see that others find the scripting useful, rendari used it in a very simple and effective way in the other post. dELTA was using it for an unpacking rebuilding job the other day. Sure a lot of things are still missing, but it was one hell of a work to create the first version and I was so tired of coding that I wanted to see first if people would like it.

JMI
March 22nd, 2008, 11:49
I believe that you may safely assume that as more and more people become familiar with the CFF Explorer and its scripting additions, the more they will be looking forward to the eventual release of CFF VIII. Such efforts certainly help to advance the completion of the tasks of a great many "reverser's."

Regards,

rendari
March 22nd, 2008, 14:15
Daniel, I cannot imagine the time and effort you must have put in CFF Explorer. What I can tell you is that it is one of the coolest and most useful programs in my toolkit, and not a day passes where I don't use it (I know that sounds weird, but its the truth!). So, thank you for all the effort you've put into this great program, and know that at least one person out there sincerely thanks you for all the work and effort you put into this beauty

-Peace

Rendari

Daniel Pistelli
March 22nd, 2008, 19:38
I read your comment earlier in the evening, rendari, and decided to answer later. I wanted to think about it a bit. One comment like this seriously repays me of all the effort I put into this project. The CFF Explorer started mainly as utility which I needed (for work) in order to analyze the .NET format. I was cautious. I didn't call the tool .NET Explorer, or something like this, because I wanted to keep my options open. I re-thought the PE approach from scratch and the core had a good abstraction: thus, a good potential to evolve in something more complicated. So, I decided to bring the project a little further. At the beginning I felt quite uncomfortable. I felt too old for playing "I write the new super cool PE editor" etc. How many PE editors were there already? When I was working on the first version of the CFF Explorer there was already lordpe (which I consider a good PE editor for the time it came out), PE Explorer (which has more features than lordpe, but I consider it a bad software: it doesn't even support PE64: hey, we're in 2008, wake up!), PE Tools, StudPE, PE Edit, PEView and many more. I really felt too old to keep playing with reversing toys. What kept me going was the belief that the core of the CFF was interesting and offered many more possibilities than existing PE Editors. In fact, it has the capability to evolve into a general file editor and this was my idea from the beginning (thus the name CFF: Common File Format Explorer). I'm going to do a huge (very big) update to the core in the next version. What keeps me going now is the fact that the core is very solid and there is a very good base to bring the project much further. Fact is the CFF is here to stay and it will evolve, I strongly believe it could become the standard such as IDA is in the disassembler category. I know what JMI means when he says familiarity with the CFF, but the scripting was only a shot. I had fun implementing it (although it was one hell of a work), but I really believed that no one would use it. Why? Well, many reasons, but for once it is a very new thing: a PE editor with its own scripting language... I'm extremely surprised seeing people use it. Anyway, thanks.