Самообновление C ++ с помощью Shell / CreateProcess ()

Я хотел бы сделать самообновление на c ++. Я видел этот пост о том, как удалить себя после выполнения.

Как написать программу на C ++ так, чтобы она удалялась после выполнения?

Я хочу, чтобы моя программа запускала этот код PowerShell после выполнения: Изображение

После этого запустите файл newFile.exe.

Я уже пытался заставить его работать, но безуспешно. Если я использую CreateProcess (), нужно ли отложить szCmd?

Заранее спасибо :)

РЕДАКТИРОВАТЬ:

Я почти получил то, что хотел, код:

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);
}

Проблема в том, что Test.exe - это консольное приложение на C ++, а это значит, что если я это сделаю:

std::string str = "powershell.exe Move-Item -Path " + source + " -Destination " + target 
+ " -Force; " + target;

+ цель; он запускает консольное приложение с помощью Powershell: / Я бы хотел, чтобы оно запускалось с обычной консоли, как это сделать? пожалуйста, помогите спасибо :)


person RequestFX    schedule 22.01.2021    source источник
comment
Вызов Sleep в свой сценарий PowerShell?   -  person Paul Sanders    schedule 23.01.2021
comment
Один из обычных способов решения проблемы - использовать отдельную программу для обновления.   -  person Phil1970    schedule 23.01.2021


Ответы (1)


возможно удалить self exe без создания внешнего процесса, который будет ждать завершения нашего процесса.

идея от Джонаса Л.

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;
}

или NT версия этого кода

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;
}

протестировал и работал на win 7, 8.1, 10. но думаю, что это ошибка в реализации ntfs.

файл действительно удаляется, а не просто становится невидимым. родительская папка после этого также может быть удалена (если в ней больше нет файлов). проверено вызовом NtQueryVolumeInformationFile с FileFsFullSizeInformation - AvailableAllocationUnits фактически увеличивается на размер файла и на FSCTL_GET_NTFS_FILE_RECORD (идентификатор файла получается из NtQueryInformationFile с FileInternalInformation) - запись стала недействительной после удаления файла таким образом. если мы копируем exe обратно - SequenceNumber увеличивается (если тот же MftRecordIndex повторно используется)

если вы хотите не на основе ошибки, а удалить покупку, создать дочерний процесс, возможно, например, следующее решение:

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;
}

здесь мы выполняем cmd.exe (% ComSpec%) с порядком выхода - / C exit \ r \ n * командная строка (когда мы имя приложения прямого прохода - системе не требуется синтаксический анализ и изменение командной строки, это может быть постоянная строка). и введите 2 вызова APC в cmd - сначала дождитесь выхода нашего процесса, а затем удалите файл

person RbMm    schedule 23.01.2021