In this post I’ll explore the concept of self-deleting and how an applications can delete itself from the disk once execution completes, with demonstrations for both windows and linux operating systems. also the different ways it can be implemented and finally I’ll provide a detailed explanation with some code snippet. Source Code
which uses one of the techniques to create a temporary file and use it to delete the original executable. The second example is Linux based, start by forking the current process and creating a thread in the child process that waits for the parent process to terminate, and finally attempts to delete the executable file by repeatedly calling the unlink function until it succeeds.
Win
When it’s time to delete ourselves we first spawns a new process using that file, and then deletes the temporary file. Calling szCmdLine as its input parameter which creates a temporary file in the system’s tmp directory and copies the current executable file to that tmp file finally creates a new process using the temporary file, waits for Sleep(); and then closes the handle to the tmp file
The general idea is to create a temporary copy of the executable file, launch it as a new process, and then delete the original executable before the new process terminates, This can be used as an Opsec or as an evasion technique since the executable file will no longer be available after function gets called.
which uses one of the techniques to create a temporary file and use it to delete the original executable. The second example is Linux based, start by forking the current process and creating a thread in the child process that waits for the parent process to terminate, and finally attempts to delete the executable file by repeatedly calling the unlink function until it succeeds.
Win
C++:
int
CommitSuicide(char *szCmdLine)
{
HANDLE hTemp;
char szPath[MAX_PATH];
char szTemp[MAX_PATH];
static BYTE buf[1024];
STARTUPINFO si;
PROCESS_INFORMATION pi;
UINT ret;
//open a temporary file
GetTempPath(MAX_PATH, szTemp);
lstrcat(szTemp, "suicide.exe");
GetModuleFileName(0, szPath, MAX_PATH);
CopyFile(szPath, szTemp, FALSE);
hTemp = CreateFile(szTemp, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
//Create a process using the temporary executable. This will cause
//the file's handle count to increase, so we can close it.
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
lstrcat(szTemp, " ");
lstrcat(szTemp, szCmdLine);
ret = CreateProcess(0, szTemp, 0, 0, FALSE, NORMAL_PRIORITY_CLASS, 0, 0, &si, &pi);
//Close our handle to the new process. Because the process is
//memory-mapped, there will still be a handle held by the O/S, so
//it won't get deleted. Give the other process a chance to run..
Sleep(100);
CloseHandle(hTemp);
return 0;
}
When it’s time to delete ourselves we first spawns a new process using that file, and then deletes the temporary file. Calling szCmdLine as its input parameter which creates a temporary file in the system’s tmp directory and copies the current executable file to that tmp file finally creates a new process using the temporary file, waits for Sleep(); and then closes the handle to the tmp file
C++:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, PSTR szCmdLine, int iCmdShow)
{
char szPath[MAX_PATH];
// Parse the command line
// Normally, we should not be run with any paramters
// We re-spawn ourselves with the current module's path
if(szCmdLine[0] == '\0')
{
// Spawn a duplicate process, and tell it to delete US
HMODULE hModule = GetModuleHandle(0);
GetModuleFileName(hModule, szPath, MAX_PATH);
CommitSuicide(szPath);
// Exit normally
return 0;
}
// This is where we pick up execution second time we run,
// When a filename has been specified on the command line
else
{
// Give the calling process time to exit...
Sleep(200);
// Delete the file specified on command line.
DeleteFile(szCmdLine);
// Exit normally. When we close, this executable will be automatically
return 0;
}
}
- Next, we check if any command line parameters have been passed we Sleep(); and then deletes the file specified by the command line parameter if none we re-spawn the path of the current executable file as a command line parameter.
C:
/* Structure to inject into remote process. Contains
function pointers and code to execute. */
typedef struct _SELFDEL
{
struct _SELFDEL *Arg0; // pointer to self
unsigned char opCodes[CODESIZE]; // code
pid_t hParent; // parent process pid
void (*fnWaitForSingleObject)(void *);
void (*fnCloseHandle)(void *);
int (*fnUnlink)(const char *);
void (*fnSleep)(unsigned int);
void (*fnExit)(int);
int fRemDir;
char szFileName[PATH_MAX]; // file to delete
} SELFDEL;
/* Routine to execute in remote process. */
static void remote_thread(SELFDEL *remote)
{
// wait for parent process to terminate
void *status;
pthread_join(pthread_self(), &status);
// try to delete the executable file
while(remote->fnUnlink(remote->szFileName) == -1)
{
// failed - try again in one second's time
remote->fnSleep(1);
}
// finished! exit so that we don't execute garbage code
remote->fnExit(0);
}
/* Delete currently running executable and exit */
int SelfDelete(int fRemoveDirectory)
{
SELFDEL local = {0};
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if (pid == 0) { // child process
// copy in binary code
memcpy(local.opCodes, &remote_thread, CODESIZE);
local.fnWaitForSingleObject = (void (*)(void *))pthread_join;
local.fnCloseHandle = (void (*)(void *))pthread_detach;
local.fnUnlink = unlink;
local.fnSleep = (void (*)(unsigned int))sleep;
local.fnExit = exit;
local.fRemDir = fRemoveDirectory;
getcwd(local.szFileName, PATH_MAX);
strcat(local.szFileName, "/");
strcat(local.szFileName, program_invocation_name);
// Give remote process a copy of our own process pid
local.hParent = getpid();
// create detached thread
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t tid;
int rc = pthread_create(&tid, &attr, (void *(*)(void *))&remote_thread, &local);
pthread_attr_destroy(&attr);
if (rc != 0) {
perror("pthread_create");
return -1;
}
// sleep for a second before exiting
sleep(1);
return 0;
} else { // parent process
return 1;
}
}
int main(void)
{
SelfDelete(1);
return 0;
}
The general idea is to create a temporary copy of the executable file, launch it as a new process, and then delete the original executable before the new process terminates, This can be used as an Opsec or as an evasion technique since the executable file will no longer be available after function gets called.