使用 Shell / CreateProcess() 的 C++ 自更新程序
C++ Self-updater with Shell / CreateProcess()
我想用 C++ 制作一个自我更新程序。我看过这个post如何在执行后删除自己。
How to write a program in C++ such that it will delete itself after execution?
我希望我的程序在执行后 运行 这个 powershell 代码:Image
然后 运行 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 启动控制台应用程序:/我希望它从普通控制台启动,该怎么做?请帮忙谢谢:)
可能在不创建外部进程的情况下删除自身 exe,这将等待我们的进程终止。
想法来自 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;
}
或此代码的 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
(文件 ID 是从 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%) 并执行退出命令 - " =41=] /C exit\r\n"* 命令行(当我们直接传递应用程序名称时-系统不需要解析和修改命令行,它可以是常量字符串)。并向 cmd 注入 2 APC 调用 - 首先等待我们的进程退出,然后删除文件
我想用 C++ 制作一个自我更新程序。我看过这个post如何在执行后删除自己。
How to write a program in C++ such that it will delete itself after execution?
我希望我的程序在执行后 运行 这个 powershell 代码:Image
然后 运行 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 启动控制台应用程序:/我希望它从普通控制台启动,该怎么做?请帮忙谢谢:)
可能在不创建外部进程的情况下删除自身 exe,这将等待我们的进程终止。
想法来自 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;
}
或此代码的 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
(文件 ID 是从 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%) 并执行退出命令 - " =41=] /C exit\r\n"* 命令行(当我们直接传递应用程序名称时-系统不需要解析和修改命令行,它可以是常量字符串)。并向 cmd 注入 2 APC 调用 - 首先等待我们的进程退出,然后删除文件