4

I'm trying to create a process on another machine that deletes itself when its done. I'm using the DELETE_ON_CLOSE flag with CreateFile. This method is a bit popular but I'm having trouble because I can't execute it while it's open(expected but that's what some solutions do). To work around this I tried opening the file with read permissions. The DELETE_ON_CLOSE flag says that it should only delete a file when all pointers to it are gone. I have a pointer to it with the read, I close the write handle and the file deletes leaving my open handle unreadable. Any other way around this would be gladly appreciated.

I also have considered the possibility that since this is a remote file system there's something funky going on with the handles.

I cannot modify the code of the execuatble I'm sending over so a self-deleting executable is the last thing I want to do.

Making my program wait around to clean up the service will cause it to hang for an unacceptably long time because of how long it can take to destroy a service on the remote box.

//Open remote file for reading and set the delete flag
HANDLE remote_fh = CreateFile(&remote_file_location[0], 
    GENERIC_WRITE, 
    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 
    NULL, 
    CREATE_ALWAYS, 
    FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 
    NULL);

if(!remote_fh)
{
    debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
    RevertToSelf();
    return dead_return;
}

//File to read from
HANDLE local_fh = CreateFile(&local_file_location[0],
    GENERIC_READ,
    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);


if(!local_fh)
{

    debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
    RevertToSelf();
    return dead_return;
}

byte buf[256];

DWORD bytesRead;
DWORD bytesWritten;

//Copy the file
while(ReadFile(local_fh, buf, 256, &bytesRead, NULL) && bytesRead > 0)
{
    WriteFile(remote_fh, buf, bytesRead, &bytesWritten, NULL);
}

CloseHandle(local_fh);


//Create a file retainer to hold the pointer so the file doesn't get deleted before the service starts
HANDLE remote_retain_fh = CreateFile(&remote_file_location[0],
    GENERIC_READ,
    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);

if (!remote_retain_fh)
{
    debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
    RevertToSelf();
    return dead_return;
}


CloseHandle(remote_fh);


//if(!CopyFile(&local_file_location[0], &remote_file_location[0], false))
//{
//  debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
//  RevertToSelf();
//  return dead_return;
//}

remote_service.Create(Service::GetServiceName().c_str());

//In the words of my daughter: "OH, OH, FILE ALL GONE!"
Pipe pipe(L"\\\\" + *hostname + L"\\pipe\\dbg");

CloseHandle(remote_fh);
James M
  • 18,506
  • 3
  • 48
  • 56
Bluebaron
  • 2,289
  • 2
  • 27
  • 37
  • 4
    It is a dead-end. Use the [existing answer](http://stackoverflow.com/questions/1606140/how-can-a-program-delete-its-own-executable) – Hans Passant Aug 13 '13 at 20:49
  • I don't see an appropriate existing answer. – Bluebaron Aug 13 '13 at 21:09
  • 1
    You say "I'm trying to create a process on another machine that deletes itself", but your code looks like it's just trying to have a remote file be deleted on closing -- which is it? Are you trying to create a self-deleting executable? – Adam Rosenfield Aug 13 '13 at 23:08
  • I'm hoping I don't have to modify the code of the executables that I'm sending over. I'm hoping for a universal solution here. It would be a very tough thing to add this to all our existing projects, all new ones in the future, ones that I don't even have control over. – Bluebaron Aug 14 '13 at 00:42
  • Are you sure `HANDLE remote_retain_fh = CreateFile(&remote_file_location[0],...` really succeeds, as there is no error checking dnoe for this call in the sources posted. – alk Aug 14 '13 at 06:07
  • You can't check `CreateFile`'s return value like this `if(!remote_fh)`, since it doesn't return `NULL` on error but `INVALID_HANDLE_VALUE`. – Max Truxa Aug 14 '13 at 10:22
  • The most obvious approach (even if not as pretty as your current idea) would be just to wait for the remote process to die and then delete the file through [`DeleteFile`](http://msdn.microsoft.com/en-us/library/windows/desktop/aa363915%28v=vs.85%29.aspx). – Max Truxa Aug 14 '13 at 10:29
  • Yes the remote_retain_fh file is getting a value. I can read from the remote_retain_fh I close remote_fh. I will fix that, I just coded it in for hackery. – Bluebaron Aug 15 '13 at 17:30
  • @Qntm the problem with that is that Microsoft is quite clear that it can take a very long time to dispose of a service; which is what this is. Just so weird that I have an open pointer but it still dies. I wish I had one single example of someone doing this on even a local system. Lots of people have said this works on a local system. – Bluebaron Aug 15 '13 at 17:32
  • Just use ZwDeleteFile – paulm Aug 15 '13 at 18:03

1 Answers1

1

FILE_FLAG_DELETE_ON_CLOSE requires FILE_SHARE_DELETE permission. The problem with that is that when a process starts up, the operating system opens a file handle to the executable and requires NOT sharing the FILE_SHARE_DELETE permission so that you can't delete the executable file while the process is open.

Consider: you open a handle to an executable file without exclusive FILE_SHARE_DELETE, but say just FILE_GENERIC_READ. Someone else (you, later, or indeed another thread or process) then launches that executable. No problem because nobody's trying to delete the executable. But if you were then to try to gain FILE_SHARE_DELETE permission, you'd fail because the executable is already running with exclusive file delete permission.

Keith4G
  • 387
  • 2
  • 11