I read an interesting article the other day that spoke about the various mechanisms a Win32 application can employ for deleting itself from the disk once execution completes. The basic issue is, of course, that while the module is being executed, the operating system has the file locked. So, something like this will just not work:

Of the various options available, the author of the said article suggests the following approach as being the definitive one as it has the added benefit of functioning correctly on all versions of Microsoft Windows (starting with '95).

Now would be a good time to hop over to the article and see what it's about (and while you're there, make sure you look at some of the other articles - pretty neat site). Here's the link:

When it's time to delete ourselves, we first spawn an external process that is guaranteed to exist on all Windows computers (explorer.exe, for example) in the suspended state. We do this by calling CreateProcess, passing CREATE_SUSPENDED for the dwCreationFlags parameter. Note that when a process is launched this way, there's really no telling at what point the primary thread of the process will get suspended. But, it does appear to get suspended long before the entry point gets invoked and, in fact, it occurs even before the Win32 environment for the process has been fully initialized.

After this, we get the CONTEXT data (basically, the CPU register state) for the suspended primary thread (from the remote process) via GetThreadContext.

We then manipulate the stack pointer (ESP) to allocate some space on the remote stack for storing some of our data (like the path to the executable to be deleted). After this, we plonk the binary code for a local routine that we've written for deleting files over to the remote process (along with the data it needs), by calling WriteProcessMemory.

Next, we mess around with the instruction pointer (EIP) so that it points to the binary code we've copied to the remote process, and update the suspended thread's context (via SetThreadContext).

And finally, we resume execution of the remote process (via ResumeThread). Since the EIP in the remote thread is now pointing to our code, it executes it; which of course, happily deletes the original executable. And that's it!

While this approach does get the job done, the fact that our deletion code executes in the remote process even before Windows has had a chance to initialize it fully, places some restrictions on the kind of APIs that we can invoke. It so turns out that APIs like DeleteFile and ExitProcess do work while the process is in this half-baked state. I figured, I'll modify the approach somewhat so that it allows us to call any API we want from our injected code. Here's what I did:

As before, we launch the external process in a suspended state. However, instead of plonking our code at the location that ESP happens to be pointing at when it got suspended, we put it over the executable's entry-point routine, i.e., we replace the remote process' entry point with our own injected code. And when the entry-point code executes, we can be pretty sure that the Win32 environment is fully initialized and primed for use!

Figuring out where the entry point of a module lives requires us to parse PE file format structures. In your own program, for example, the following code would give you a pointer to the entry point routine in the process' executable image:

The entry point that you define, by the way, - main or WinMain - isn't the actual entry point routine. The compiler inserts its own entry point, which in turn calls our function. This entry point, typically, does stuff like CRT initialization and cleanup. In an ANSI console app, for instance, the actual entry point routine is something called mainCRTStartup.

It appears logical, therefore, that we should be able to find the entry point routine in remote processes also in a similar fashion, using ReadProcessMemory. While that is so, finding the equivalent of the module variable in the code given above for remote processes turns out to be trickier than anticipated. The problem is that there is no convenient GetModuleHandle routine that'll work for remote processes.

As it turns out, GetModuleHandle returns a virtual address that is valid only within the relevant process' address space. ReadProcessMemory, however, requires real addresses to work with. So the question is, how do we get to know the remote process' base address in memory? The solution, as it turned out, requires us to dig deep into the OS's internals! The credit for this solution goes to Ashkbiz Danehkar whose article called Injective Code Inside Import Table outlines a method for finding this.

In brief, the operating system maintains a user-mode data structure for every thread in the system, called the Thread Environment Block (TEB), which describes pretty much everything you'd want to know about the thread, including a pointer to another data structure called the Process Environment Block (PEB) which, as may be apparent, describes processes, including, happily for us, a pointer to the image's base address in memory! These structures are not, however, documented (by Microsoft that is ;). But some very, very clever folks here have managed to figure out the layout for these structures all by themselves!

So, all we need to do is:

Figure out where the TEB for the primary thread lives in the remote process; this information is stored in the thread's FS register, which is accessible via the GetThreadSelectorEntry API.

Read the PEB using the pointer to it in the thread's TEB via ReadProcessMemory.

Use the pointer to the image's base address in the PEB, and parse the PE structures till we are left with a reference to the remote process' entry point routine.

