Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/windhawk/app/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "functions.h"

#include "resource.h"

namespace Functions {

namespace {
Expand Down Expand Up @@ -215,6 +217,12 @@ BOOL IsRunAsAdmin() {
PCWSTR LoadStrFromRsrc(UINT uStrId) {
PCWSTR pStr;
if (!LoadString(nullptr, uStrId, (WCHAR*)&pStr, 0)) {
if (uStrId == IDS_TRAY_ADMIN_CMD) {
return L"Command Prompt (Admin)";
}
if (uStrId == IDS_TRAY_OPEN_ADMIN) {
return L"Open Windhawk (Admin)";
}
pStr = L"(Could not load resource)";
}

Expand Down
59 changes: 59 additions & 0 deletions src/windhawk/app/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "functions.h"
#include "logger.h"
#include "resource.h"
#include "service.h"
#include "session_metadata.h"
#include "ui_control.h"
#include "version.h"
Expand Down Expand Up @@ -508,9 +509,11 @@ LRESULT CMainWindow::OnTrayIcon(UINT uMsg, WPARAM wParam, LPARAM lParam) {
enum class Action {
kNone,
kOpenUI,
kOpenUIAsAdmin,
kOpenUpdatePage,
kModTaskManager,
kToolkit,
kOpenAdminCmd,
kExit,
};

Expand All @@ -522,6 +525,9 @@ LRESULT CMainWindow::OnTrayIcon(UINT uMsg, WPARAM wParam, LPARAM lParam) {

menu.AppendMenu(MF_STRING, static_cast<UINT_PTR>(Action::kOpenUI),
Functions::LoadStrFromRsrc(IDS_TRAY_OPEN));
menu.AppendMenu(MF_STRING,
static_cast<UINT_PTR>(Action::kOpenUIAsAdmin),
Functions::LoadStrFromRsrc(IDS_TRAY_OPEN_ADMIN));
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING,
static_cast<UINT_PTR>(Action::kModTaskManager),
Expand All @@ -531,6 +537,9 @@ LRESULT CMainWindow::OnTrayIcon(UINT uMsg, WPARAM wParam, LPARAM lParam) {
(std::wstring(Functions::LoadStrFromRsrc(IDS_TRAY_TOOLKIT)) +
(m_disableToolkitHotkey ? L"" : L"\tCtrl+Win+W"))
.c_str());
menu.AppendMenu(MF_STRING,
static_cast<UINT_PTR>(Action::kOpenAdminCmd),
Functions::LoadStrFromRsrc(IDS_TRAY_ADMIN_CMD));
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, static_cast<UINT_PTR>(Action::kExit),
Functions::LoadStrFromRsrc(IDS_TRAY_EXIT));
Expand Down Expand Up @@ -570,6 +579,10 @@ LRESULT CMainWindow::OnTrayIcon(UINT uMsg, WPARAM wParam, LPARAM lParam) {
RunUI();
break;

case Action::kOpenUIAsAdmin:
RunUIAsAdmin();
break;

case Action::kOpenUpdatePage:
OpenUpdatePage();
break;
Expand All @@ -582,6 +595,10 @@ LRESULT CMainWindow::OnTrayIcon(UINT uMsg, WPARAM wParam, LPARAM lParam) {
ShowToolkitDialog();
break;

case Action::kOpenAdminCmd:
OpenAdminCmd();
break;

case Action::kExit:
if (m_portable) {
Exit();
Expand Down Expand Up @@ -1015,6 +1032,22 @@ void CMainWindow::StopService(HWND hWnd) {
&bVerificationFlagChecked);
}

void CMainWindow::OpenAdminCmd() {
if (m_portable) {
ShellExecute(m_hWnd, L"runas", L"cmd.exe", nullptr, nullptr,
SW_SHOWNORMAL);
return;
}

try {
Service::LaunchAdminCmd();
} catch (const std::exception& e) {
LOG(L"Failed to launch admin CMD via service: %S", e.what());
ShellExecute(m_hWnd, L"runas", L"cmd.exe", nullptr, nullptr,
SW_SHOWNORMAL);
}
}

void CMainWindow::RunUI(HWND hWnd) {
if (!hWnd) {
hWnd = m_hWnd;
Expand All @@ -1028,6 +1061,32 @@ void CMainWindow::RunUI(HWND hWnd) {
}
}

void CMainWindow::RunUIAsAdmin(HWND hWnd) {
if (!hWnd) {
hWnd = m_hWnd;
}

if (m_portable) {
auto modulePath = wil::GetModuleFileName<std::wstring>();
auto uiExePath =
std::filesystem::path(modulePath).parent_path() / L"windhawk-ui.exe";
ShellExecute(hWnd, L"runas", uiExePath.c_str(), nullptr, nullptr,
SW_SHOWNORMAL);
return;
}

try {
Service::LaunchAdminUI();
} catch (const std::exception& e) {
LOG(L"Failed to launch admin UI via service: %S", e.what());
auto modulePath = wil::GetModuleFileName<std::wstring>();
auto uiExePath =
std::filesystem::path(modulePath).parent_path() / L"windhawk-ui.exe";
ShellExecute(hWnd, L"runas", uiExePath.c_str(), nullptr, nullptr,
SW_SHOWNORMAL);
}
}

void CMainWindow::CloseUI() {
try {
UIControl::CloseUI();
Expand Down
2 changes: 2 additions & 0 deletions src/windhawk/app/main_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ class CMainWindow : public CWindowImpl<CMainWindow, CWindow, CNullTraits>,
void Exit();
void StopService(HWND hWnd = nullptr);
void RunUI(HWND hWnd = nullptr);
void RunUIAsAdmin(HWND hWnd = nullptr);
void CloseUI();
UINT GetNextUpdateDelay(ULONGLONG lastUpdateCheck);
void SetLastUpdateTime();
void ResetLastUpdateTime();
void OpenUpdatePage();
void ShowLoadedModsDialog();
void ShowToolkitDialog(bool triggeredBySystemInstability = false);
void OpenAdminCmd();
void SwitchToSafeMode();
void HandleExplorerCrash(int explorerCrashCount);

Expand Down
2 changes: 2 additions & 0 deletions src/windhawk/app/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#define IDD_TOOLKIT 0x84
#define IDS_TRAY_OPEN 0x90
#define IDS_TRAY_LOADED_MODS 0x91
#define IDS_TRAY_OPEN_ADMIN 0x92
#define IDS_TRAY_TOOLKIT 0xA0
#define IDS_TRAY_ADMIN_CMD 0xA1
#define IDS_TRAY_EXIT 0xB0
#define IDS_EXITDLG_TITLE 0xB1
#define IDS_EXITDLG_CONTENT 0xB2
Expand Down
Binary file modified src/windhawk/app/rsrc.rc
Binary file not shown.
181 changes: 181 additions & 0 deletions src/windhawk/app/service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,94 @@ void CreateProcessOnSessionId(DWORD dwSessionId,
nullptr, &startupInfo, &processInfo));
}

void CreateAdminProcessOnSessionId(DWORD dwSessionId,
const WCHAR* pszPath,
WCHAR* pszCommandLine) {
wil::unique_handle userToken;
THROW_IF_WIN32_BOOL_FALSE(WTSQueryUserToken(dwSessionId, &userToken));

HANDLE hTargetToken = userToken.get();
wil::unique_handle linkedTokenHandle;

TOKEN_LINKED_TOKEN linkedToken = {};
DWORD returnLength = 0;
if (GetTokenInformation(userToken.get(), TokenLinkedToken, &linkedToken,
sizeof(linkedToken), &returnLength) &&
linkedToken.LinkedToken) {
linkedTokenHandle.reset(linkedToken.LinkedToken);
hTargetToken = linkedTokenHandle.get();
}

wil::unique_environment_block environment;
THROW_IF_WIN32_BOOL_FALSE(
CreateEnvironmentBlock(&environment, hTargetToken, FALSE));

wil::unique_process_information processInfo;
STARTUPINFO startupInfo = {sizeof(STARTUPINFO)};
startupInfo.lpDesktop = const_cast<LPWSTR>(L"WinSta0\\Default");

THROW_IF_WIN32_BOOL_FALSE(CreateProcessAsUser(
hTargetToken, pszPath, pszCommandLine, nullptr, nullptr, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, environment.get(),
nullptr, &startupInfo, &processInfo));
}

void LaunchAdminCmdOnActiveSession() {
DWORD sessionId = WTSGetActiveConsoleSessionId();
if (sessionId == 0xFFFFFFFF) {
WTS_SESSION_INFO* sessionInfo = nullptr;
DWORD dwCount = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessionInfo,
&dwCount)) {
wil::unique_wtsmem_ptr<WTS_SESSION_INFO> scopedSessionInfo(
sessionInfo);
for (DWORD i = 0; i < dwCount; i++) {
if (sessionInfo[i].State == WTSActive) {
sessionId = sessionInfo[i].SessionId;
break;
}
}
}
}

if (sessionId == 0xFFFFFFFF) {
THROW_WIN32_MSG(ERROR_NOT_FOUND, "No active interactive session found");
}

WCHAR cmdPath[] = L"C:\\Windows\\System32\\cmd.exe";
CreateAdminProcessOnSessionId(sessionId, cmdPath, nullptr);
}

void LaunchAdminUIOnActiveSession() {
DWORD sessionId = WTSGetActiveConsoleSessionId();
if (sessionId == 0xFFFFFFFF) {
WTS_SESSION_INFO* sessionInfo = nullptr;
DWORD dwCount = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessionInfo,
&dwCount)) {
wil::unique_wtsmem_ptr<WTS_SESSION_INFO> scopedSessionInfo(
sessionInfo);
for (DWORD i = 0; i < dwCount; i++) {
if (sessionInfo[i].State == WTSActive) {
sessionId = sessionInfo[i].SessionId;
break;
}
}
}
}

if (sessionId == 0xFFFFFFFF) {
THROW_WIN32_MSG(ERROR_NOT_FOUND, "No active interactive session found");
}

auto modulePath = wil::GetModuleFileName<std::wstring>();
auto uiExePath =
std::filesystem::path(modulePath).parent_path() / L"windhawk-ui.exe";
std::wstring commandLine = L"\"" + uiExePath.native() + L"\"";
CreateAdminProcessOnSessionId(sessionId, uiExePath.c_str(),
commandLine.data());
}

// Launches the Windhawk daemon on every logged-on session, each on that
// session's own user token. A daemon opens the UI unless started with
// -tray-only, so the session named by runUiSessionId gets one without it, which
Expand Down Expand Up @@ -176,6 +264,8 @@ class ServiceInstance {
wil::unique_event m_svcScanForProcessesEvent;
wil::unique_event m_svcEmergencyStopEvent;
wil::unique_event m_svcSafeModeStopEvent;
wil::unique_event m_svcLaunchAdminCmdEvent;
wil::unique_event m_svcLaunchAdminUIEvent;
std::optional<EngineControl> m_engineControl;
};

Expand Down Expand Up @@ -274,6 +364,12 @@ VOID ServiceInstance::SvcInit(DWORD dwArgc, LPTSTR* lpszArgv) {
m_svcSafeModeStopEvent.reset(Functions::CreateEventForMediumIntegrity(
ServiceCommon::kSafeModeStopEventName, TRUE));

m_svcLaunchAdminCmdEvent.reset(Functions::CreateEventForMediumIntegrity(
ServiceCommon::kLaunchAdminCmdEventName, FALSE));

m_svcLaunchAdminUIEvent.reset(Functions::CreateEventForMediumIntegrity(
ServiceCommon::kLaunchAdminUIEventName, FALSE));

auto settings =
StorageManager::GetInstance().GetAppConfig(L"Settings", false);

Expand Down Expand Up @@ -301,6 +397,8 @@ VOID ServiceInstance::SvcRun(DWORD dwArgc, LPTSTR* lpszArgv) {
m_svcScanForProcessesEvent.get(),
m_svcEmergencyStopEvent.get(),
m_svcSafeModeStopEvent.get(),
m_svcLaunchAdminCmdEvent.get(),
m_svcLaunchAdminUIEvent.get(),
};

while (true) {
Expand Down Expand Up @@ -344,6 +442,28 @@ VOID ServiceInstance::SvcRun(DWORD dwArgc, LPTSTR* lpszArgv) {
break;
}

case WAIT_OBJECT_0 + 4: {
VERBOSE(L"Received launch admin CMD event");
keepLooping = true;
try {
LaunchAdminCmdOnActiveSession();
} catch (const std::exception& e) {
LOG(L"Failed to launch admin CMD: %S", e.what());
}
break;
}

case WAIT_OBJECT_0 + 5: {
VERBOSE(L"Received launch admin UI event");
keepLooping = true;
try {
LaunchAdminUIOnActiveSession();
} catch (const std::exception& e) {
LOG(L"Failed to launch admin UI: %S", e.what());
}
break;
}

default:
LOG(L"Received unknown event %u", dwWaitResult);
break;
Expand Down Expand Up @@ -461,6 +581,15 @@ DWORD ServiceInstance::SvcCtrlHandlerEx(DWORD dwControl,
}
return NO_ERROR;

case ServiceCommon::kControlLaunchAdminCmd:
VERBOSE("Handling SERVICE_CONTROL_LAUNCH_ADMIN_CMD");
try {
LaunchAdminCmdOnActiveSession();
} catch (const std::exception& e) {
LOG(L"Failed to launch admin CMD: %S", e.what());
}
return NO_ERROR;

case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;

Expand Down Expand Up @@ -652,4 +781,56 @@ void Stop(bool disableAutoStart) {
}
}

void LaunchAdminCmd() {
wil::unique_event event(
OpenEvent(EVENT_MODIFY_STATE, FALSE,
ServiceCommon::kLaunchAdminCmdEventName));
if (event) {
SetEvent(event.get());
return;
}

wil::unique_schandle scManager(
OpenSCManager(nullptr, // local computer
nullptr, // ServicesActive database
0));
THROW_LAST_ERROR_IF_NULL(scManager);

wil::unique_schandle service(
OpenService(scManager.get(), ServiceCommon::kName,
SERVICE_USER_DEFINED_CONTROL));
THROW_LAST_ERROR_IF_NULL(service);

SERVICE_STATUS serviceStatus;
THROW_IF_WIN32_BOOL_FALSE(
ControlService(service.get(), ServiceCommon::kControlLaunchAdminCmd,
&serviceStatus));
}

void LaunchAdminUI() {
wil::unique_event event(
OpenEvent(EVENT_MODIFY_STATE, FALSE,
ServiceCommon::kLaunchAdminUIEventName));
if (event) {
SetEvent(event.get());
return;
}

wil::unique_schandle scManager(
OpenSCManager(nullptr, // local computer
nullptr, // ServicesActive database
0));
THROW_LAST_ERROR_IF_NULL(scManager);

wil::unique_schandle service(
OpenService(scManager.get(), ServiceCommon::kName,
SERVICE_USER_DEFINED_CONTROL));
THROW_LAST_ERROR_IF_NULL(service);

SERVICE_STATUS serviceStatus;
THROW_IF_WIN32_BOOL_FALSE(
ControlService(service.get(), ServiceCommon::kControlLaunchAdminUI,
&serviceStatus));
}

} // namespace Service
Loading