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