diff options
| author | Martin Ridgers <[email protected]> | 2021-11-16 14:36:14 +0100 |
|---|---|---|
| committer | Martin Ridgers <[email protected]> | 2021-11-16 14:39:00 +0100 |
| commit | 651eeac1ba759b01f0cbdd10ee792c6be2748195 (patch) | |
| tree | b3f0008f7b70d3737dd9a99b994f024d33d10b9b /zencore/thread.cpp | |
| parent | Added a zen::CreateProc() function for spawning child processes (diff) | |
| download | zen-651eeac1ba759b01f0cbdd10ee792c6be2748195.tar.xz zen-651eeac1ba759b01f0cbdd10ee792c6be2748195.zip | |
zen::CreateProc() variant that can launch processes unelevated
Diffstat (limited to 'zencore/thread.cpp')
| -rw-r--r-- | zencore/thread.cpp | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/zencore/thread.cpp b/zencore/thread.cpp index e155d2ab2..88b92e655 100644 --- a/zencore/thread.cpp +++ b/zencore/thread.cpp @@ -3,11 +3,13 @@ #include <zencore/thread.h> #include <zencore/except.h> +#include <zencore/scopeguard.h> #include <zencore/string.h> #include <zencore/testing.h> #if ZEN_PLATFORM_WINDOWS # include <shellapi.h> +# include <Shlobj.h> # include <zencore/windows.h> #elif ZEN_PLATFORM_LINUX # include <chrono> @@ -625,6 +627,97 @@ static CreateProcResult CreateProcNormal( return ProcessInfo.hProcess; } +static CreateProcResult CreateProcUnelevated( + const std::filesystem::path& Executable, + std::string_view CommandLine, + const CreateProcOptions& Options) +{ + /* Launches a binary with the shell as its parent. The shell (such as + Explorer) should be an unelevated process. */ + + // No sense in using this route if we are not elevated in the first place + if (IsUserAnAdmin() == FALSE) + { + return CreateProcNormal(Executable, CommandLine, Options); + } + + // Get the users' shell process and open it for process creation + HWND ShellWnd = GetShellWindow(); + if (ShellWnd == nullptr) + { + return nullptr; + } + + DWORD ShellPid; + GetWindowThreadProcessId(ShellWnd, &ShellPid); + + HANDLE Process = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, ShellPid); + if (Process == nullptr) + { + return nullptr; + } + auto $0 = MakeGuard([&] { CloseHandle(Process); }); + + // Creating a process as a child of another process is done by setting a + // thread-attribute list on the startup info passed to CreateProcess() + SIZE_T AttrListSize; + InitializeProcThreadAttributeList(nullptr, 1, 0, &AttrListSize); + + auto AttrList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(AttrListSize); + auto $1 = MakeGuard([&] { free(AttrList); }); + + if (!InitializeProcThreadAttributeList(AttrList, 1, 0, &AttrListSize)) + { + return nullptr; + } + + BOOL bOk = UpdateProcThreadAttribute(AttrList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, + (HANDLE*)&Process, sizeof(Process), nullptr, nullptr); + if (!bOk) + { + return nullptr; + } + + // By this point we know we are an elevated process. It is not allowed to + // create a process as a child of another unelevated process that share our + // elevated console window if we have one. So we'll need to create a new one. + uint32_t CreateProcFlags = EXTENDED_STARTUPINFO_PRESENT; + if (GetConsoleWindow() != nullptr) + { + CreateProcFlags |= CREATE_NEW_CONSOLE; + } + else + { + CreateProcFlags |= DETACHED_PROCESS; + } + + // Everything is set up now so we can proceed and launch the process + STARTUPINFOEXW StartupInfo = { + .StartupInfo = { .cb = sizeof(STARTUPINFOEXW) }, + .lpAttributeList = AttrList, + }; + PROCESS_INFORMATION ProcessInfo = {}; + + if (Options.Flags & CreateProcOptions::Flag_NewConsole) + { + CreateProcFlags |= CREATE_NEW_CONSOLE; + } + + ExtendableWideStringBuilder<256> CommandLineZ; + CommandLineZ << CommandLine; + + bOk = CreateProcessW(Executable.c_str(), CommandLineZ.Data(), nullptr, nullptr, + FALSE, CreateProcFlags, nullptr, nullptr, &StartupInfo.StartupInfo, + &ProcessInfo); + if (bOk == FALSE) + { + return nullptr; + } + + CloseHandle(ProcessInfo.hThread); + return ProcessInfo.hProcess; +} + static CreateProcResult CreateProcElevated( const std::filesystem::path& Executable, std::string_view CommandLine, @@ -662,6 +755,11 @@ CreateProcResult CreateProc( const CreateProcOptions& Options) { #if ZEN_PLATFORM_WINDOWS + if (Options.Flags & CreateProcOptions::Flag_Unelevated) + { + return CreateProcUnelevated(Executable, CommandLine, Options); + } + if (Options.Flags & CreateProcOptions::Flag_Elevated) { return CreateProcElevated(Executable, CommandLine, Options); |