2020-12-15 20:16:09 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <shellapi.h>
|
|
|
|
#include <sddl.h>
|
|
|
|
|
2021-06-14 17:55:59 +08:00
|
|
|
#include <winrt/base.h>
|
|
|
|
#include <winrt/Windows.Foundation.Collections.h>
|
|
|
|
|
2020-12-15 20:16:09 +08:00
|
|
|
#include <string>
|
2021-05-21 22:24:33 +08:00
|
|
|
#include <common/logger/logger.h>
|
|
|
|
#include <common/utils/winapi_error.h>
|
2020-12-15 20:16:09 +08:00
|
|
|
|
|
|
|
// Returns true if the current process is running with elevated privileges
|
|
|
|
inline bool is_process_elevated(const bool use_cached_value = true)
|
|
|
|
{
|
|
|
|
auto detection_func = []() {
|
|
|
|
HANDLE token = nullptr;
|
|
|
|
bool elevated = false;
|
|
|
|
|
|
|
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
|
|
|
{
|
|
|
|
TOKEN_ELEVATION elevation;
|
|
|
|
DWORD size;
|
|
|
|
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
|
|
|
|
{
|
|
|
|
elevated = (elevation.TokenIsElevated != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token)
|
|
|
|
{
|
|
|
|
CloseHandle(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
return elevated;
|
|
|
|
};
|
|
|
|
static const bool cached_value = detection_func();
|
|
|
|
return use_cached_value ? cached_value : detection_func();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drops the elevated privileges if present
|
|
|
|
inline bool drop_elevated_privileges()
|
|
|
|
{
|
|
|
|
HANDLE token = nullptr;
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT | WRITE_OWNER, &token))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
PSID medium_sid = NULL;
|
|
|
|
if (!::ConvertStringSidToSid(SDDL_ML_MEDIUM, &medium_sid))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TOKEN_MANDATORY_LABEL label = { 0 };
|
|
|
|
label.Label.Attributes = SE_GROUP_INTEGRITY;
|
|
|
|
label.Label.Sid = medium_sid;
|
|
|
|
DWORD size = (DWORD)sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid);
|
|
|
|
|
|
|
|
BOOL result = SetTokenInformation(token, TokenIntegrityLevel, &label, size);
|
|
|
|
LocalFree(medium_sid);
|
|
|
|
CloseHandle(token);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run command as elevated user, returns true if succeeded
|
|
|
|
inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params)
|
|
|
|
{
|
|
|
|
SHELLEXECUTEINFOW exec_info = { 0 };
|
|
|
|
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
|
|
|
|
exec_info.lpVerb = L"runas";
|
|
|
|
exec_info.lpFile = file.c_str();
|
|
|
|
exec_info.lpParameters = params.c_str();
|
|
|
|
exec_info.hwnd = 0;
|
|
|
|
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
|
|
exec_info.lpDirectory = 0;
|
|
|
|
exec_info.hInstApp = 0;
|
|
|
|
exec_info.nShow = SW_SHOWDEFAULT;
|
|
|
|
|
|
|
|
return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL
|
|
|
|
inline bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid)
|
|
|
|
{
|
|
|
|
auto executable_args = L"\"" + file + L"\"";
|
|
|
|
if (!params.empty())
|
|
|
|
{
|
|
|
|
executable_args += L" " + params;
|
|
|
|
}
|
|
|
|
|
|
|
|
HWND hwnd = GetShellWindow();
|
|
|
|
if (!hwnd)
|
|
|
|
{
|
2021-06-10 19:08:56 +08:00
|
|
|
if (GetLastError() == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
Logger::warn(L"GetShellWindow() returned null. Shell window is not available");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::error(L"GetShellWindow() failed. {}", get_last_error_or_default(GetLastError()));
|
|
|
|
}
|
|
|
|
|
2020-12-15 20:16:09 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
DWORD pid;
|
|
|
|
GetWindowThreadProcessId(hwnd, &pid);
|
|
|
|
|
|
|
|
winrt::handle process{ OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid) };
|
|
|
|
if (!process)
|
|
|
|
{
|
2021-05-21 22:24:33 +08:00
|
|
|
Logger::error(L"OpenProcess() failed. {}", get_last_error_or_default(GetLastError()));
|
2020-12-15 20:16:09 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SIZE_T size = 0;
|
|
|
|
|
|
|
|
InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
|
|
|
|
auto pproc_buffer = std::make_unique<char[]>(size);
|
|
|
|
auto pptal = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(pproc_buffer.get());
|
2021-05-21 22:24:33 +08:00
|
|
|
if (!pptal)
|
|
|
|
{
|
|
|
|
Logger::error(L"pptal failed to initialize. {}", get_last_error_or_default(GetLastError()));
|
|
|
|
return false;
|
|
|
|
}
|
2020-12-15 20:16:09 +08:00
|
|
|
|
|
|
|
if (!InitializeProcThreadAttributeList(pptal, 1, 0, &size))
|
|
|
|
{
|
2021-05-21 22:24:33 +08:00
|
|
|
Logger::error(L"InitializeProcThreadAttributeList() failed. {}", get_last_error_or_default(GetLastError()));
|
2020-12-15 20:16:09 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE process_handle = process.get();
|
2021-05-21 22:24:33 +08:00
|
|
|
if (!UpdateProcThreadAttribute(pptal,
|
|
|
|
0,
|
|
|
|
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
|
|
|
|
&process_handle,
|
|
|
|
sizeof(process_handle),
|
|
|
|
nullptr,
|
|
|
|
nullptr))
|
2020-12-15 20:16:09 +08:00
|
|
|
{
|
2021-05-21 22:24:33 +08:00
|
|
|
Logger::error(L"UpdateProcThreadAttribute() failed. {}", get_last_error_or_default(GetLastError()));
|
2020-12-15 20:16:09 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
STARTUPINFOEX siex = { 0 };
|
|
|
|
siex.lpAttributeList = pptal;
|
|
|
|
siex.StartupInfo.cb = sizeof(siex);
|
|
|
|
|
|
|
|
PROCESS_INFORMATION pi = { 0 };
|
|
|
|
auto succeeded = CreateProcessW(file.c_str(),
|
|
|
|
const_cast<LPWSTR>(executable_args.c_str()),
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
FALSE,
|
|
|
|
EXTENDED_STARTUPINFO_PRESENT,
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
&siex.StartupInfo,
|
|
|
|
&pi);
|
|
|
|
if (succeeded)
|
|
|
|
{
|
|
|
|
if (pi.hProcess)
|
|
|
|
{
|
|
|
|
if (returnPid)
|
|
|
|
{
|
|
|
|
*returnPid = GetProcessId(pi.hProcess);
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
}
|
|
|
|
if (pi.hThread)
|
|
|
|
{
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
}
|
|
|
|
}
|
2021-05-21 22:24:33 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::error(L"CreateProcessW() failed. {}", get_last_error_or_default(GetLastError()));
|
|
|
|
}
|
2020-12-15 20:16:09 +08:00
|
|
|
|
|
|
|
return succeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run command with the same elevation, returns true if succeeded
|
|
|
|
inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid)
|
|
|
|
{
|
|
|
|
auto executable_args = L"\"" + file + L"\"";
|
|
|
|
if (!params.empty())
|
|
|
|
{
|
|
|
|
executable_args += L" " + params;
|
|
|
|
}
|
|
|
|
|
2021-01-14 22:29:12 +08:00
|
|
|
STARTUPINFO si = { sizeof(STARTUPINFO) };
|
2020-12-15 20:16:09 +08:00
|
|
|
PROCESS_INFORMATION pi = { 0 };
|
|
|
|
auto succeeded = CreateProcessW(file.c_str(),
|
|
|
|
const_cast<LPWSTR>(executable_args.c_str()),
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
FALSE,
|
|
|
|
0,
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
&si,
|
|
|
|
&pi);
|
|
|
|
|
|
|
|
if (succeeded)
|
|
|
|
{
|
|
|
|
if (pi.hProcess)
|
|
|
|
{
|
|
|
|
if (returnPid)
|
|
|
|
{
|
|
|
|
*returnPid = GetProcessId(pi.hProcess);
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pi.hThread)
|
|
|
|
{
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return succeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the current process is running from administrator account
|
|
|
|
// The function returns true in case of error since we want to return false
|
|
|
|
// only in case of a positive verification that the user is not an admin.
|
|
|
|
inline bool check_user_is_admin()
|
|
|
|
{
|
|
|
|
auto freeMemory = [](PSID pSID, PTOKEN_GROUPS pGroupInfo) {
|
|
|
|
if (pSID)
|
|
|
|
{
|
|
|
|
FreeSid(pSID);
|
|
|
|
}
|
|
|
|
if (pGroupInfo)
|
|
|
|
{
|
|
|
|
GlobalFree(pGroupInfo);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
HANDLE hToken;
|
|
|
|
DWORD dwSize = 0;
|
|
|
|
PTOKEN_GROUPS pGroupInfo;
|
|
|
|
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
|
|
PSID pSID = NULL;
|
|
|
|
|
|
|
|
// Open a handle to the access token for the calling process.
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call GetTokenInformation to get the buffer size.
|
|
|
|
if (!GetTokenInformation(hToken, TokenGroups, NULL, dwSize, &dwSize))
|
|
|
|
{
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate the buffer.
|
|
|
|
pGroupInfo = (PTOKEN_GROUPS)GlobalAlloc(GPTR, dwSize);
|
|
|
|
|
|
|
|
// Call GetTokenInformation again to get the group information.
|
|
|
|
if (!GetTokenInformation(hToken, TokenGroups, pGroupInfo, dwSize, &dwSize))
|
|
|
|
{
|
|
|
|
freeMemory(pSID, pGroupInfo);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a SID for the BUILTIN\Administrators group.
|
|
|
|
if (!AllocateAndInitializeSid(&SIDAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSID))
|
|
|
|
{
|
|
|
|
freeMemory(pSID, pGroupInfo);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop through the group SIDs looking for the administrator SID.
|
|
|
|
for (DWORD i = 0; i < pGroupInfo->GroupCount; ++i)
|
|
|
|
{
|
|
|
|
if (EqualSid(pSID, pGroupInfo->Groups[i].Sid))
|
|
|
|
{
|
|
|
|
freeMemory(pSID, pGroupInfo);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
freeMemory(pSID, pGroupInfo);
|
|
|
|
return false;
|
|
|
|
}
|