If you're wondering what the weird code initializing dwFSBase means, all I can do is direct you to the documentation for the LDT_ENTRY data structure in MSDN. Structures of this kind are partly the reason why system programmers tend to go bald early in life!

Now that we know where the entry point lives in the remote process, it should be really straightforward, right? Wrong! There still is that itsy bitsy problem of figuring out how we are to pass data to the remote process!

As you might have noticed, the function remote_thread makes all system calls via function pointers, instead of calling them directly. This is done because, in the normal course, the compiler generates tiny stubs whenever calls to routines in dynamically loaded DLLs are made from a program. This stub jumps to a function pointer stored in a table initialized by the operating system's loader at runtime. Since we don't want these fancy stubs generated for code that is meant to be injected into a remote process, we deal exclusively with function pointers.

Fortunately for us, the system APIs (in kernel32, user32 etc.) always get loaded at the same virtual address in all processes. So, all we need to do is initialize a data structure with pointers to all the system calls we want to make from the remote process, and pass this structure along also. With our entry-point overwrite strategy, of course, how are we to do this? To make a long story short, I settled for the following approach.

I then converted this into shellcode. If you've never heard of the term shellcode before, then here's a quick primer on what it is. If you know what it is already, then feel free to skip the next section.

shellcode is the technical term (in security circles) for binary machine code that is typically used in exploits as the payload. Here's a quick and dirty way of generating the shellcode from the obj file generated when you compile your source files. In our case, we are interested in whipping the shellcode up for the remote_thread routine. Here's what you've got to do:

The line _remote_thread: marks the beginning of the assembly dump for the remote_thread routine, and the line containing the ret statement indicates the end of the routine. Open s.asm in VS.NET 2002/2003/2005, and delete everything except the stuff that falls between _remote_thread: and ret. Delete the line containing _remote_thread: as well, so you end up with something that looks like this:

What you're left with is a file that can be divided into three logical columns.

All the stuff till and including the ':' is the byte-offset for that instruction. So when you see the number '00000003', it indicates that that instruction is 3 bytes away from the beginning of the routine.

This is followed by the machine code for that instruction with one or more trailing white space characters.

All the stuff after the machine code is the assembly instruction.

We are interested in the second column, which we extract out using some nifty regular expressions in Visual Studio's Find/Replace dialog. Open the Find/Replace dialog in Visual Studio, and run the following expressions in the given order:

Sl. No.

Find

Replace

Description

1

<nobr>[0-9A-F]+\::b+{[0-9A-F:b]+}.*</nobr>

\1

Strips the first and the third column from the file.

2

^:b+

nothing

Removes leading white space. Ensure that there is absolutely nothing in the "Replace with" textbox.

3

:b+$

nothing

Removes trailing white space. Again, ensure that there is absolutely nothing in the "Replace with" textbox.

4

\n

space

Removes all new line characters from the file so there's just one line in the file. Enter a single space character in the "Replace with" textbox.

5

space

<nobr>', '\\x</nobr>

Replaces all space characters with the literal ', '\x.

Now, type this at the beginning of the line:

char shellcode[] = { '\x

And finally, type this at the end of the line:

' };

After all of this, you should end up with something that looks like this:

As it turns out in our case, the value 0xFFFFFFFF that we initialized the pointer remote with in remote_thread shows up the exact same way in the shellcode also. Since we know where the entry point lives in the remote process, all we need to do is to first replace 0xFFFFFFFF in the shellcode with the actual pointer to the data before over-writing the entry point. Here's how this looks:

You'll probably never have to write a program that deletes itself, in your career; but there are a few nifty, if somewhat esoteric, tricks in there, eh?! One commercial program that does this sort of thing is the helper program that you download when you use the Copilot service. However, I suspect that program uses a different technique that is probably a lot more straightforward and a gazillion times less interesting ;). Briefly, an implementation note in Copilot's functional specification document states that self-deletion is a simple matter of:

embedding a small EXE as a resource into your main program

extracting this EXE to a temporary location, passing FILE_FLAG_DELETE_ON_CLOSE to CreateFile

and having the small EXE wait till the main EXE exits, before deleting it

The FILE_FLAG_DELETE_ON_CLOSE flag should cause the OS to delete the small EXE when all open handles to it are closed. That's really neat, but hey, where's the fun in that?!