Self Deleting Executables

In this post, I’ll provide an example of self-deleting programs, as well as the different ways it can be used, and finally a detailed explanation. The first example is for Windows (OS), which create a temporary file and use it to delete the original executable, The second example is Linux based simply uses built in functions to initiate the self-deletion process.

Example 01

This program can help make sure that any executables that are created are removed from the system once their job is complete.

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

Example 02

This actually a simple one this function deletes the current executable by obtaining its path readlink and calling the unlink .

void 
CommitSuicide() {
    char path[256];
    int status;

    // Get the path of the current program
    status = readlink("/proc/self/exe", path, sizeof(path) - 1);
    if (status == -1) {
        exit(EXIT_FAILURE);
    }
    path[status] = '\0';

    // Copy the executable to a temporary file
    char tmp_path[] = "/tmp/XXXXXX";
    int fd = mkstemp(tmp_path);
    if (fd == -1) {
        exit(EXIT_FAILURE);
    }
    if (write(fd, path, status) != status) {
        exit(EXIT_FAILURE);
    }
    close(fd);

    // Launch the temporary executable as a new process
    if (fork() == 0) {
        execl(tmp_path, "", NULL);
    }

    // Delete the original executable
    unlink(path);

    // Exit the current process
    exit(EXIT_SUCCESS);
}

int main() {
    CommitSuicide();
    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 anti-analysis technique since the executable file will no longer be available after CommitSuicide function gets called.

That’s all for now. I hope you learned something from this. Please feel free to share any techniques or thought

References

15 Likes

I remember reading about OPSEC TTPs and this was one of them, on-disk and in-memory cleanup. Though the in-memory cleanup is usually implemented the on-disk cleanup isn’t.

What would be nice to see is the manual modification of the $MFT record for NTFS and other artifacts, like JumpLists, AppCompatCache and so on, to almost completely remove the history of the malware ever running (or more like touching disk). Obviously everything from userland is a joke anyway (OALabs reference).

2 Likes

SDelete it handles any existing free portions of the “NTFS” with files that fit within an “MFT record” I actually implemented a self destructive program on Windows Server using this tool worked just fine

It would be nice but it’s not necessary, putting the effort and time specially it the goal does not require a high success rate is not worth it.

1 Like

Didn’t know sdelete modified $MFT entries

1 Like

There are some tricky aspect for example SDelete has no way of allocating free space to securely overwrite it, So to overwrite file names of a file that you delete, SDelete renames the file 26 times, each time replacing each character For instance, the first rename of “foo.txt” would be to “AAA.AAA”.

SDelete - Sysinternals | Microsoft Learn

3 Likes

This is very intresting. Thanks for the great info!

2 Likes

I have found a similar solution to yours

void DelMe() // function to delete self after running
{
     TCHAR szModuleName[MAX_PATH];
     TCHAR szCmd[2 * MAX_PATH];
     STARTUPINFO si = { 0 };
     PROCESS_INFORMATION pi = { 0 };

     GetModuleFileName(NULL, szModuleName, MAX_PATH);

     StringCbPrintf(szCmd, 2 * MAX_PATH, "cmd.exe /C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del /f /q \"%s\"", szModuleName); // cmd.exe ping ... del

     CreateProcess(NULL, szCmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);

     CloseHandle(pi.hThread);
     CloseHandle(pi.hProcess);
     exit(0);
}
3 Likes
2 Likes

Very interesting, might use this later.

1 Like

This topic was automatically closed after 121 days. New replies are no longer allowed.