aboutsummaryrefslogtreecommitdiff
path: root/zencore/thread.cpp
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2021-11-16 14:36:14 +0100
committerMartin Ridgers <[email protected]>2021-11-16 14:39:00 +0100
commit651eeac1ba759b01f0cbdd10ee792c6be2748195 (patch)
treeb3f0008f7b70d3737dd9a99b994f024d33d10b9b /zencore/thread.cpp
parentAdded a zen::CreateProc() function for spawning child processes (diff)
downloadzen-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.cpp98
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);