Wie kann sich ein Programm selbst löschen?



  • Das Thema "wie kann sich ein Programm selbst von Platte löschen" ist immer wieder ein heißes Thema, nach dem oft und gerne gefragt wird. Wer ein einfaches delete der laufenden Exe-Datei versucht, wird rasch feststellen daß dies nicht klappt - die Datei ist während der Programmausführung gesperrt.

    Es gibt zahlreiche Wege und Artikel dazu, die allerdings im Web eher verstreut zu finden sind.

    Beginnen wir also zunächst mit einem der "Freaks" Jeffrey Richter - er weiß wie es geht.

    Quelle: http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0198/win320198.htm& nav=/msj/0198/newnav.htm

    Da diese Seiten ständig umziehen erlaube ich mir die Freiheit hier den Text zu kopieren.

    Hinweis: die im Text angegebenen Quellcodes "Figure #n" folgen auf den nächsten Seiten.

    January 1996

    Q&A Win32

    Jeffrey Richter wrote Advanced Windows (Microsoft Press, 1995) and Windows 3.1: A Developer's Guide (M&T Books, 1992). Jeff is a consultant and teaches Win32-based programming seminars. He can be reached at v-jeffrr@microsoft.com.

    Q: We're very close to shipping our product. In order for us to get the "Designed for Windows® 95" logo, we must supply an application that uninstalls our software from the user's machine. We have developed an application called UNSETUP.EXE. But how can we make UNSETUP.EXE delete itself? Right now, we can remove all of the application's files and subdirectories but we cannot delete the program that deletes these files and the subdirectory containing it. How can we write a program that can delete itself?

    Anthony Spica
    Cherry Hill, NJ

    AAs: I'm sure you are aware, the following code does not work correctly:

    TCHAR szEXEPathname[_MAX_PATH];
    GetModuleFileName(NULL, szEXEPathname, _MAX_PATH);
    DeleteFile(szEXEPathname);
    

    This code gets the full pathname of the currently running executable and then attempts to delete it by calling DeleteFile. Because Windows 95 and Windows NTª load EXE files into memory as memory-mapped files, the system opens the EXE file when the process is started and automatically closes the file when the process terminates. When DeleteFile is called, the system sees that the EXE file is still opened, DeleteFile returns FALSE, and a subsequent call to GetLastError returns 5, which identifies an access violation error (ERROR_ACCESS_DENIED).

    That straightforward technique won't work. The first thing that popped into my mind to solve this problem was the little-known MoveFileEx function:

    BOOL MoveFileEx(LPCTSTR lpExistingFileName, 
                    LPCTSTR lpNewFileName, DWORD dwFlags);
    

    MoveFileEx allows you to move a file from one directory to another. Passing NULL for the lpNewFileName parameter tells the system to move the file to nowhere. This is the same thing as telling the system to delete the file and is basically the same thing as calling DeleteFile. So why would this be a better solution? MoveFileEx's third parameter, dwFlags, allows you to specify flags that alter the behavior slightly. The MOVEFILE_DELAY_UNTIL_REBOOT flag tells the system that the file is not to be moved (deleted) until the system is rebooted. When the system is rebooted, the UNSETUP.EXE file will not be in use, the system will delete the file, and everything's swell.

    Unfortunately, there are three problems with the MoveFileEx technique. It doesn't remove the subdirectory that contains the file. Also, the file is not deleted immediately. I know several people who've had Windows NT running continuously over a full year without ever rebooting! If these people install and uninstall several applications, their hard drives would soon start to be populated with a whole bunch of UNSETUP.EXE files. The third and final problem with MoveFileEx is the biggest: it's not implemented on Windows 95.

    Windows 95 does offer a technique that lets you move/delete a file when the user reboots. When Windows 95 boots, it runs an application called WININIT.EXE. This application looks for a file called WININIT.INI. If this file exists, WININIT.EXE then looks for a section within the file labeled Rename.

    [Rename]
    NUL=C:\Program Files\Win95ADG\UNSETUP.EXENUL=C:\Program Files\SomeApp\UNSETUP.EXEC:\Calc.exe=C:\Windows\Calc.exe
    

    Every line in the Rename section indicates a file that needs to be moved or deleted. The name to the left of the equal sign indicates the new pathname of the file and the name on the right indicates the current pathname of the file. If the name on the left is NUL, WININIT.EXE deletes the file. Because WININIT.INI's contents look very much like any old-style INI file, you might be tempted to use the WritePrivateProfileString function to add entries to the Rename section. Don't-the WritePrivateProfileString function ensures that no more than one entry in a section can have NUL to the left of the equal sign. If every UNSETUP.EXE program used WritePrivateProfileString to add its entry to the WININIT.INI file, the following scenario could occur. The user runs your UNSETUP.EXE; then, before rebooting, the user uninstalls another application. But because WritePrivateProfileString doesn't allow more than one entry to begin with NUL=, your entry is deleted from the file and your application's UNSETUP.EXE file will never be deleted.

    The ReplaceFileOnReboot function shown in Figure 1 hides from you the implementation differences of moving/deleting a file on reboot between Windows 95 and Windows NT. The function first tries to call MoveFileEx. If this function fails, it then opens (or creates) a WININIT.INI file and properly adds entries to the Rename section. Of course, in a future version of Windows 95, Microsoft will fully support the MoveFileEx function. I have written the ReplaceFileOnReboot function so that it always calls MoveFileEx and will continue to work correctly on future versions of Windows 95.

    I decided to try something else since I didn't like the fact that the ReplaceFileOnReboot function still forces the user to reboot in order to delete the UNSETUP.EXE file. Here is the code I used for my next attempt:

    int WINAPI WinMain (HINSTANCE hinstExe, 
                        HINSTANCE hinstExePrev,
                        LPSTR lpszCmdLine, int nCmdShow) {
        TCHAR szEXEPathname[_MAX_PATH];
       HANDLE hfile;
    
       GetModuleFileName(NULL, szEXEPathname, _MAX_PATH);
       hfile = CreateFile(szEXEPathname, GENERIC_READ,
                          FILE_SHARE_READ, NULL,
                          OPEN_EXISTING,
                          FILE_FLAG_DELETE_ON_CLOSE, NULL);
       CloseHandle(hfile);
       return(0);
    }
    

    My idea was that since the EXE file is already open, why not go and open the file a second time by calling CreateFile using the FILE_FLAG_DELETE_ON_CLOSE flag? This flag tells the system that the file should be deleted when it is no longer in use. After CreateFile returns, I immediately call CloseHandle assuming that the system remembers that I want the file deleted when it's no longer in use. So, when the process terminates, I expect the system to automatically delete the file.

    After writing the small test application and experimenting with it, I soon discovered that this technique doesn't work at all. When I debugged the program under Windows 95, I saw that the call to CreateFile was succeeding but the system was just ignoring my request to open the file with delete-on-close access. When I closed the file and terminated the process, the executable file remained on my hard disk.

    I then went and tested my application under Windows NT. On Windows NT, the call to CreateFile fails returning INVALID_HANDLE_VALUE and GetLastError returns ERROR_ACCESS_DENIED. This is because Windows NT supports the ability to share a file with delete access. When I attempt to open a file specifying the FILE_FLAG_DELETE_ON_CLOSE flag, the system checks to see if the file has already been opened with the FILE_SHARE_DELETE flag. FILE_SHARE_DELETE is not documented in the Win32 SDK documentation, but it can be found in the Windows NT DDK's NTDDK.H file:

    #define FILE_SHARE_DELETE               0x00000004
    

    Taking the failure of my previous technique in stride, I started working on my next idea. I needed to somehow execute code in my process's address space without having my EXE file loaded anymore. That made me think of DLLs. DLLs can be dynamically loaded and unloaded as desired simply by calling LoadLibrary and FreeLibrary. All I'd need to do is dynamically unload my EXE file from my address space just as I would unload a DLL. You may recall that the FreeLibrary function takes a single parameter: the HINSTANCE (or HMODULE) of a loaded DLL.

    BOOL FreeLibrary(HINSTANCE hinstDll);
    

    In Win32®-based programs, the HINSTANCE of a DLL is the virtual memory address where the DLL was loaded into a process's address space. This is true of EXE files as well: the hinstExe parameter passed to WinMain identifies the virtual address where the EXE file got loaded. So it stands to reason that if I call FreeLibrary and pass in the EXE's hinstExe value, the system should unload the EXE file from my process's address space, right?

    My code to test this theory looks like this:

    int WINAPI WinMain (HINSTANCE hinstExe, 
                        HINSTANCE hinstExePrev,
                        LPSTR lpszCmdLine, int nCmdShow) {
    
       TCHAR szEXEPathname[_MAX_PATH];
       GetModuleFileName(NULL, szEXEPathname, _MAX_PATH);
       FreeLibrary(hinstExe);
       DeleteFile(szEXEPathname);
       return(0);
    }
    

    The code seems simple enough but if you build this sample code and run it on Windows 95, you'll consistently get access violations and, of course, the UNSETUP.EXE file won't be deleted from the user's hard drive. This was a fun sample to code and execute inside a debugger. When you debug this application, you'll want to keep the EXE file visible in the debugger's memory window. Then, watch what happens when you execute the FreeLibrary call. FreeLibrary actually does unload the EXE file from the process's address space. You can easily see this because the contents of the debugger's memory window changes to all questions marks. But then FreeLibrary returns and the code that contains the call to DeleteFile is gone and this is what causes the access violation.

    This is a relatively easy problem to solve, especially if you remember my article "Load Your 32-bit DLL into Another Process's Address Space Using INJLIB," MSJ May 1994. (My Advanced Windows book, Microsoft Press, 1995 has the latest version of this code and some bug fixes I've made since the article appeared.) That article described a technique that allows you to dynamically inject code into another process's address space and execute this injected code. This injected code in the remote process does not come from a DLL or an EXE but instead comes from copying bytes from the local process's address space into the remote process's address space. Your problem requires a stripped-down version of the inject library code (see Figure 2).

    For a full understanding of the code in Figure 2, please refer to the inject library article. Basically, the DeleteExecutable function first calls HeapAlloc to allocate a block of memory in your process's address space. Then a function is copied from your EXE file into the dynamically allocated block of memory. The rules that this function must follow are spelled out in the inject library article. Next, a DELEXEINFO data structure is initialized with the full pathname of the process's EXE file, a flag indicating whether the containing subdirectory should also be deleted, and the address of the functions (in KERNEL32.DLL) that are going to be called from the code in the dynamically allocated memory block.

    After the data structure is initialized, the code in the memory block is called and is passed the address of the DELEXEINFO structure (which is on the thread's stack). The code in the memory block uses the information in the DELEXEINFO structure to call FreeLibrary to unload the EXE file, then DeleteFile to delete the EXE file (which will succeed now), and then call ExitProcess so that the process terminates. It would be a bad mistake to allow the code in the memory block to return because the calling code no longer exists after FreeLibrary unloads the EXE file.

    To use my function, all you have to do is include the DELEXE.H header file and add the DELEXE.C source file to your project. Then in your code, just place a call to my DeleteExecutable function:

    void WINAPI DeleteExecutable(DWORD dwExitCode, 
                                 BOOL fRemoveDir);
    

    The first parameter is your process's exit code, and the second parameter tells the function whether it should also attempt to remove the subdirectory that contains the UNSETUP.EXE file. The function is prototyped as returning void because it will never return.

    This is my favorite solution because it deletes the EXE file immediately. But now I have some bad news: this method doesn't work on Windows NT. The reason is that Windows NT doesn't allow FreeLibrary to unload the process's EXE file. At first, I thought that Windows NT just sets a really big usage count on the EXE file module. I figured I could call FreeLibrary passing the hinstExe value continuously in a loop. Each call to FreeLibrary would decrement the EXE file's usage count until the EXE file is unloaded. I thought I could detect this because FreeLibrary will return FALSE if I pass an address that does not represent a module. The pseudocode would be something like this:

    while (FreeLibrary(hinstExe))
        ;
     DeleteFile(szEXEPathname);
    

    I coded this up and tested it on Windows NT. After several hours, the while loop still had not terminated. Either Windows NT just doesn't allow FreeLibrary to affect an EXE or this loop will take too long to execute and the performance of the UNSETUP.EXE would be too horrendous anyway.

    After all of this, I decided on a brute-force method that consistently works on both Windows 95 and Windows NT: batch files. Batch files have the unique ability to delete themselves. If you create a batch file containing this single command:

    del %0.bat
    

    and run it at the command prompt, the batch file will delete itself, followed by the error message:

    The batch file cannot be found.
    

    This error message is harmless. The DeleteExecutableBF function I wrote (see Figure 3) creates a batch file that kills the running executable program. If the executable is called UNSETUP.EXE and resides in the C:\Win95ADG subdirectory, the contents of the batch file look like this:

    :Repeat
    del "C:\Win95ADG\UNSETUP.EXE"
    if exist "UNSETUP.EXE" goto Repeat
    rmdir "C:\Win95ADG"
    del "\DelUS.bat"
    

    This batch file attempts to delete the UNSETUP.EXE file. If the file is not deleted because it is still executing, the batch file attempts to delete the file again. When the executable file no longer exists, the batch file removes the subdirectory that contained the file and then deletes itself.

    You'll notice that the DeleteExecutableBF function does a lot of work to run the batch file. The system always executes batch files inside a console window. There is no need for the user to know that you are running a batch file, so I set the STARTUPINFO's wShowWindow member to SW_HIDE.

    Another thing to be aware of is that the batch file polls for the existence of the executable file. In a preemptive, multithreaded environment, polling is an awful thing to do because you are causing the system to waste precious CPU cycles. But in batch files, there is no way to call WaitForSingleObject or WaitForMultipleObjects, so you must use a polling technique.

    However, you can adjust the priority class and relative thread priorities of the processes and threads involved with my solution. When the DeleteExecutableBF function calls CreateProcess to spawn the batch file, I specify both the CREATE_SUSPENDED and IDLE_PRIORITY_CLASS flags. The CREATE_SUSPENDED flag tells the system to create the console window (which will be invisible) to run the batch file, but the system is not allowed to schedule it any CPU time yet. The IDLE_PRIORITY_CLASS flag tells the system that this process should not be scheduled CPU time frequently. This way fewer CPU cycles will be wasted by the batch file while it polls for the executable file to terminate.

    After CreateProcess returns, I explicitly set its primary thread's relative thread priority to THREAD_PRIORITY_IDLE. This further reduces the amount of CPU time that will be wasted by the batch file. Then I set the executable thread's relative priority to time critical and the executable's priority class to high. This causes the system to schedule CPU time to the executable's thread very frequently so that it can terminate as soon as possible. I do not set the priority class to real time because this would interfere with threads that are responsible for processing various hardware events.

    After all of the thread priorities have been adjusted, I close the handle to the batch file's process and then resume the batch file's thread. This allows the system to schedule CPU time to execute the batch file, but not a lot of time to the batch file because its priority is so low. Finally, I close the handle to the batch file's thread and return from the DeleteExecutableBF function. At this point, you want the executable process to terminate as soon as possible so that the batch file will terminate, stop polling, and stop wasting CPU cycles.

    The TDELEXE.C file (see Figure 4) is a small application that simply tests all of my techniques. When you invoke it, it asks the user with a message box (see Figure 5) how the user would like to delete the executable file. Of course, you can only run the program once since the EXE file is destroyed when the process terminates. If you can't easily rebuild the code, I suggest you make a backup of the original EXE file before running it each time.

    Internet:

    Jeffrey Richter
    v-jeffrr@microsoft.com

    Eric Maffei
    ericm@microsoft.com

    From the January 1996 issue of Microsoft Systems Journal.



  • Damit nicht genug, konnte man in den Newsgroups auch folgenden kleinen aber netten Tipp finden.

    Gary Nebbett schrieb damals in der Newsgroup microsoft.public.win32.programmer.kernel:

    Subject: Delete oneself from disk while running in RAM
    Date: 12/20/2000
    Author: Gary Nebbett gary.nebbett@syngenta.com

    The appended code is my favourite approach for NT systems; it does not require any additional files - just insert the code into an existing program.

    Gary

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

    In der folgenden Diskussion schreibt er dann (was das Risiko dieses Weges betrifft):

    > Wow, that HANDLE(4) is breathtaking
    > in its daring. Howdya know that the
    > process's main section is always going
    > to be the first thing opened in
    > the process?

    > dave

    > p.s. Your code seems to lack
    > error handling 🙂

    Hello Dave,
    The rather cavalier reasoning and observations that led to the hard-coded value is that when creating a new process, the first handle that is added to the handle table is that of the executable image section and there is no user-mode variable that holds this value (and therefore the handle will presumably never be closed). This is of course just an artifact of the implementation.

    Never having worked on a safety critical system, I am rather in the habit of just letting a program crash and then analysing the remains 🙂

    Gary

    Diese Diskussion geht noch etwas weiter:

    In article 91r679$6si$1@bob.news.rcn.net, "dave porter" porter@mangozoft.com wrote:

    > Actually, I do have one question - it seems
    > you need to CloseHandle -and- FreeLibrary
    > to get rid of the code section?

    > So there's something other than handle #4
    > being open which is keeping the code
    > around? (I presume FreeLibrary is merely
    > Win32ese for FreeImageFileSections, there's nothing
    > particularly libroid about it).

    > dave

    Hello Dave,
    That is an interesting question. If you look at figure 7-24 in the 3rd edition of Inside Microsoft Windows 2000, there are two things that keep the whole mesh of data structures (including the file object for the executable file) alive: the VAD and the section object. CloseHandle(HANDLE(4)) deletes the section object and UnmapViewOfFile(module) deletes the VAD. In the program that I posted, I used FreeLibrary(ULONG(module) + 1) because I had previously observed some undocumented behaviour of FreeLibrary that caused it to ignore reference counts and simply unmap a library if the low bit of the module was set. On reflection, I think that it would be better to use UnmapViewOfFile directly. Attached is an updated (and slightly shorter!) version of the program.

    Gary

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

    [ Dieser Beitrag wurde am 30.06.2002 um 10:31 Uhr von Marc++us editiert. ]



  • Damit ist das Thema noch nicht beendet. In der Ausgabe vom Januar 1998 greift Jeffrey Richter das Thema erneut auf.

    Microsoft Systems Journal, January 1998

    Quelle: http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0198/win320198.htm& nav=/msj/0198/newnav.htm

    Auch hier wieder der Text:

    Q&A Win 32

    Jeffrey Richter wrote Advanced Windows, Third Edition (Microsoft Press, 1998) and Windows 95: A Developer's Guide (M&T Books, 1995). Jeff is a consultant and teaches Win32 programming courses (www.solsem.com). He can be reached at www.JeffreyRichter.com.

    I discussed how to write an unsetup program that was able to delete itself from the disk in my January 1996 Win32® Q&A column, and I presented three techniques for making an application delete itself. A reader recently suggested another technique that I'd like to share with you. I have tested this technique only on Windows NT®, but I believe that it will also work on Windows® 95. Figure 1 shows the code.
    The technique works as follows: when the program wants to delete itself, it first copies the EXE file on the disk to the user's temporary directory. I call this copy the clone. Then the program calls CreateFile to open the cloned EXE file. The call to CreateFile passes the FILE_FLAG_DELETE_ ON_CLOSE option. This tells the operating system to delete the file when it is closed (I'll come back to this later). Finally, the original EXE file spawns this cloned version of the EXE, passing it two command-line arguments. The first argument is the inheritable handle of the original process, and the second is the full path name of the original EXE file. After spawning the cloned version, the original EXE file simply terminates.
    At this point, the cloned version of the EXE file (located in the user's temporary directory) starts running. This version examines its command-line arguments and detects that it is not the original version but the cloned version. The cloned version extracts the process handle to the original EXE file and waits for the original process to terminate. Once the original process has terminated, the cloned process can delete the EXE file image. This, of course, gives you the desired effect of deleting the EXE file that was running. But what about deleting the cloned EXE file that was copied to the user's temporary directory? Because the cloned EXE file was opened by the original EXE with the FILE_FLAG_ DELETE_ON_CLOSE flag, the operating system will delete the cloned EXE file automatically when it terminates. Nothing could be easier.

    Anbei der im Text als Figure 1 bezeichnete Code:

    Figure 1: DeleteMe.cpp

    /*
     Module name: DeleteMe.cpp
     Written by: Jeffrey Richter
     Description: Allows an EXEcutable file to delete itself
     ********************************************************************/
    
     #include <Windows.h>
     #include <stdlib.h>
     #include <tchar.h>
    
     /////////////////////////////////////////////////////////////////////
    
     int WINAPI WinMain(HINSTANCE h, HINSTANCE h2, LPSTR psz, int n) {
    
        // Is this the original EXE or the clone EXE?
        // If the command-line 1  argument, this is the original EXE
        // If the command-line >1 argument, this is the clone EXE
        if (__argc == 1) {
    
           // Original EXE: Spawn clone EXE to delete this EXE
    
           // Copy this EXEcutable image into the user's temp directory
           TCHAR szPathOrig[_MAX_PATH], szPathClone[_MAX_PATH];
           GetModuleFileName(NULL, szPathOrig, _MAX_PATH);
           GetTempPath(_MAX_PATH, szPathClone);
           GetTempFileName(szPathClone, __TEXT("Del"), 0, szPathClone); 
           CopyFile(szPathOrig, szPathClone, FALSE);
    
           // Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE
           HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL,                            
        OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
    
           // Spawn the clone EXE passing it our EXE's process handle
           // and the full path name to the original EXE file.
           TCHAR szCmdLine[512];
           HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE,                                             
        GetCurrentProcessId());
           wsprintf(szCmdLine, __TEXT("%s %d \"%s\""), szPathClone, hProcessOrig,                  
        szPathOrig);
           STARTUPINFO si;
           ZeroMemory(&si, sizeof(si));
           si.cb = sizeof(si);
           PROCESS_INFORMATION pi;
           CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
           CloseHandle(hProcessOrig);
           CloseHandle(hfile);
    
           // This original process can now terminate.
           } else {
    
           // Clone EXE: When original EXE terminates, delete it
           HANDLE hProcessOrig = (HANDLE) _ttoi(__targv[1]);
           WaitForSingleObject(hProcessOrig, INFINITE);
           CloseHandle(hProcessOrig);
           DeleteFile(__targv[2]);
           // Insert code here to remove the subdirectory too (if desired).
    
           // The system will delete the clone EXE automatically 
           // because it was opened with FILE_FLAG_DELETE_ON_CLOSE
        }
        return(0);
     }
    

    Figure 2: Coptex

    Optex.h 
    
     /******************************************************************************
     Module name: Optex.h
     Written by:  Jeffrey Richter
     Purpose:     Defines the COptex (optimized mutex) synchronization object
     ******************************************************************************/
     #pragma once
    
     ///////////////////////////////////////////////////////////////////////////////
    
     class COptex {
     public:
        COptex(LPCSTR pszName,  DWORD dwSpinCount = 4000);
        COptex(LPCWSTR pszName, DWORD dwSpinCount = 4000);
        ~COptex();
        void SetSpinCount(DWORD dwSpinCount);
        void Enter();
        BOOL TryEnter();
        void Leave();
    
     private:
        typedef struct {
           DWORD m_dwSpinCount;
           long  m_lLockCount;
           DWORD m_dwThreadId;
           long  m_lRecurseCount;
        } SHAREDINFO, *PSHAREDINFO;
    
        BOOL        m_fUniprocessorHost;
        HANDLE      m_hevt;
        HANDLE      m_hfm;
        PSHAREDINFO m_pSharedInfo;
    
     private:
        BOOL CommonConstructor(PVOID pszName, BOOL fUnicode, DWORD dwSpinCount);
     };
    
     ///////////////////////////////////////////////////////////////////////////////
    
     inline COptex::COptex(LPCSTR pszName, DWORD dwSpinCount) {
        CommonConstructor((PVOID) pszName, FALSE, dwSpinCount);
     }
    
     ///////////////////////////////////////////////////////////////////////////////
    
     inline COptex::COptex(LPCWSTR pszName, DWORD dwSpinCount) {
        CommonConstructor((PVOID) pszName, TRUE, dwSpinCount);
     }
    
    Optex.cpp 
    
     /******************************************************************************
     Module name: Optex.cpp
     Written by:  Jeffrey Richter
     Purpose:     Implements the COptex (optimized mutex) synchronization object
     ******************************************************************************/
     #include <windows.h>
     #include "Optex.h"
    
     ///////////////////////////////////////////////////////////////////////////////
    
     BOOL COptex::CommonConstructor(PVOID pszName, BOOL fUnicode, DWORD dwSpinCount) 
    {
    
        m_hevt = m_hfm = NULL;
        m_pSharedInfo = NULL;
    
        SYSTEM_INFO sinf;
        GetSystemInfo(&sinf);
        m_fUniprocessorHost = (sinf.dwNumberOfProcessors == 1);
    
        char szNameA[100];
        if (fUnicode) {   // Convert Unicode name to ANSI
           wsprintfA(szNameA, "%S", pszName);
           pszName = (PVOID) szNameA;
        }
        char sz[100];
        wsprintfA(sz, "JMR_Optex_Event_%s", pszName);
        m_hevt = CreateEventA(NULL, FALSE, FALSE, sz);
        if (m_hevt != NULL) {
           wsprintfA(sz, "JMR_Optex_MMF_%s", pszName);
           m_hfm = CreateFileMappingA(NULL, NULL, PAGE_READWRITE, 0,
                                      sizeof(*m_pSharedInfo), sz);
           if (m_hfm != NULL) {
              m_pSharedInfo = (PSHAREDINFO) MapViewOfFile(m_hfm, FILE_MAP_WRITE, 
                                                         0, 0, 0);
    
              // Note: SHAREDINFO's m_lLockCount, m_dwThreadId, and m_lRecurseCount
              // members need to be initialized to 0. Fortunately, a new pagefile 
              // MMF sets all of its data to 0 when created. This saves us from 
              // some thread synchronization work.
    
              if (m_pSharedInfo != NULL) 
                 SetSpinCount(dwSpinCount);
           }
        }
        return((m_hevt != NULL) && (m_hfm != NULL) && (m_pSharedInfo != NULL));
     }
    
     ///////////////////////////////////////////////////////////////////////////////
    
     COptex::~COptex() {
     #ifdef _DEBUG
        if (m_pSharedInfo->m_dwThreadId != 0) DebugBreak();
     #endif
        UnmapViewOfFile(m_pSharedInfo);
        CloseHandle(m_hfm);
        CloseHandle(m_hevt);
     }
    
     ///////////////////////////////////////////////////////////////////////////////
    
     void COptex::SetSpinCount(DWORD dwSpinCount) {
        if (!m_fUniprocessorHost)
           InterlockedExchange((PLONG) &m_pSharedInfo->m_dwSpinCount, dwSpinCount);
     }
    
     ///////////////////////////////////////////////////////////////////////////////
    
     void COptex::Enter() {
    
        // Spin, trying to get the Optex
        if (TryEnter()) return;
    
        DWORD dwThreadId = GetCurrentThreadId();  // The calling thread's ID
        if (InterlockedIncrement(&m_pSharedInfo->m_lLockCount) == 1) {
    
           // Optex is unowned, let this thread own it once
           InterlockedExchange((PLONG) &m_pSharedInfo->m_dwThreadId, dwThreadId);
           m_pSharedInfo->m_lRecurseCount = 1;
    
        } else {
    
           // Optex is owned by a thread
           if (m_pSharedInfo->m_dwThreadId == dwThreadId) {
    
              // Optex is owned by this thread, own it again
              m_pSharedInfo->m_lRecurseCount++;
    
           } else {
    
              // Optex is owned by another thread
              // Wait for the owning thread to release the Optex
              WaitForSingleObject(m_hevt, INFINITE);
    
              // We got ownership of the Optex
              InterlockedExchange((PLONG) &m_pSharedInfo->m_dwThreadId, 
                                   dwThreadId);         // We own it now
              m_pSharedInfo->m_lRecurseCount = 1;       // We own it once
           }
        }
     }
    
     ///////////////////////////////////////////////////////////////////////////////
    
     BOOL COptex::TryEnter() {
    
        DWORD dwThreadId = GetCurrentThreadId();  // The calling thread's ID
    
        // If the lock count is zero, the Optex is unowned and
        // this thread can become the owner of it now.
        BOOL fThisThreadOwnsTheOptex = FALSE;
        DWORD dwSpinCount = m_pSharedInfo->m_dwSpinCount;
        do {
           fThisThreadOwnsTheOptex = (0 == (DWORD) 
              InterlockedCompareExchange((PVOID*) &m_pSharedInfo->m_lLockCount, 
                                          (PVOID) 1, (PVOID) 0)); 
    
           if (fThisThreadOwnsTheOptex) {
    
              // We now own the Optex
              InterlockedExchange((PLONG) &m_pSharedInfo->m_dwThreadId, 
                                   dwThreadId);         // We own it
              m_pSharedInfo->m_lRecurseCount = 1;       // We own it once
           } else {
             // Some thread owns the Optex
             if (m_pSharedInfo->m_dwThreadId == dwThreadId) {
                // We already own the Optex
                InterlockedIncrement(&m_pSharedInfo->m_lLockCount);
                m_pSharedInfo->m_lRecurseCount++;   // We own it again
                fThisThreadOwnsTheOptex = TRUE;     // Return that we own the Optex
             }
          }
       } while (!fThisThreadOwnsTheOptex && (dwSpinCount-- > 0));
    
       // Return whether or not this thread owns the Optex
       return(fThisThreadOwnsTheOptex);
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void COptex::Leave() {
    #ifdef _DEBUG
       if (m_pSharedInfo->m_dwThreadId != GetCurrentThreadId()) 
          DebugBreak();
    #endif
    
       if (--m_pSharedInfo->m_lRecurseCount > 0) {
    
          // We still own the Optex
          InterlockedDecrement(&m_pSharedInfo->m_lLockCount);
    
       } else {
    
          // We don't own the Optex
          InterlockedExchange((PLONG) &m_pSharedInfo->m_dwThreadId, 0);
          if (InterlockedDecrement(&m_pSharedInfo->m_lLockCount) > 0) {
          // Other threads are waiting, wake one of them
             SetEvent(m_hevt);
          }
       }
    }
    
    TestOptex.cpp 
    
    /******************************************************************************
    Module name: TestOptex.cpp
    Written by:  Jeffrey Richter
    Purpose:     A simple app that tests the COptex class 
    NOTE:        Run multiple instances of this app to fully test the COptex
    ******************************************************************************/
    #include <windows.h>
    #include "Optex.h"
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #pragma data_seg("Shared")
    int g_n= 0;
    DWORD g_dw[1024] = { 0 };
    #pragma data_seg()
    
    #pragma comment(linker, "/section:Shared,rws")
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void main() {
       COptex optex("TestOptex");
       while (TRUE) {
          optex.Enter();
          if (g_n == 1024) { optex.Leave(); break; }
          g_dw[g_n++] = GetCurrentProcessId();
          optex.Leave();
       }
       GetLastError();
    }
    

    [ Dieser Beitrag wurde am 30.06.2002 um 10:36 Uhr von Marc++us editiert. ]



  • Hier nun die Listings zu diesem Beispiel:

    Figure 1: ReplaceFileOnReboot

    RFOR.H
    
     /******************************************************************************
    Module name: RFoR.h
    Written by:  Jeffrey Richter
    Note:        This function works on both Windows 95 and Windows NT.
    ******************************************************************************/
    
    BOOL WINAPI ReplaceFileOnReboot (LPCTSTR pszExisting, LPCTSTR pszNew);
    
    ///////////////////////////////// End of File /////////////////////////////////
    RFOR.C
    
     /******************************************************************************
    Module name: RFoR.c
    Written by:  Jeffrey Richter
    Note:        This function abstracts the differences between Windows 95 and 
                 Windows NT for replacing/deleting a file when the system reboots.
    ******************************************************************************/
    
    #define STRICT
    #include <windows.h>
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #include "RFoR.h"
    
    ///////////////////////////////////////////////////////////////////////////////
    
    BOOL WINAPI ReplaceFileOnReboot (LPCTSTR pszExisting, LPCTSTR pszNew) {
    
       // First, attempt to use the MoveFileEx function.
       BOOL fOk = MoveFileEx(pszExisting, pszNew, MOVEFILE_DELAY_UNTIL_REBOOT);
       if (fOk) return(fOk);
    
       // If MoveFileEx failed, we are running on Windows 95 and need to add
       // entries to the WININIT.INI file (an ANSI file).
       // Start a new scope for local variables.
       {
       char szRenameLine[1024];   
    
       int cchRenameLine = wsprintfA(szRenameLine, 
    #ifdef UNICODE
          "%ls=%ls\r\n", 
    #else
          "%hs=%hs\r\n", 
    #endif
          (pszNew == NULL) ? __TEXT("NUL") : pszNew, pszExisting);
          char szRenameSec[] = "[Rename]\r\n";
          int cchRenameSec = sizeof(szRenameSec) - 1;
          HANDLE hfile, hfilemap;
          DWORD dwFileSize, dwRenameLinePos;
          TCHAR szPathnameWinInit[_MAX_PATH];
    
          // Construct the full pathname of the WININIT.INI file.
          GetWindowsDirectory(szPathnameWinInit, _MAX_PATH);
          lstrcat(szPathnameWinInit, __TEXT("\\WinInit.Ini"));
    
          // Open/Create the WININIT.INI file.
          hfile = CreateFile(szPathnameWinInit,      
             GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 
             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    
          if (hfile == INVALID_HANDLE_VALUE) 
             return(fOk);
    
          // Create a file mapping object that is the current size of 
          // the WININIT.INI file plus the length of the additional string
          // that we're about to insert into it plus the length of the section
          // header (which we might have to add).
          dwFileSize = GetFileSize(hfile, NULL);
          hfilemap = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0, 
             dwFileSize + cchRenameLine + cchRenameSec, NULL);
    
          if (hfilemap != NULL) {
    
             // Map the WININIT.INI file into memory.  Note: The contents 
             // of WININIT.INI are always ANSI; never Unicode.
             LPSTR pszWinInit = (LPSTR) MapViewOfFile(hfilemap, 
                FILE_MAP_WRITE, 0, 0, 0);
    
             if (pszWinInit != NULL) {
    
                // Search for the [Rename] section in the file.
                LPSTR pszRenameSecInFile = strstr(pszWinInit, szRenameSec);
    
                if (pszRenameSecInFile == NULL) {
    
                   // There is no [Rename] section in the WININIT.INI file.
                   // We must add the section too.
                   dwFileSize += wsprintfA(&pszWinInit[dwFileSize], "%s",
                                           szRenameSec);
                   dwRenameLinePos = dwFileSize;
    
                } else {
    
                   // We found the [Rename] section, shift all the lines down
                   PSTR pszFirstRenameLine = strchr(pszRenameSecInFile, '\n');
                   // Shift the contents of the file down to make room for 
                   // the newly added line.  The new line is always added 
                   // to the top of the list.
                   pszFirstRenameLine++;   // 1st char on the next line
                   memmove(pszFirstRenameLine + cchRenameLine, pszFirstRenameLine, 
                      pszWinInit + dwFileSize - pszFirstRenameLine);                  
                   dwRenameLinePos = pszFirstRenameLine - pszWinInit;
                }
    
                // Insert the new line
                memcpy(&pszWinInit[dwRenameLinePos], szRenameLine,cchRenameLine);
    
                UnmapViewOfFile(pszWinInit);
    
                // Calculate the true, new size of the file.
                dwFileSize += cchRenameLine;
    
                // Everything was successful.
                fOk = TRUE; 
             }
             CloseHandle(hfilemap);
          }
    
          // Force the end of the file to be the calculated, new size.
          SetFilePointer(hfile, dwFileSize, NULL, FILE_BEGIN);
          SetEndOfFile(hfile);
    
          CloseHandle(hfile);
       }
    
       return(fOk);
    }
    
    ///////////////////////////////// End of File /////////////////////////////////
    

    Figure 2: DELEXE

    DELEXE.H
    
     /******************************************************************************
    Module name: DelExe.h
    Written by:  Jeffrey Richter
    Note:        This function works on Windows 95 but does NOT work on Windows NT.
    ******************************************************************************/
    
    VOID WINAPI DeleteExecutable (DWORD dwExitCode, BOOL fRemoveDir);
    
    ///////////////////////////////// End of File /////////////////////////////////
    DELEXE.C
    
     /******************************************************************************
    Module name: DelExe.c
    Written by:  Jeffrey Richter
    Note:        This function works on Windows 95 but does NOT work on Windows NT.
    ******************************************************************************/
    
    #define STRICT
    #include <Windows.h>
    #include <tchar.h>
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #include "DelExe.h"
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // Prototypes for functions that we explicitly import from Kernel32.DLL
    typedef BOOL (WINAPI *PROCFREELIBRARY)(HINSTANCE);
    typedef BOOL (WINAPI *PROCDELETEFILE)(LPCTSTR);
    typedef BOOL (WINAPI *PROCREMOVEDIRECTORY)(LPCTSTR);
    typedef VOID (WINAPI *PROCEXITPROCESS)(DWORD);
    
    // Data structure containing all the information we need to delete ourselves,
    // remove our containing directory, and terminate ourselves.
    typedef struct {
    
       HINSTANCE hinstExe;
       PROCFREELIBRARY pfnFreeLibrary;
    
       PROCDELETEFILE pfnDeleteFile;
       TCHAR szFile[_MAX_PATH];
    
       PROCREMOVEDIRECTORY pfnRemoveDirectory;
       TCHAR szDir[_MAX_PATH];
    
       PROCEXITPROCESS pfnExitProcess;
       DWORD dwExitCode;
    } DELEXEINFO, *PDELEXEINFO;
    
    typedef VOID (WINAPI *PROCDELEXE)(PDELEXEINFO);
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // Code to be injected into our own address space.
    static void WINAPI DelExeInjCode (PDELEXEINFO pdei) {
    
          // Remove the EXE file from our address space
          pdei->pfnFreeLibrary(pdei->hinstExe);
    
          // Delete the EXE file now that it is no longer in use
          pdei->pfnDeleteFile(pdei->szFile);
    
          if (pdei->pfnRemoveDirectory != NULL) {
                // Remove the directory (which is now empty)
                pdei->pfnRemoveDirectory(pdei->szDir);
          }
    
          // Terminate our process
          pdei->pfnExitProcess(pdei->dwExitCode);
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // This function just marks the end of the previous function
    static void WINAPI AfterDelExeInjCode (void) {
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void WINAPI DeleteExecutable (DWORD dwExitCode, BOOL fRemoveDir) {
    
          HINSTANCE hinstKrnl = GetModuleHandle(__TEXT("KERNEL32"));
          HANDLE hheap = GetProcessHeap();
    
          // Calculate the number of bytes in the DelExeInjCode function.
          const int cbFuncSize = ((LPBYTE)(DWORD)
          AfterDelExeInjCode - (LPBYTE)(DWORD) DelExeInjCode);
    
          DELEXEINFO dei;
    
          // From our process's default heap, allocate memory where we can 
          // inject our own function.
          PROCDELEXE pfnDelExe = HeapAlloc(hheap, HEAP_ZERO_MEMORY, cbFuncSize);
    
          // Inject the DelExeInjCode function into the memory block
          memcpy(pfnDelExe, DelExeInjCode, cbFuncSize);
    
          // Initialize the DELEXEINFO structure.
          dei.hinstExe = GetModuleHandle(NULL);
          dei.pfnFreeLibrary = (PROCFREELIBRARY) 
          GetProcAddress(hinstKrnl, "FreeLibrary");
    
          // Assume that the subdirectory is NOT to be removed.
          dei.pfnRemoveDirectory = NULL;
    #ifdef UNICODE
          dei.pfnDeleteFile = (PROCDELETEFILE) 
          GetProcAddress(hinstKrnl, "DeleteFileW");
    #else
          dei.pfnDeleteFile = (PROCDELETEFILE) 
          GetProcAddress(hinstKrnl, "DeleteFileA");
    #endif
          GetModuleFileName(dei.hinstExe, dei.szFile, _MAX_PATH);
    
       if (fRemoveDir) {
    
                // The subdirectory should be removed.
    #ifdef UNICODE
                dei.pfnRemoveDirectory = (PROCREMOVEDIRECTORY) 
                GetProcAddress(hinstKrnl, "RemoveDirectoryW");
    #else
                dei.pfnRemoveDirectory = (PROCREMOVEDIRECTORY) 
                GetProcAddress(hinstKrnl, "RemoveDirectoryA");
    #endif
                lstrcpy(dei.szDir, dei.szFile);
                *_tcsrchr(dei.szDir, __TEXT('\\')) = 0;
       }
    
          dei.pfnExitProcess = (PROCEXITPROCESS) 
          GetProcAddress(hinstKrnl, "ExitProcess");
          dei.dwExitCode = dwExitCode;
    
          pfnDelExe(&dei);
          // We never get here because pfnDelExe never returns
    }
    
    ///////////////////////////////// End of File /////////////////////////////////
    

    Figure 3: DeleteExecutableBF

    DELEXEBF.H
    
     /******************************************************************************
    Module name: DelExeBF.h
    Written by:  Jeffrey Richter
    Note:        This function works on both Windows 95 and Windows NT.
    ******************************************************************************/
    
    VOID WINAPI DeleteExecutableBF (void);
    
    ///////////////////////////////// End of File /////////////////////////////////
    DELEXEBF.C
    
     /******************************************************************************
    Module name: DelExeBF.c
    Written by:  Jeffrey Richter
    Note:        This function works on both Windows 95 and Windows NT.
    ******************************************************************************/
    
    #define STRICT
    #include <Windows.h>
    #include <tchar.h>
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #include "DelExeBF.h"
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // The name of the temporary batch file
    #define DELUNSETUPBAT     __TEXT("\\DelUS.bat")
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void WINAPI DeleteExecutableBF (void) {
       HANDLE hfile;
       STARTUPINFO si;
       PROCESS_INFORMATION pi;
    
       // Create a batch file that continuously attempts to delete our executable
       // file.  When the executable no longer exists, remove its containing
       // subdirectory, and then delete the batch file too.
       hfile = CreateFile(DELUNSETUPBAT, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
       if (hfile != INVALID_HANDLE_VALUE) {
    
          TCHAR szBatFile[1000];
          TCHAR szUnsetupPathname[_MAX_PATH];
          TCHAR szUnsetupPath[_MAX_PATH];
          DWORD dwNumberOfBytesWritten;
    
          // Get the full pathname of our executable file.
          GetModuleFileName(NULL, szUnsetupPathname, _MAX_PATH);
    
          // Get the path of the executable file (without the filename)
          lstrcpy(szUnsetupPath, szUnsetupPathname);
          *_tcsrchr(szUnsetupPath, __TEXT('\\')) = 0;     // Chop off the name
    
          // Construct the lines for the batch file.
          wsprintf(szBatFile,
             __TEXT(":Repeat\r\n")
             __TEXT("del \"%s\"\r\n")
             __TEXT("if exist \"%s\" goto Repeat\r\n")
             __TEXT("rmdir \"%s\"\r\n")
             __TEXT("del \"%s\"\r\n"), 
             szUnsetupPathname, szUnsetupPathname, szUnsetupPath, DELUNSETUPBAT);
    
          // Write the batch file and close it.
          WriteFile(hfile, szBatFile, lstrlen(szBatFile) * sizeof(TCHAR),
             &dwNumberOfBytesWritten, NULL);
          CloseHandle(hfile);
    
          // Get ready to spawn the batch file we just created.
          ZeroMemory(&si, sizeof(si));
          si.cb = sizeof(si);
    
          // We want its console window to be invisible to the user.
          si.dwFlags = STARTF_USESHOWWINDOW;
          si.wShowWindow = SW_HIDE;
    
          // Spawn the batch file with low-priority and suspended.
          if (CreateProcess(NULL, DELUNSETUPBAT, NULL, NULL, FALSE,
             CREATE_SUSPENDED | IDLE_PRIORITY_CLASS, NULL, __TEXT("\\"), &si, &pi)) {
    
             // Lower the batch file's priority even more.
             SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
    
             // Raise our priority so that we terminate as quickly as possible.
             SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
             SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    
             // Allow the batch file to run and clean-up our handles.
             CloseHandle(pi.hProcess);
             ResumeThread(pi.hThread);
             // We want to terminate right away now so that we can be deleted
             CloseHandle(pi.hThread);
          }
       }
    }
    
    ///////////////////////////////// End of File /////////////////////////////////
    

    Figure 4: TDELEXE.C

    /******************************************************************************
    Module name: TDelExe.c
    Written by:  Jeffrey Richter
    Note:        This function tests the functions that delete our own executable.
    ******************************************************************************/
    
    #define STRICT
    #include <Windows.h>
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #include "RFoR.h"       // to delete this executable file on reboot.
    #include "DelExe.h"     // to delete this executable file using a batch file.
    #include "DelExeBF.h"   // to delete this executable file using code.
    
    ///////////////////////////////////////////////////////////////////////////////
    
    int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE hinstExePrev, 
                        LPSTR lpszCmdLine, int nCmdShow) {
    
       int nId = MessageBox(NULL, 
          __TEXT("Choose Yes to delete this executable file on reboot.\n")
          __TEXT("Choose No to delete this executable file using a batch file.\n")
          __TEXT("Choose Cancel to delete this executable file using code.\n"),
          __TEXT("Delete Executable"), MB_YESNOCANCEL);
    
       switch (nId) {
          case IDYES:
             {
             TCHAR szPathname[_MAX_PATH];
             GetModuleFileName(NULL, szPathname, _MAX_PATH);
             ReplaceFileOnReboot(szPathname, NULL);
             }
             break;
    
          case IDNO:
             DeleteExecutableBF();
             // We want to terminate right away so that we can be deleted
             break;
    
          case IDCANCEL:
            DeleteExecutable(0, TRUE);
            // DeleteExecutable never returns back to us.
             break;
       }
    
           return(0);
    }
    
    ///////////////////////////////// End of File /////////////////////////////////
    


  • Nicht verschwiegen werden soll auch der "Batch-Ansatz", mit dem wohl die meisten Leute das Problem lösen. Es wird am Programmende eine Batch-Datei gestartet, die die Datei von der Platte löscht. Völlig ohne Hacks, aber könnte manchmal Probleme geben, wenn die Batch-Datei zu schnell ist.

    Ursprünglich hier gepostet von Dr. Greenthumb:

    Hallo,
    du musst das mit einer Bat-Datei machen:

    ofstream f("tmp.bat");
    f << ":loop" << endl;
    f << "del test.exe" << endl;
    f << "if exist test.exe goto loop" << endl;
    f << "del tmp.bat" << endl;
    f.close();
    ShellExecute(0,"open","tmp.bat",0,0,SW_SHOW);
    

    Eine ähnliche Variante verwendet den Windows-Scripting-Host und VBScript (setzt voraus daß dies installiert und aktiviert ist):

    include <fstream>
    #include <windows.h>
    using namespace std;
    
    int main (int argc, char * argv[])
    {
        const char * const FileName = argv[0];
        ofstream delme("delme.vbs");
        delme << "On Error Resume Next\n";
        delme << "Set fso = CreateObject(\"Scripting.FileSystemObject\")\n";
        delme << "Do While (fso.FileExists(\"" << FileName << "\"))\n";
        delme << "WScript.Sleep 100\n";
        delme << "fso.DeleteFile(\"" << FileName << "\")\n";
        delme << "Loop\n";
        delme << "fso.DeleteFile(\"delme.vbs\")\n";
        delme.close();
        ShellExecute(0,"open","delme.vbs",0,0,SW_SHOW);
    }
    

    Eingesandt wurde dies von Gerard Choinka.

    [ Dieser Beitrag wurde am 06.07.2002 um 10:21 Uhr von Marc++us editiert. ]



  • Ein diesem nicht ganz unähnlicher Tipp fand sich auch im BuHA-Board www.buhaboard.de:

    *exe löschen
    joar, man kann ja _normalerweise_ sein eigenes programm nicht löschen....fals wer bessere methoden kennt, bitte bei mir melden! ansonsten hier mal nen paar möglichkeiten...

    win9x: man "unmappt" seine exe ausm speicher (mit freelibrary)...und lässt dann seinen code aufm heap weiterlaufen...durch das unmappen wird die exe entladen und der sperrschutz zum löschen is weg (funzt unter winnt nich)...nachteil ist, das man seinen code aufn heap packen muss..

    winnt: man schreibt einfach den lösch code in einen andere prozess...führt diesen code aus (der ein bisl wartet) und kickt sich selbst ausm speicher....der andere prozess löscht einen dann*

    void deleteMySelfWin9x()
    {
       void* freelibrary = GetProcAddress(GetModuleHandle("Kernel32.dll"), "FreeLibrary");
       void* exitprocess = GetProcAddress(GetModuleHandle("Kernel32.dll"), "ExitProcess");
       void* deletefile = GetProcAddress(GetModuleHandle("Kernel32.dll"), "DeleteFileA");
       void* lasterror = GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetLastError");
       char* myApp = new char[MAX_PATH];
       GetModuleFileName(0, myApp, MAX_PATH);
       char* myCode = new char[50];
       myCode[0] = 0x68; // push myApp
       *(DWORD*)(myCode+1) = (DWORD)myApp;
       myCode[5] = 0x68; // push eip simulieren
       *(DWORD*)(myCode+6) = (DWORD)(myCode+16);
       myCode[10] = 0x68; // push deletefile
       *(DWORD*)(myCode+11) = (DWORD) deletefile;
       myCode[15] = 0xc3; // ret
       myCode[16] = 0x90; // nop
    
       myCode[17] = 0x68; // push 0
       *(DWORD*)(myCode+18) = (DWORD)0;
       myCode[22] = 0x68; // push 0
       *(DWORD*)(myCode+23) = (DWORD) 0;
       myCode[27] = 0x68; // push exitprocess
       *(DWORD*)(myCode+28) = (DWORD) exitprocess;
       myCode[32] = 0xc3; // ret
    
       /*myCode[17] = 0x68; // push eip simulieren
       *(DWORD*)(myCode+18) = (DWORD)(myCode+28);
       myCode[22] = 0x68; // push getlasterror
       *(DWORD*)(myCode+23) = (DWORD) lasterror;
       myCode[27] = 0xc3; // ret
       myCode[28] = 0xCC; // int 3h
       */
       __asm
       {
          push 0x00400000
          push myCode
          jmp freelibrary
       }
    }
    
    void deleteMySelfWinNt()
    {
       void* deletefile = GetProcAddress(GetModuleHandle("Kernel32.dll"), "DeleteFileA");
       void* sleep = GetProcAddress(GetModuleHandle("Kernel32.dll"), "Sleep");
       void* exitthread = GetProcAddress(GetModuleHandle("Kernel32.dll"), "ExitThread");
    
       // eine anwendung suchen die wir gebrauchen können;)
       HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
       HANDLE hProcess=0;
       PROCESSENTRY32 pe = { sizeof(pe) };
       if (Process32First(hSnapshot, &pe))
       {
          hProcess=  OpenProcess(PROCESS_VM_WRITE+PROCESS_CREATE_THREAD,
                                 false, pe.th32ProcessID);
          if (!hProcess)
          {
             while (Process32Next(hSnapshot, &pe))
             {
                hProcess = OpenProcess(PROCESS_VM_WRITE+PROCESS_CREATE_THREAD +
                           PROCESS_VM_OPERATION, false, pe.th32ProcessID);
                if (hProcess)
                   break;
             }
          }
       }
       if (!hProcess) // kein prozess gefunden....
                      // hier könnte man notepad oder so starten 
                      // *fg*...aba man findet immer einen! z.b. die shell (explorer.exe)
          return; 
       char* Code = (char*)VirtualAllocEx(hProcess, 0, 50, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
       char* App = (char*)VirtualAllocEx(hProcess, 0, MAX_PATH, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    
       char myApp[MAX_PATH];
       GetModuleFileName(0, myApp, MAX_PATH);
       char myCode[50];
    
       // sleep 1000  == eine sekunde warten
       myCode[0] = 0x68; // push 1000
       *(DWORD*)(myCode+1) = 1000;
       myCode[5] = 0x68; // push eip simulieren
       *(DWORD*)(myCode+6) = (DWORD)(Code+16);
       myCode[10] = 0x68; // push sleep
       *(DWORD*)(myCode+11) = (DWORD) sleep;
       myCode[15] = 0xc3; // ret
    
       // datei löschen
       myCode[16] = 0x68; // push App
       *(DWORD*)(myCode+17) = (DWORD)App;
       myCode[21] = 0x68; // push eip simulieren
       *(DWORD*)(myCode+22) = (DWORD)(Code+32);
       myCode[26] = 0x68; // push deletefile
       *(DWORD*)(myCode+27) = (DWORD) deletefile;
       myCode[31] = 0xc3; // ret
    
       // thread beenden
       myCode[32] = 0x68; // push 0
       *(DWORD*)(myCode+33) = (DWORD)0;
       myCode[37] = 0x68; // push eip simulieren , scheiß egal
       *(DWORD*)(myCode+38) = (DWORD)0;
       myCode[42] = 0x68; // push exitthread
       *(DWORD*)(myCode+43) = (DWORD)exitthread;
       myCode[47] = 0xc3; // ret
       myCode[48] = 0x90; // nop 
    
       // code und dateinamen in den anderen prozess kopieren
       DWORD written;
       WriteProcessMemory(hProcess, Code, myCode, 48, &written);
       WriteProcessMemory(hProcess, App, myApp, MAX_PATH, &written);
    
       // thread im anderen prozess starten
       DWORD id;
       CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)Code,   0, 0, &id);
       // selber kicken
       ExitProcess(0);
    }
    

    [ Dieser Beitrag wurde am 30.06.2002 um 10:30 Uhr von Marc++us editiert. ]



  • Da häng ich noch ein Link dran, der das Thema auch ausführlichst behandelt:
    http://www.catch22.net/tuts/selfdel
    http://blogorama.nerdworks.in/comment.aspx?entryID=21


Anmelden zum Antworten