I would like to make a self updater in c++. I have seen this post of how to delete itself after execution.
How to write a program in C++ such that it will delete itself after execution?
I would like my program to run this powershell code after execution:Image
And afterwards run the newFile.exe.
I already tried to get it to work but no luck. If I do CreateProcess() does the szCmd need to be delayed?
Thanks in advance :)
EDIT :
I almost got it the way I wanted it to, the code:
void shellMove(std::string source, std::string target) {
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
std::string str = "powershell.exe Move-Item -Path " + source + " -Destination " + target + " -Force; " + target;
LPSTR s = const_cast<char*>(str.c_str());
CreateProcessA(NULL, s, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
std::string getDirPath() {
char buffer[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, buffer, MAX_PATH);
return std::string(buffer).substr(0, std::string(buffer).find_last_of("\\"));
}
std::string getFilePath() {
char buffer[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, buffer, MAX_PATH);
return std::string(buffer);
}
void main() {
std::string fileName = "Test.exe";
shellMove(getDirPath() + "\\" + fileName + "~", getDirPath() + "\\" + fileName);
}
The problem is, the Test.exe is a c++ console app, which means if I do:
std::string str = "powershell.exe Move-Item -Path " + source + " -Destination " + target
+ " -Force; " + target;
+target; it starts the console app with Powershell :/ I would like it to start with the normal console, how to do this? please help thanks :)
possible delete self exe without create external process, which will be wait on our process terminate.
idea from Jonas L
ULONG DeleteSelfWin32()
{
// MAX_PATH for more simply code here
WCHAR path[MAX_PATH];
GetModuleFileNameW(0, path, _countof(path));
ULONG dwError = GetLastError();
if (dwError == NOERROR)
{
HANDLE hFile = CreateFileW(path, DELETE, FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILE_RENAME_INFO fri = { TRUE, 0, 2 * sizeof(WCHAR), ':' };
fri.FileName[1] = '~'; // exist place in FILE_RENAME_INFORMATION due align on HANDLE
dwError = SetFileInformationByHandle(hFile, FileRenameInfo, &fri, sizeof(fri)) ? NOERROR : GetLastError();
CloseHandle(hFile);
if (dwError == NOERROR)
{
hFile = CreateFileW(path, DELETE, FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILE_DISPOSITION_INFO fdi = { TRUE };
dwError = SetFileInformationByHandle(hFile, FileDispositionInfo, &fdi, sizeof(fdi)) ? NOERROR : GetLastError();
CloseHandle(hFile);
}
else
{
dwError = GetLastError();
}
}
}
else
{
dwError = GetLastError();
}
}
return dwError;
}
or NT version of this code
NTSTATUS DeleteSelfNT()
{
// MAX_PATH for more simply code here
WCHAR path[MAX_PATH];
GetModuleFileNameW(0, path, _countof(path));
if (GetLastError()) return STATUS_NAME_TOO_LONG;
NTSTATUS status;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
if (0 <= (status = RtlDosPathNameToNtPathName_U_WithStatus(path, &ObjectName, 0, 0)))
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
if (0 <= NtOpenFile(&hFile, DELETE, &oa, &iosb, FILE_SHARE_DELETE, 0))
{
FILE_RENAME_INFORMATION fri = { true, 0, 2*sizeof(WCHAR), ':'};
fri.FileName[1] = '~'; // exist place in FILE_RENAME_INFORMATION due align on HANDLE
status = NtSetInformationFile(hFile, &iosb, &fri, sizeof(fri), FileRenameInformation);
NtClose(hFile);
if (0 <= status)
{
if (0 <= (status = NtOpenFile(&hFile, DELETE, &oa, &iosb, FILE_SHARE_DELETE, 0)))
{
FILE_DISPOSITION_INFORMATION fdi = { true };
status = NtSetInformationFile(hFile, &iosb, &fdi, sizeof(fdi), FileDispositionInformation);
NtClose(hFile);
}
}
}
RtlFreeUnicodeString(&ObjectName);
}
return status;
}
tested and worked on win 7, 8.1, 10. but think this is bug in ntfs implementation.
file is really deleted, not simply become invisible. the parent folder after this also can be deleted (if no more files in it). checked by call NtQueryVolumeInformationFile
with FileFsFullSizeInformation
- AvailableAllocationUnits
is actually increased on file size and by FSCTL_GET_NTFS_FILE_RECORD
( file id is get from NtQueryInformationFile
with FileInternalInformation
) - record became invalid after delete file in such way. if we copy exe back - the SequenceNumber is incremented (if the same MftRecordIndex reused)
if want not based on bug and delete buy create child process, possible for example next solution:
EXTERN_C
NTSYSAPI
VOID
NTAPI
RtlDispatchAPC(
PAPCFUNC pfnAPC,
ULONG_PTR dwData,
PVOID ApcActivationContext
);
BOOL DeleteSelf()
{
WCHAR path[MAX_PATH];
GetModuleFileNameW(0, path, _countof(path));
if (GetLastError()) return FALSE;
BOOL fOk = FALSE;
struct OA_UN : public OBJECT_ATTRIBUTES, UNICODE_STRING {} oa {sizeof(OBJECT_ATTRIBUTES)};
if (0 <= RtlDosPathNameToNtPathName_U_WithStatus(path, &oa, 0, 0))
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
oa.ObjectName = &oa;
NTSTATUS status = NtOpenFile(&hFile, DELETE, &oa, &iosb, FILE_SHARE_DELETE, 0);
RtlFreeUnicodeString(&oa);
if (0 <= status)
{
if (GetEnvironmentVariableW(L"ComSpec", path, _countof(path)))
{
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessW(path, const_cast<PWSTR>(L"* /C exit\r\n"), 0, 0, 0, CREATE_SUSPENDED|DETACHED_PROCESS, 0, 0, &si, &pi))
{
HANDLE hProcess;
if (DuplicateHandle(NtCurrentProcess(), NtCurrentProcess(), pi.hProcess, &hProcess, SYNCHRONIZE, FALSE, 0) &&
0 <= ZwQueueApcThread(pi.hThread, (PKNORMAL_ROUTINE)ZwWaitForSingleObject, hProcess, 0, 0))
{
fOk = DuplicateHandle(NtCurrentProcess(), hFile, pi.hProcess,
&oa.RootDirectory, 0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);
hFile = 0;
if (fOk)
{
oa.ObjectName = 0;
PVOID pv;
fOk = ( pv = VirtualAllocEx(pi.hProcess, 0, sizeof(oa), MEM_COMMIT, PAGE_READWRITE)) &&
(oa.ObjectName = reinterpret_cast<OA_UN*>(pv)) &&
WriteProcessMemory(pi.hProcess, pv, &oa, sizeof(oa), 0) &&
0 <= ZwQueueApcThread(pi.hThread, (PKNORMAL_ROUTINE)RtlDispatchAPC, ZwDeleteFile, pv, INVALID_HANDLE_VALUE);
}
}
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
if (hFile) NtClose(hFile);
}
}
return fOk;
}
here we exec cmd.exe (%ComSpec%) with order do exit - " /C exit\r\n"* command line (when we direct pass application name - system not need parse and modify command line and it can be constant string). and inject 2 APC calls to cmd - first wait for our process exit and then delete file