zoukankan      html  css  js  c++  java
  • 【转贴】Sourcecode and Code Snippets

    原帖地址:http://www.catch22.net/source/


    This is a subject that tends to come up every so often in the newsgroups, so I thought I'd write an article about the techniques I've collected to enable an executable to delete itself from disk (whilst running, that is). There is very little information on the web, and what information exists is also hard to find.

    Why would you want a program to delete itself? The only good reason I know of is an un-install program that needs to remove an application, as well as itself in order to completely remove the application from disk. I'm sure there are other good reasons for self-deleting executables, but un-installation is probably the most common.

    I know many different methods, each of which I will describe shortly. I must take this opportunity to mention that only one of these techniques has been developed by myself. Apart from the last method, I am presenting the same material described by Jeffrey Richter in his January 1996 MSJ column, titled "Win32 Q&A". Click here to read the original article. The rest of the techniques were originally developed by Gary Nebbett - or heavily influenced by his work. So, I hope no-one thinks I am ripping off other people's ideas because all these techniques have existed for years before I encountered them.

    There used to be two methods for a program to delete itself - actually doing it from the same program, and forcing a separate program to do it on your behalf. When Windows XP came out the "self-deleting" executable became a part of history - it is no longer possible for a Windows program (which runs on any version of Windows) to delete itself. However there are lots of techniques which can achieve the same effect - the ones I know about are listed below.

    Why the obvious doesn't work

    If you try to run the following code, nothing will happen.

    TCHAR szFilePath[_MAX_PATH];
    // Get current executable path
    GetModuleFileName(NULL, szFilePath, _MAX_PATH);
    // Delete specified file
    DeleteFile(szFilePath);

    The code above retrieves the full path to the current executable, and then tries to delete the file whilst running. This code will fail - because all 32bit versions of Windows (95, 98, ME, NT, 2000, XP etc) use a mechanism called memory-mapped files to load an executable image into memory.

    When the Windows loader runs an executable, it opens the executable's disk file and maps that region of disk into memory, effectively loading the executable into memory. This disk file is kept open during the lifetime of the process, and is only closed when the process terminates. Because of this lock on the file, it is normally impossible to delete an executable file whilst it is running. Just run notepad.exe, then try to delete the notepad executable - it won't work.

    The MoveFileEx method

    I'm going to mention this technique even though it doesn't really solve our problem, because it is quite useful to know and can be handy in other situations.

    MoveFileEx(szExistingFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); 

    The MoveFileEx API moves a file to a new location. When you pass NULL as the second parameter, this causes the file to be moved "nowhere", effectively deleting the file. Now, ordinarily this would fail if you tried this with the path to the current executable. However, if we specify MOVEFILE_DELAY_UNTIL_REBOOT in the dwFlags parameter, this tells Windows not to move (or delete) the file until the system is shutdown or rebooted.

    There are a few problems with this technique. Firstly, you cannot remove the directory that the executable resides in. Second, the file is not deleted immediately - if your system doesn't get rebooted very often, then the file will stay around. But the biggest problem is that MoveFileEx is not implemented on Windows 95/98/ME. Today this is not really a problem and the MoveFileEx is the neatest, safest way for a program to delete itself.

    The WININIT.INI method

    Under Windows 95/98/ME, an application called WININIT.EXE runs each time the system is started. This application looks for a file called WININIT.INI. If this file exists, WININIT.EXE looks for a section called [Rename]. Each entry in the [Rename] section specifies a file rename operation which will occur (once) when time the system starts. This method is obviously very similar to the MoveFileEx method described above.

    [Rename]
    NUL=c:\dir\myapp.exe
    c:\dir\new.exe=c:\dir\old.exe

    The filename to the left of the equal sign specifies the new name of the filename on the right. When NUL is used as the new filename, the file is deleted. This means that an application can write an entry into WININIT.INI, specifying NUL and the applications own full path.

    You must be careful when writing an entry to the [Rename] section. You cannot use WritePrivateProfileString API call, because this function prevents any duplicate entries from occuring under the same section. This restriction would prevent there from being more than one "NUL=" entry. Therefore you must manually write any entry if you want to use this technique.

    The Self-Deleting Batch File method

    This is quite a well known method, and was documented in MSDN some time ago. This technique works on both Windows 95 and Windows NT. It works because MS-DOS batch files are able to delete themselves. To test this technique, create a small batch file containing the single command:

    del %0.bat

    The batch file, when run, deletes itself and issues an error "The batch file cannot be found". This error is just a simple message, so it can be safely ignored. By itself this isn't too useful, but when modified to delete our executable it solves our problem, albeit in a rather forceful manner. Our executable will create a batch file (called C:\DelUs.bat) with the following content:

    :Repeat
    del "C:\MYDIR\MYPROG.EXE"
    if exist "MYPROG.EXE" goto Repeat
    rmdir "C:\MYDIR"
    del "\DelUS.bat"

    This batch file repeatedly attempts to delete the specified file, and will run continuously consuming CPU until it succeeds. When the execuable has been deleted, the batch file then deletes itself.

    The executable needs to spawn off the batch file using CreateProcess, and then should exit immediately. It would be a good idea to give the batch file's thread of execution a low priority so that it doesn't get much execution time until the original executable has terminated.

    The sourcecode download at the top of this article contains the full code to this technique.

    The COMSPEC method

    This is a method kindly shared by Tony Varnas, who recently emailed me this snippet:

    BOOL SelfDelete()
    {
    TCHAR szFile[MAX_PATH], szCmd[MAX_PATH];
    if((GetModuleFileName(0,szFile,MAX_PATH)!=0) &&
    (GetShortPathName(szFile,szFile,MAX_PATH)!=0))
    {
    lstrcpy(szCmd,"/c del ");
    lstrcat(szCmd,szFile);
    lstrcat(szCmd," >> NUL");
    if((GetEnvironmentVariable("ComSpec",szFile,MAX_PATH)!=0) &&
    ((INT)ShellExecute(0,0,szFile,szCmd,0,SW_HIDE)>32))
    return TRUE;
    }
    return FALSE;
    }
    

    This method is very similar to the batch-file method (above) but is alot neater in its implementation. It works under all 32bit versions of Windows (95,98,ME,NT,2000,XP), as long as the COMSPEC environment variable is defined. This is always defined (by default) to be the full path to the operating system's command interpreter. For Windows 95, this is "command.exe". For Windows NT, this is "cmd.exe".

    The function will only work if the executable has exited, so it is important to call this function and then exit immediately. It works by spawning a copy of the system's command interpreter, and asking it to perform the following command:

    del exepath >> NUL

    This deletes the current executable, and pipes the output to NUL (no output). The shell process is created with a hidden window as well, so the whole process is invisible.

    The DELETE_ON_CLOSE method

    The CreateFile API call accepts several flags which affect how a file is created or opened. One of these flags, FILE_FLAG_DELETE_ON_CLOSE, specifies that the file will be deleted when the last handle to it is closed. The basis to this technique will be to run an executable with this flag set, so that when it exits, it is deleted automatically.

    The first step is to create an empty file with the DELETE_ON_CLOSE flag specified. The exact binary content of the current executable file is then copied into this new file, in effect duplicating the executable on disk. A new process is then created (using the new executable file). This has the effect that the duplicate file's handle count is incremented. Also, when the new process was created, the full path of the current process was passed through the command-line argument.

    Next, the current executable (which wants to delete itself) closes the file handle used to create the new process, and then exits. Now, the duplicate's file-handle count is decremented, but because CreateProcess incremented its handle count when it started, the file is not deleted.

    At this point, the duplicate executable has started running. The PID specified on the command-line is used to open a handle to the original process. The duplicate waits for the original process to terminate, using WaitForSingleObject. When this call returns, the duplicate can call DeleteFile on the filename also specified through its command-line argument. The original executable (the one that wanted to delete itself) has been successfully deleted. This just leaves the duplicate copy, which exits normally. The duplicate's file-handle count drops to zero, the DELETE_ON_CLOSE flag comes into effect, and the duplicate file is deleted also.

    It sounds a bit complicated, but it's not too difficult. Here's the steps one more time:

    [ Current process ]
    1. Create a new file with FILE_FLAG_DELETE_ON_CLOSE.
    2. Copy the current executable's content into the new file.
    3. Create a new process with the duplicate executable:
    4. Pass the current executable's full path and PID in the call to CreateFile.
    5. Sleep for a short time to give the new process time to start.
    6. Close the new file.
    7. Exit current process.
    [ Duplicate process ]
    8.  Wait for the process specified on command-line to die.
    9.  Delete file specified on command-line.
    10. Exit duplicate process.  

    There are just a couple of technicalities to mention. First, when the "new" process is spawned, the "old" process must sleep for a short period, enough to let the Windows loader open the file and create the process (thus incrementing it's file count).

    Second, the new process must wait until the old process terminates, which releases its file count.

    Third, when the duplicate executable is created, it must also have the FILE_SHARE_DELETE flag specified, otherwise CreateProcess will fail, because it won't be able to open the file whilst we have it open with the DELETE_ON_CLOSE flag set.

    Obviously this method will require careful coding, because the program must be written in such a way so that it can perform these dual tasks. The "new" executable must know that it's job is to delete the file specified on the command line, for instance.

    It's a little messy, but it does work very well. In fact, the uninstall program that I wrote, which is included with the software you can download from this site, uses this very method. I've included an example program which demonstrates this technique.

    An alternative method is to write a very small stand-alone executable, which it's sole task is to delete the file-name specified on it's command-line. This executable could then be imbedded as a "payload" to the executable which wants to delete itself. This payload would be created and executed in the same way as described above.

    The Ultimate Self-Deleting Executable!

    This inline assembly snippet is short and simple. I can't claim credit for this code - I found it posted on usenet some time ago. The author's name is Gary Nebbett, author of "Windows NT Native API Reference". Actually this technique was first published in Windows Developer Journal around 1996.

    #include <windows.h>
    int main(int argc, char *argv[])
    {
    char    buf[MAX_PATH];
    HMODULE module;
    module = GetModuleHandle(0);
    GetModuleFileName(module, buf, MAX_PATH);
    CloseHandle((HANDLE)4);
    __asm
    {
    lea     eax, buf
    push    0
    push    0
    push    eax
    push    ExitProcess
    push    module
    push    DeleteFile
    push    UnmapViewOfFile
    ret
    }
    return 0;
    }
    

    This snippet ONLY works under Windows NT and 2000. As soon as you compile and run the above program, it just disappears from disk! It works because under NT and 2000 the OS keeps a usermode HANDLE reference to the memory-mapped file which backs the executable image on disk. This HANDLE always has a value of "4" under these OSs.

    Unfortunately this gem only works under NT/2000, not XP or .NET Server. This is because these OSs no longer maintain a usermode HANDLE to the executable's memory-mapped file - this section object is only accessible in kernel using a pointer reference. So although this technique is incredibly slick (it is the only true self-deleting method) it is no longer as-is.

    Now for Windows 9x!

    Thanks must go to Tony Varnas again for some great detective work. He managed to unearth the following assembler snippet which works exactly like the snippet above, but this time for Windows 95,98,ME (tested on all three).

    #include <windows.h>
    int main(int argc, char *argv[])
    {
    char    buf[MAX_PATH];
    HMODULE module;
    module = GetModuleHandle(0);
    GetModuleFileName(module, buf, MAX_PATH);
    __asm
    {
    lea     eax, buf
    push    0
    push    0
    push    eax
    push    ExitProcess
    push    module
    push    DeleteFile
    push    FreeLibrary
    ret
    }
    return 0;
    }
    

    Solution for XP+

    After the original self-deleting technique Gary Nebbett posted a solution on Jan 19 2002 which works for XP. The following code should be packaged as a DLL which is callable from RunDLL32. The idea is that an external program (RunDLL32) is used which first waits for the original executable to finish. The DLL which is loaded into RunDLL then performs the original self-deleting trick. This is possible because the kernel maintains references to DLLs differently to the main executable.

    #include <windows.h>
    #pragma comment(linker, "-export:CleanupA=_CleanupA@16?)
    extern "C" void CALLBACK CleanupA(HWND, HINSTANCE, PSTR, int)
    {
    static MEMORY_BASIC_INFORMATION mbi;
    VirtualQuery(&mbi, &mbi, sizeof mbi);
    PVOID module = mbi.AllocationBase;
    CHAR buf[MAX_PATH];
    GetModuleFileName(HMODULE(module), buf, sizeof buf);
    __asm
    {
    lea     eax, buf
    push    0
    push    0
    push    eax
    push    ExitProcess
    push    module
    push    DeleteFile
    push    FreeLibrary
    ret
    }
    }
    

    The idea that an external program could be used to self-delete it's parent is what influenced me to develop the following technique which works for all versions of Windows, but does not require a separate DLL which is dropped to disk.

    The Definitive Self Deleting Executablenew

    I am pleased to present what I believe is the definitive self-deleting executable, for all versions of Windows.

    Now this technique is complicated so I won't include the code here - please get it from the sourcecode zipfile (selfdel05.c). Instead I will describe how it works and the issues involved.

    • Firstly a child process is created in a suspended state (any process will do - i.e. explorer.exe).
    • Some code is then injected into the address-space of the child process.
    • The injected code waits for the parent-process to exit.
    • The parent-process is then deleted.
    • The injected code then calls ExitProcess, which terminates the child process.

    The neat thing about this technique is that the spawned child process never really executes (no windows ever appear) - only the "self-deleting" code in the child gets to execute. After the parent process terminates and is deleted, the child process terminates using ExitProcess, preventing anything further from happening. Effectively an arbitrary child-process is hijacked and coerced into deleting it's parent.

    My first version of this technique (inside selfdelxp.c) used CreateRemoteThread to inject the code into the suspended child process. This was quite neat because the child never ran - only the injected thread with the self-deleting code ever executed. The downside to this technique is that it only ran on Windows-NT based platforms.

    The latest version (selfdel05.c) does not use CreateRemoteThread to inject code into the child. It therefore works on all versions of Windows. The self-deleting code is injected into the spawned child by hijacking the primary thread of the process. The thread's stack is manipulated in such a way so that space is "allocated" on the stack - into which the self-deleting code is written. The instruction-pointer for the thread is then altered so that it points to the injected code on the thread's stack. When the child process (and primary thread) is resumed the code executes right off the stack, deleting the parent process, and then exits.

    There are issues involved with this last technique. The first is the injection of code into an already running thread - this is not a desirable thing to do, but because the thread's "real" execution is never resumed (the process exits as soon as the parent is deleted) it seems like a viable way to do this.

    The second issue involves the Win32 environment. When a child process is created in a suspended state the program has not started executing yet - there is still alot of Win32 environment setup that must be performed before the entry-point to the executable is called. So when we hijack this thread to do our bidding, we are doing so in an environment that is not yet fully initialized, and is not ready for Win32 API calls (it is still mid-way through executing some internel part of the OS!). The API calls that are made (WaitForSingleObject, DeleteFile etc) do however work - but this is not guaranteed in furture OS releases.

    The exact same issue exists when you CreateRemoteThread into a process that was started as suspended - the OS hasn't yet finished intializing your process's environment before the remote thread starts to execute. This is why alot of Win32 API calls will fail (such as RemoveDirectory).

    Conclusion

    I've used this article to describe all the methods I know of to delete an executable whilst it is running. You can take your pick of the techniques, but the last method presented is currently the only one which works well for all versions of Windows.

  • 相关阅读:
    【xamarin + MvvmCross 从零开始】一、环境安装
    .NET微服务从0到1:服务容错(Polly)
    .NET微服务从0到1:服务注册与发现(Consul)
    .NET微服务从0到1:API网关(Ocelot)
    .NET Core之单元测试(四):Fluent Assertions的使用
    .NET Core之单元测试(三):Mock框架Moq的使用
    .NET Core之单元测试(二):使用内存数据库处理单元测试中的数据库依赖
    .NET Core之单元测试(一):入门
    win+navicat
    JDBC介绍和Mybatis运行原理及事务处理
  • 原文地址:https://www.cnblogs.com/rock_chen/p/792357.html
Copyright © 2011-2022 走看看