#include "service_host.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "path_manager.h" #include "rd_log.h" #include "session_helper_shared.h" namespace crossdesk { namespace { using Json = nlohmann::json; constexpr char kSecureDesktopKeyboardIpcCommandPrefix[] = "secure-input-key:"; constexpr char kSecureDesktopMouseIpcCommandPrefix[] = "secure-input-mouse:"; using SendSasFunction = VOID(WINAPI*)(BOOL); struct SasResult { bool success = false; DWORD error_code = 0; std::string error; }; struct InputDesktopInfo { bool available = false; DWORD error_code = 0; std::string name; }; struct ScopedEnvironmentBlock { ~ScopedEnvironmentBlock() { if (environment != nullptr) { DestroyEnvironmentBlock(environment); } } LPVOID environment = nullptr; }; std::string WideToUtf8(const std::wstring& value); std::wstring GetCurrentExecutablePathW() { wchar_t path[MAX_PATH] = {0}; DWORD length = GetModuleFileNameW(nullptr, path, MAX_PATH); if (length == 0 || length >= MAX_PATH) { return L""; } return std::wstring(path, length); } std::wstring QuoteWindowsArgument(const std::wstring& value) { std::wstring escaped = L"\""; for (wchar_t character : value) { if (character == L'\"') { escaped += L"\\\""; continue; } escaped += character; } escaped += L"\""; return escaped; } void InitializeServiceLogger() { static std::once_flag once_flag; std::call_once(once_flag, []() { PathManager path_manager("CrossDesk"); std::filesystem::path log_path = path_manager.GetLogPath() / "service"; if (!log_path.empty() && path_manager.CreateDirectories(log_path)) { InitLogger(log_path.string()); return; } InitLogger("logs/service"); }); } std::string EscapeJsonString(const std::string& value) { std::string escaped; escaped.reserve(value.size()); for (char character : value) { switch (character) { case '\\': escaped += "\\\\"; break; case '"': escaped += "\\\""; break; case '\b': escaped += "\\b"; break; case '\f': escaped += "\\f"; break; case '\n': escaped += "\\n"; break; case '\r': escaped += "\\r"; break; case '\t': escaped += "\\t"; break; default: escaped += character; break; } } return escaped; } std::string Trim(const std::string& value) { size_t begin = 0; while (begin < value.size() && std::isspace(static_cast(value[begin])) != 0) { ++begin; } size_t end = value.size(); while (end > begin && std::isspace(static_cast(value[end - 1])) != 0) { --end; } return value.substr(begin, end - begin); } std::string ToLower(std::string value) { std::transform(value.begin(), value.end(), value.begin(), [](unsigned char character) { return static_cast(std::tolower(character)); }); return value; } std::string BuildErrorJson(const char* error, DWORD error_code = 0) { std::ostringstream stream; stream << "{\"ok\":false,\"error\":\"" << error << "\""; if (error_code != 0) { stream << ",\"code\":" << error_code; } stream << "}"; return stream.str(); } std::string QueryNamedPipeMessage(const std::wstring& pipe_name, const std::string& command, DWORD timeout_ms) { constexpr int kPipeConnectRetryCount = 3; constexpr DWORD kPipeConnectRetryDelayMs = 15; auto is_transient_pipe_error = [](DWORD error) { return error == ERROR_FILE_NOT_FOUND || error == ERROR_PIPE_BUSY || error == ERROR_SEM_TIMEOUT; }; HANDLE pipe = INVALID_HANDLE_VALUE; for (int attempt = 0; attempt < kPipeConnectRetryCount; ++attempt) { if (!WaitNamedPipeW(pipe_name.c_str(), timeout_ms)) { const DWORD error = GetLastError(); if (attempt + 1 < kPipeConnectRetryCount && is_transient_pipe_error(error)) { Sleep(kPipeConnectRetryDelayMs); continue; } return BuildErrorJson("pipe_unavailable", error); } pipe = CreateFileW(pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (pipe != INVALID_HANDLE_VALUE) { break; } const DWORD error = GetLastError(); if (attempt + 1 < kPipeConnectRetryCount && is_transient_pipe_error(error)) { Sleep(kPipeConnectRetryDelayMs); continue; } return BuildErrorJson("pipe_connect_failed", error); } DWORD pipe_mode = PIPE_READMODE_MESSAGE; SetNamedPipeHandleState(pipe, &pipe_mode, nullptr, nullptr); DWORD bytes_written = 0; if (!WriteFile(pipe, command.data(), static_cast(command.size()), &bytes_written, nullptr)) { std::string error = BuildErrorJson("pipe_write_failed", GetLastError()); CloseHandle(pipe); return error; } char buffer[4096] = {0}; DWORD bytes_read = 0; if (!ReadFile(pipe, buffer, sizeof(buffer) - 1, &bytes_read, nullptr)) { std::string error = BuildErrorJson("pipe_read_failed", GetLastError()); CloseHandle(pipe); return error; } CloseHandle(pipe); return std::string(buffer, buffer + bytes_read); } std::string BuildSecureDesktopKeyboardIpcCommand(int key_code, bool is_down) { std::ostringstream stream; stream << kSecureDesktopKeyboardIpcCommandPrefix << key_code << ":" << (is_down ? 1 : 0); return stream.str(); } std::string BuildSecureDesktopMouseIpcCommand(int x, int y, int wheel, int flag) { std::ostringstream stream; stream << kSecureDesktopMouseIpcCommandPrefix << x << ":" << y << ":" << wheel << ":" << flag; return stream.str(); } std::string BuildSecureInputHelperKeyboardCommand(int key_code, bool is_down) { std::ostringstream stream; stream << kCrossDeskSecureInputKeyboardCommandPrefix << key_code << ":" << (is_down ? 1 : 0); return stream.str(); } bool ParseSecureDesktopKeyboardIpcCommand(const std::string& command, int* key_code_out, bool* is_down_out) { if (key_code_out == nullptr || is_down_out == nullptr) { return false; } if (command.rfind(kSecureDesktopKeyboardIpcCommandPrefix, 0) != 0) { return false; } const size_t key_begin = sizeof(kSecureDesktopKeyboardIpcCommandPrefix) - 1; const size_t separator = command.find(':', key_begin); if (separator == std::string::npos) { return false; } try { *key_code_out = std::stoi(command.substr(key_begin, separator - key_begin)); } catch (...) { return false; } const std::string state = command.substr(separator + 1); if (state == "1" || state == "down") { *is_down_out = true; return true; } if (state == "0" || state == "up") { *is_down_out = false; return true; } return false; } bool CreateSessionSystemToken(DWORD session_id, HANDLE* token_out, DWORD* error_code_out = nullptr) { if (token_out == nullptr) { return false; } *token_out = nullptr; if (error_code_out != nullptr) { *error_code_out = 0; } HANDLE process_token = nullptr; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID, &process_token)) { if (error_code_out != nullptr) { *error_code_out = GetLastError(); } return false; } HANDLE primary_token = nullptr; BOOL duplicated = DuplicateTokenEx( process_token, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID, nullptr, SecurityImpersonation, TokenPrimary, &primary_token); CloseHandle(process_token); if (!duplicated) { if (error_code_out != nullptr) { *error_code_out = GetLastError(); } return false; } if (!SetTokenInformation(primary_token, TokenSessionId, &session_id, sizeof(session_id))) { if (error_code_out != nullptr) { *error_code_out = GetLastError(); } CloseHandle(primary_token); return false; } *token_out = primary_token; return true; } const char* SessionEventToString(DWORD event_type) { switch (event_type) { case WTS_CONSOLE_CONNECT: return "console-connect"; case WTS_CONSOLE_DISCONNECT: return "console-disconnect"; case WTS_REMOTE_CONNECT: return "remote-connect"; case WTS_REMOTE_DISCONNECT: return "remote-disconnect"; case WTS_SESSION_LOGON: return "session-logon"; case WTS_SESSION_LOGOFF: return "session-logoff"; case WTS_SESSION_LOCK: return "session-lock"; case WTS_SESSION_UNLOCK: return "session-unlock"; default: return "unknown"; } } const char* DetermineInteractiveStage(bool lock_app_visible, bool credential_ui_visible, bool secure_desktop_active) { if (credential_ui_visible) { return "credential-ui"; } if (lock_app_visible) { return "lock-screen"; } if (secure_desktop_active) { return "secure-desktop"; } return "user-desktop"; } bool GetSessionUserName(DWORD session_id, std::wstring* username_out) { if (username_out == nullptr) { return false; } username_out->clear(); LPWSTR username = nullptr; DWORD bytes = 0; if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &username, &bytes)) { return false; } if (username != nullptr) { *username_out = username; WTSFreeMemory(username); return true; } return false; } bool QuerySessionLockState(DWORD session_id, bool* session_locked_out) { if (session_locked_out == nullptr) { return false; } *session_locked_out = false; PWTSINFOEXW session_info = nullptr; DWORD bytes = 0; if (!WTSQuerySessionInformationW( WTS_CURRENT_SERVER_HANDLE, session_id, WTSSessionInfoEx, reinterpret_cast(&session_info), &bytes)) { return false; } bool success = false; if (session_info != nullptr && bytes >= sizeof(WTSINFOEXW) && session_info->Level == 1) { const LONG session_flags = session_info->Data.WTSInfoExLevel1.SessionFlags; if (session_flags == WTS_SESSIONSTATE_LOCK) { *session_locked_out = true; success = true; } else if (session_flags == WTS_SESSIONSTATE_UNLOCK) { *session_locked_out = false; success = true; } } if (session_info != nullptr) { WTSFreeMemory(session_info); } return success; } bool IsLogonUiRunningInSession(DWORD session_id) { HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { return false; } PROCESSENTRY32W entry{}; entry.dwSize = sizeof(entry); bool found = false; if (Process32FirstW(snapshot, &entry)) { do { if (_wcsicmp(entry.szExeFile, L"LogonUI.exe") != 0) { continue; } DWORD process_session_id = 0xFFFFFFFF; if (ProcessIdToSessionId(entry.th32ProcessID, &process_session_id) && process_session_id == session_id) { found = true; break; } } while (Process32NextW(snapshot, &entry)); } CloseHandle(snapshot); return found; } InputDesktopInfo GetInputDesktopInfo() { InputDesktopInfo info; HDESK desktop = OpenInputDesktop(0, FALSE, GENERIC_READ); if (desktop == nullptr) { info.error_code = GetLastError(); return info; } DWORD bytes_needed = 0; GetUserObjectInformationW(desktop, UOI_NAME, nullptr, 0, &bytes_needed); if (bytes_needed == 0) { info.error_code = GetLastError(); CloseDesktop(desktop); return info; } std::wstring desktop_name(bytes_needed / sizeof(wchar_t), L'\0'); if (!GetUserObjectInformationW(desktop, UOI_NAME, desktop_name.data(), bytes_needed, &bytes_needed)) { info.error_code = GetLastError(); CloseDesktop(desktop); return info; } CloseDesktop(desktop); while (!desktop_name.empty() && desktop_name.back() == L'\0') { desktop_name.pop_back(); } info.available = true; info.name = WideToUtf8(desktop_name); return info; } std::string WideToUtf8(const std::wstring& value) { if (value.empty()) { return {}; } int size_needed = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, nullptr, 0, nullptr, nullptr); if (size_needed <= 1) { return {}; } std::string result(static_cast(size_needed), '\0'); WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, result.data(), size_needed, nullptr, nullptr); result.pop_back(); return result; } bool QuerySoftwareSasGeneration(DWORD* value_out, bool* existed_out) { if (value_out == nullptr || existed_out == nullptr) { return false; } *value_out = 0; *existed_out = false; HKEY key = nullptr; constexpr wchar_t kPolicyKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, kPolicyKey, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &key) != ERROR_SUCCESS) { return false; } DWORD value = 0; DWORD value_size = sizeof(value); DWORD type = REG_DWORD; LONG result = RegQueryValueExW(key, L"SoftwareSASGeneration", nullptr, &type, reinterpret_cast(&value), &value_size); if (result == ERROR_SUCCESS && type == REG_DWORD) { *value_out = value; *existed_out = true; } RegCloseKey(key); return true; } bool SetSoftwareSasGeneration(DWORD value) { HKEY key = nullptr; constexpr wchar_t kPolicyKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; DWORD disposition = 0; LONG result = RegCreateKeyExW(HKEY_LOCAL_MACHINE, kPolicyKey, 0, nullptr, 0, KEY_SET_VALUE, nullptr, &key, &disposition); if (result != ERROR_SUCCESS) { return false; } result = RegSetValueExW(key, L"SoftwareSASGeneration", 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); RegCloseKey(key); return result == ERROR_SUCCESS; } bool RestoreSoftwareSasGeneration(DWORD original_value, bool existed_before) { HKEY key = nullptr; constexpr wchar_t kPolicyKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, kPolicyKey, 0, KEY_SET_VALUE, &key); if (result != ERROR_SUCCESS) { return false; } if (!existed_before) { result = RegDeleteValueW(key, L"SoftwareSASGeneration"); RegCloseKey(key); return result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND; } result = RegSetValueExW(key, L"SoftwareSASGeneration", 0, REG_DWORD, reinterpret_cast(&original_value), sizeof(original_value)); RegCloseKey(key); return result == ERROR_SUCCESS; } SasResult SendSasNow() { SasResult result; HMODULE sas_module = LoadLibraryW(L"sas.dll"); if (sas_module == nullptr) { result.error = "sas_dll_unavailable"; result.error_code = GetLastError(); return result; } auto* send_sas = reinterpret_cast(GetProcAddress(sas_module, "SendSAS")); if (send_sas == nullptr) { result.error = "send_sas_proc_missing"; result.error_code = GetLastError(); FreeLibrary(sas_module); return result; } DWORD original_value = 0; bool existed_before = false; bool queried = QuerySoftwareSasGeneration(&original_value, &existed_before); bool mutated_policy = false; if (queried) { if (!existed_before || (original_value != 1 && original_value != 3)) { if (!SetSoftwareSasGeneration(1)) { result.error = "set_software_sas_generation_failed"; result.error_code = GetLastError(); FreeLibrary(sas_module); return result; } mutated_policy = true; } } send_sas(FALSE); if (mutated_policy) { RestoreSoftwareSasGeneration(original_value, existed_before); } FreeLibrary(sas_module); result.success = true; return result; } struct PipeSecurityAttributes { PipeSecurityAttributes() = default; ~PipeSecurityAttributes() { if (security_descriptor_ != nullptr) { LocalFree(security_descriptor_); } } bool Initialize() { constexpr wchar_t kPipeSddl[] = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;AU)"; if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( kPipeSddl, SDDL_REVISION_1, &security_descriptor_, nullptr)) { return false; } attributes_.nLength = sizeof(attributes_); attributes_.lpSecurityDescriptor = security_descriptor_; attributes_.bInheritHandle = FALSE; return true; } SECURITY_ATTRIBUTES* get() { return &attributes_; } private: SECURITY_ATTRIBUTES attributes_{}; PSECURITY_DESCRIPTOR security_descriptor_ = nullptr; }; struct KernelObjectSecurityAttributes { KernelObjectSecurityAttributes() = default; ~KernelObjectSecurityAttributes() { if (security_descriptor_ != nullptr) { LocalFree(security_descriptor_); } } bool Initialize() { constexpr wchar_t kObjectSddl[] = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;AU)"; if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( kObjectSddl, SDDL_REVISION_1, &security_descriptor_, nullptr)) { return false; } attributes_.nLength = sizeof(attributes_); attributes_.lpSecurityDescriptor = security_descriptor_; attributes_.bInheritHandle = FALSE; return true; } SECURITY_ATTRIBUTES* get() { return &attributes_; } private: SECURITY_ATTRIBUTES attributes_{}; PSECURITY_DESCRIPTOR security_descriptor_ = nullptr; }; } // namespace CrossDeskServiceHost* CrossDeskServiceHost::instance_ = nullptr; CrossDeskServiceHost::CrossDeskServiceHost() { InitializeServiceLogger(); } CrossDeskServiceHost::~CrossDeskServiceHost() { ShutdownRuntime(); } void WINAPI CrossDeskServiceHost::ServiceMain([[maybe_unused]] DWORD argc, [[maybe_unused]] LPWSTR* argv) { if (instance_ != nullptr) { instance_->RunServiceLoop(true); } } BOOL WINAPI CrossDeskServiceHost::ConsoleControlHandler(DWORD control_type) { if (instance_ == nullptr) { return FALSE; } switch (control_type) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: case CTRL_CLOSE_EVENT: case CTRL_SHUTDOWN_EVENT: instance_->RequestStop(); return TRUE; default: return FALSE; } } DWORD WINAPI CrossDeskServiceHost::ServiceControlHandler( DWORD control, DWORD event_type, LPVOID event_data, [[maybe_unused]] LPVOID context) { if (instance_ == nullptr) { return ERROR_CALL_NOT_IMPLEMENTED; } switch (control) { case SERVICE_CONTROL_INTERROGATE: instance_->ReportServiceStatus(instance_->service_status_.dwCurrentState, NO_ERROR, 0); return NO_ERROR; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: instance_->ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3000); instance_->RequestStop(); return NO_ERROR; case SERVICE_CONTROL_SESSIONCHANGE: { DWORD session_id = 0xFFFFFFFF; if (event_data != nullptr) { auto* session = reinterpret_cast(event_data); session_id = session->dwSessionId; } instance_->RecordSessionEvent(event_type, session_id); return NO_ERROR; } default: return ERROR_CALL_NOT_IMPLEMENTED; } } int CrossDeskServiceHost::RunAsService() { instance_ = this; SERVICE_TABLE_ENTRYW service_table[] = { {const_cast(kCrossDeskServiceName), &CrossDeskServiceHost::ServiceMain}, {nullptr, nullptr}}; if (!StartServiceCtrlDispatcherW(service_table)) { DWORD error = GetLastError(); LOG_ERROR("StartServiceCtrlDispatcherW failed, error={}", error); return static_cast(error); } return 0; } int CrossDeskServiceHost::RunInConsole() { instance_ = this; return RunServiceLoop(false); } int CrossDeskServiceHost::InitializeRuntime() { stop_event_ = CreateEventW(nullptr, TRUE, FALSE, nullptr); if (stop_event_ == nullptr) { DWORD error = GetLastError(); LOG_ERROR("CreateEventW failed, error={}", error); return static_cast(error); } started_at_tick_ = GetTickCount64(); last_sas_tick_ = 0; active_session_id_ = WTSGetActiveConsoleSessionId(); process_session_id_ = 0xFFFFFFFF; input_desktop_error_code_ = 0; session_helper_process_id_ = 0; session_helper_session_id_ = 0xFFFFFFFF; session_helper_exit_code_ = 0; session_helper_last_error_code_ = 0; session_helper_status_error_code_ = 0; session_helper_report_session_id_ = 0xFFFFFFFF; session_helper_report_process_id_ = 0; session_helper_report_input_desktop_error_code_ = 0; secure_input_helper_process_id_ = 0; secure_input_helper_session_id_ = 0xFFFFFFFF; secure_input_helper_exit_code_ = 0; secure_input_helper_last_error_code_ = 0; session_locked_ = false; logon_ui_visible_ = false; prelogin_ = false; secure_desktop_active_ = false; input_desktop_available_ = false; session_helper_running_ = false; session_helper_status_ok_ = false; session_helper_report_input_desktop_available_ = false; session_helper_report_lock_app_visible_ = false; session_helper_report_logon_ui_visible_ = false; session_helper_report_secure_desktop_active_ = false; session_helper_report_credential_ui_visible_ = false; session_helper_report_unlock_ui_visible_ = false; secure_input_helper_running_ = false; last_sas_error_code_ = 0; last_sas_success_ = false; session_helper_started_at_tick_ = 0; session_helper_report_state_age_ms_ = 0; session_helper_report_uptime_ms_ = 0; secure_input_helper_started_at_tick_ = 0; session_helper_process_handle_ = nullptr; session_helper_stop_event_ = nullptr; secure_input_helper_process_handle_ = nullptr; secure_input_helper_stop_event_ = nullptr; input_desktop_name_.clear(); last_sas_error_.clear(); session_helper_last_error_.clear(); session_helper_status_error_.clear(); session_helper_report_input_desktop_.clear(); session_helper_report_interactive_stage_.clear(); secure_input_helper_last_error_.clear(); last_session_event_type_ = 0; last_session_event_session_id_ = active_session_id_; RefreshSessionState(); EnsureSessionHelper(); ipc_thread_ = std::thread(&CrossDeskServiceHost::IpcServerLoop, this); LOG_INFO("CrossDesk service runtime initialized, session_id={}", active_session_id_); return 0; } void CrossDeskServiceHost::ShutdownRuntime() { StopSecureInputHelper(); StopSessionHelper(); if (stop_event_ != nullptr) { SetEvent(stop_event_); } if (ipc_thread_.joinable()) { ipc_thread_.join(); } if (stop_event_ != nullptr) { CloseHandle(stop_event_); stop_event_ = nullptr; } } void CrossDeskServiceHost::RequestStop() { if (stop_event_ != nullptr) { SetEvent(stop_event_); } } void CrossDeskServiceHost::ReportServiceStatus(DWORD current_state, DWORD win32_exit_code, DWORD wait_hint) { if (status_handle_ == nullptr) { return; } service_status_.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status_.dwCurrentState = current_state; service_status_.dwWin32ExitCode = win32_exit_code; service_status_.dwWaitHint = wait_hint; service_status_.dwControlsAccepted = current_state == SERVICE_RUNNING ? (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE) : 0; SetServiceStatus(status_handle_, &service_status_); } int CrossDeskServiceHost::RunServiceLoop(bool as_service) { console_mode_ = !as_service; if (as_service) { status_handle_ = RegisterServiceCtrlHandlerExW( kCrossDeskServiceName, &CrossDeskServiceHost::ServiceControlHandler, this); if (status_handle_ == nullptr) { DWORD error = GetLastError(); LOG_ERROR("RegisterServiceCtrlHandlerExW failed, error={}", error); return static_cast(error); } ReportServiceStatus(SERVICE_START_PENDING, NO_ERROR, 3000); } int init_error = InitializeRuntime(); if (init_error != 0) { if (as_service) { ReportServiceStatus(SERVICE_STOPPED, static_cast(init_error), 0); } return init_error; } if (as_service) { ReportServiceStatus(SERVICE_RUNNING, NO_ERROR, 0); } if (console_mode_) { SetConsoleCtrlHandler(&CrossDeskServiceHost::ConsoleControlHandler, TRUE); std::cout << "CrossDesk service skeleton running in console mode. Press " "Ctrl+C to stop." << std::endl; } WaitForSingleObject(stop_event_, INFINITE); if (as_service) { ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3000); } ShutdownRuntime(); if (console_mode_) { SetConsoleCtrlHandler(&CrossDeskServiceHost::ConsoleControlHandler, FALSE); } if (as_service) { ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0); } return 0; } void CrossDeskServiceHost::IpcServerLoop() { PipeSecurityAttributes security_attributes; SECURITY_ATTRIBUTES* pipe_attributes = nullptr; if (security_attributes.Initialize()) { pipe_attributes = security_attributes.get(); } else { LOG_WARN("Pipe security initialization failed, error={}", GetLastError()); } while (stop_event_ != nullptr && WaitForSingleObject(stop_event_, 0) != WAIT_OBJECT_0) { HANDLE pipe = CreateNamedPipeW( kCrossDeskServicePipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 4096, 4096, 0, pipe_attributes); if (pipe == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); LOG_ERROR("CreateNamedPipeW failed, error={}", error); WaitForSingleObject(stop_event_, 1000); continue; } OVERLAPPED overlapped{}; overlapped.hEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); if (overlapped.hEvent == nullptr) { LOG_ERROR("CreateEventW for pipe failed, error={}", GetLastError()); CloseHandle(pipe); break; } BOOL connected = ConnectNamedPipe(pipe, &overlapped); DWORD connect_error = connected ? ERROR_SUCCESS : GetLastError(); if (connected) { SetEvent(overlapped.hEvent); } if (!connected && connect_error == ERROR_PIPE_CONNECTED) { SetEvent(overlapped.hEvent); connected = TRUE; } if (!connected && connect_error != ERROR_IO_PENDING) { LOG_WARN("ConnectNamedPipe failed, error={}", connect_error); CloseHandle(overlapped.hEvent); CloseHandle(pipe); continue; } if (!connected) { HANDLE wait_handles[] = {stop_event_, overlapped.hEvent}; DWORD wait_result = WaitForMultipleObjects(2, wait_handles, FALSE, INFINITE); if (wait_result == WAIT_OBJECT_0) { CancelIoEx(pipe, &overlapped); CloseHandle(overlapped.hEvent); CloseHandle(pipe); break; } } char buffer[1024] = {0}; DWORD bytes_read = 0; if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &bytes_read, nullptr) && bytes_read > 0) { std::string response = HandleIpcCommand(std::string(buffer, buffer + bytes_read)); DWORD bytes_written = 0; WriteFile(pipe, response.data(), static_cast(response.size()), &bytes_written, nullptr); FlushFileBuffers(pipe); } DisconnectNamedPipe(pipe); CloseHandle(overlapped.hEvent); CloseHandle(pipe); } } void CrossDeskServiceHost::RefreshSessionState() { std::lock_guard lock(state_mutex_); active_session_id_ = WTSGetActiveConsoleSessionId(); DWORD process_session_id = 0xFFFFFFFF; if (ProcessIdToSessionId(GetCurrentProcessId(), &process_session_id)) { process_session_id_ = process_session_id; } logon_ui_visible_ = IsLogonUiRunningInSession(active_session_id_); InputDesktopInfo desktop_info = GetInputDesktopInfo(); input_desktop_available_ = desktop_info.available; input_desktop_error_code_ = desktop_info.error_code; input_desktop_name_ = desktop_info.name; secure_desktop_active_ = _stricmp(input_desktop_name_.c_str(), "Winlogon") == 0; std::wstring username; bool username_available = GetSessionUserName(active_session_id_, &username); prelogin_ = !username_available || username.empty(); if (!QuerySessionLockState(active_session_id_, &session_locked_)) { session_locked_ = (logon_ui_visible_ || secure_desktop_active_) && !prelogin_; } } void CrossDeskServiceHost::ResetSessionHelperReportedStateLocked( const char* error, DWORD error_code) { session_helper_status_ok_ = false; session_helper_status_error_ = error != nullptr ? error : ""; session_helper_status_error_code_ = error_code; session_helper_report_session_id_ = 0xFFFFFFFF; session_helper_report_process_id_ = 0; session_helper_report_session_locked_ = false; session_helper_report_input_desktop_available_ = false; session_helper_report_input_desktop_error_code_ = 0; session_helper_report_input_desktop_.clear(); session_helper_report_lock_app_visible_ = false; session_helper_report_logon_ui_visible_ = false; session_helper_report_secure_desktop_active_ = false; session_helper_report_credential_ui_visible_ = false; session_helper_report_unlock_ui_visible_ = false; session_helper_report_interactive_stage_.clear(); session_helper_report_state_age_ms_ = 0; session_helper_report_uptime_ms_ = 0; } bool CrossDeskServiceHost::GetEffectiveSessionLockedLocked() const { return session_helper_status_ok_ ? session_helper_report_session_locked_ : session_locked_; } bool CrossDeskServiceHost::IsHelperReportingLockScreenLocked() const { return session_helper_report_lock_app_visible_ || session_helper_report_interactive_stage_ == "lock-screen"; } bool CrossDeskServiceHost::HasSecureInputUiLocked() const { return prelogin_ || secure_desktop_active_ || logon_ui_visible_ || session_helper_report_credential_ui_visible_ || session_helper_report_secure_desktop_active_ || session_helper_report_unlock_ui_visible_ || session_helper_report_interactive_stage_ == "credential-ui" || session_helper_report_interactive_stage_ == "secure-desktop"; } bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked( DWORD target_session_id) const { if (target_session_id == 0xFFFFFFFF) { return false; } return HasSecureInputUiLocked() || (GetEffectiveSessionLockedLocked() && IsHelperReportingLockScreenLocked()); } std::wstring CrossDeskServiceHost::GetSessionHelperPath() const { std::wstring current_executable = GetCurrentExecutablePathW(); if (current_executable.empty()) { return L""; } return (std::filesystem::path(current_executable).parent_path() / L"crossdesk_session_helper.exe") .wstring(); } std::wstring CrossDeskServiceHost::GetSessionHelperStopEventName( DWORD session_id) const { return L"Global\\CrossDeskSessionHelperStop-" + std::to_wstring(session_id); } std::wstring CrossDeskServiceHost::GetSecureInputHelperPath() const { return GetSessionHelperPath(); } std::wstring CrossDeskServiceHost::GetSecureInputHelperStopEventName( DWORD session_id) const { return L"Global\\CrossDeskSecureInputHelperStop-" + std::to_wstring(session_id); } void CrossDeskServiceHost::ReapSessionHelper() { HANDLE process_handle = nullptr; HANDLE stop_event_handle = nullptr; DWORD exit_code = 0; { std::lock_guard lock(state_mutex_); if (session_helper_process_handle_ == nullptr) { return; } DWORD wait_result = WaitForSingleObject(session_helper_process_handle_, 0); if (wait_result != WAIT_OBJECT_0) { session_helper_running_ = true; return; } GetExitCodeProcess(session_helper_process_handle_, &exit_code); process_handle = session_helper_process_handle_; stop_event_handle = session_helper_stop_event_; session_helper_process_handle_ = nullptr; session_helper_stop_event_ = nullptr; session_helper_running_ = false; session_helper_process_id_ = 0; session_helper_exit_code_ = exit_code; session_helper_started_at_tick_ = 0; } if (process_handle != nullptr) { CloseHandle(process_handle); } if (stop_event_handle != nullptr) { CloseHandle(stop_event_handle); } } void CrossDeskServiceHost::ReapSecureInputHelper() { HANDLE process_handle = nullptr; HANDLE stop_event_handle = nullptr; DWORD exit_code = 0; { std::lock_guard lock(state_mutex_); if (secure_input_helper_process_handle_ == nullptr) { return; } DWORD wait_result = WaitForSingleObject(secure_input_helper_process_handle_, 0); if (wait_result != WAIT_OBJECT_0) { secure_input_helper_running_ = true; return; } GetExitCodeProcess(secure_input_helper_process_handle_, &exit_code); process_handle = secure_input_helper_process_handle_; stop_event_handle = secure_input_helper_stop_event_; secure_input_helper_process_handle_ = nullptr; secure_input_helper_stop_event_ = nullptr; secure_input_helper_running_ = false; secure_input_helper_process_id_ = 0; secure_input_helper_exit_code_ = exit_code; secure_input_helper_started_at_tick_ = 0; } if (process_handle != nullptr) { CloseHandle(process_handle); } if (stop_event_handle != nullptr) { CloseHandle(stop_event_handle); } } void CrossDeskServiceHost::StopSessionHelper() { HANDLE process_handle = nullptr; HANDLE stop_event_handle = nullptr; { std::lock_guard lock(state_mutex_); process_handle = session_helper_process_handle_; stop_event_handle = session_helper_stop_event_; session_helper_process_handle_ = nullptr; session_helper_stop_event_ = nullptr; session_helper_running_ = false; session_helper_process_id_ = 0; session_helper_started_at_tick_ = 0; ResetSessionHelperReportedStateLocked(nullptr, 0); } if (stop_event_handle != nullptr) { SetEvent(stop_event_handle); } if (process_handle != nullptr) { if (WaitForSingleObject(process_handle, 3000) == WAIT_TIMEOUT) { TerminateProcess(process_handle, ERROR_PROCESS_ABORTED); WaitForSingleObject(process_handle, 1000); } } if (process_handle != nullptr) { CloseHandle(process_handle); } if (stop_event_handle != nullptr) { CloseHandle(stop_event_handle); } } void CrossDeskServiceHost::StopSecureInputHelper() { HANDLE process_handle = nullptr; HANDLE stop_event_handle = nullptr; { std::lock_guard lock(state_mutex_); process_handle = secure_input_helper_process_handle_; stop_event_handle = secure_input_helper_stop_event_; secure_input_helper_process_handle_ = nullptr; secure_input_helper_stop_event_ = nullptr; secure_input_helper_running_ = false; secure_input_helper_process_id_ = 0; secure_input_helper_started_at_tick_ = 0; } if (stop_event_handle != nullptr) { SetEvent(stop_event_handle); } if (process_handle != nullptr) { if (WaitForSingleObject(process_handle, 3000) == WAIT_TIMEOUT) { TerminateProcess(process_handle, ERROR_PROCESS_ABORTED); WaitForSingleObject(process_handle, 1000); } } if (process_handle != nullptr) { CloseHandle(process_handle); } if (stop_event_handle != nullptr) { CloseHandle(stop_event_handle); } } bool CrossDeskServiceHost::LaunchSessionHelper(DWORD session_id) { std::wstring helper_path = GetSessionHelperPath(); if (helper_path.empty() || !std::filesystem::exists(helper_path)) { std::lock_guard lock(state_mutex_); session_helper_last_error_ = "helper_binary_missing"; session_helper_last_error_code_ = ERROR_FILE_NOT_FOUND; return false; } std::wstring stop_event_name = GetSessionHelperStopEventName(session_id); KernelObjectSecurityAttributes event_security; SECURITY_ATTRIBUTES* event_attributes = nullptr; if (event_security.Initialize()) { event_attributes = event_security.get(); } HANDLE stop_event_handle = CreateEventW(event_attributes, TRUE, FALSE, stop_event_name.c_str()); if (stop_event_handle == nullptr) { std::lock_guard lock(state_mutex_); session_helper_last_error_ = "create_helper_stop_event_failed"; session_helper_last_error_code_ = GetLastError(); return false; } std::wstring command_line = QuoteWindowsArgument(helper_path) + L" --session-helper --session-id " + std::to_wstring(session_id) + L" --stop-event " + QuoteWindowsArgument(stop_event_name); std::wstring mutable_command_line = command_line; STARTUPINFOW startup_info{}; startup_info.cb = sizeof(startup_info); startup_info.lpDesktop = const_cast(L"winsta0\\default"); PROCESS_INFORMATION process_info{}; BOOL created = FALSE; if (console_mode_ && process_session_id_ == session_id) { created = CreateProcessW(helper_path.c_str(), mutable_command_line.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &startup_info, &process_info); } else { HANDLE user_token = nullptr; HANDLE primary_token = nullptr; ScopedEnvironmentBlock environment_block; if (!WTSQueryUserToken(session_id, &user_token)) { DWORD error = GetLastError(); CloseHandle(stop_event_handle); std::lock_guard lock(state_mutex_); session_helper_last_error_ = "wts_query_user_token_failed"; session_helper_last_error_code_ = error; return false; } BOOL duplicated = DuplicateTokenEx( user_token, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID, nullptr, SecurityImpersonation, TokenPrimary, &primary_token); CloseHandle(user_token); if (!duplicated) { DWORD error = GetLastError(); CloseHandle(stop_event_handle); std::lock_guard lock(state_mutex_); session_helper_last_error_ = "duplicate_token_failed"; session_helper_last_error_code_ = error; return false; } CreateEnvironmentBlock(&environment_block.environment, primary_token, FALSE); created = CreateProcessAsUserW( primary_token, helper_path.c_str(), mutable_command_line.data(), nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, environment_block.environment, nullptr, &startup_info, &process_info); DWORD error = created ? ERROR_SUCCESS : GetLastError(); CloseHandle(primary_token); if (!created) { CloseHandle(stop_event_handle); std::lock_guard lock(state_mutex_); session_helper_last_error_ = "create_process_as_user_failed"; session_helper_last_error_code_ = error; return false; } } CloseHandle(process_info.hThread); { std::lock_guard lock(state_mutex_); session_helper_process_handle_ = process_info.hProcess; session_helper_stop_event_ = stop_event_handle; session_helper_process_id_ = process_info.dwProcessId; session_helper_session_id_ = session_id; session_helper_exit_code_ = STILL_ACTIVE; session_helper_last_error_code_ = 0; session_helper_last_error_.clear(); session_helper_running_ = true; session_helper_started_at_tick_ = GetTickCount64(); } LOG_INFO("Session helper started: session_id={}, pid={}", session_id, process_info.dwProcessId); return true; } bool CrossDeskServiceHost::LaunchSecureInputHelper(DWORD session_id) { std::wstring helper_path = GetSecureInputHelperPath(); if (helper_path.empty() || !std::filesystem::exists(helper_path)) { std::lock_guard lock(state_mutex_); secure_input_helper_last_error_ = "secure_input_helper_binary_missing"; secure_input_helper_last_error_code_ = ERROR_FILE_NOT_FOUND; return false; } std::wstring stop_event_name = GetSecureInputHelperStopEventName(session_id); KernelObjectSecurityAttributes event_security; SECURITY_ATTRIBUTES* event_attributes = nullptr; if (event_security.Initialize()) { event_attributes = event_security.get(); } HANDLE stop_event_handle = CreateEventW(event_attributes, TRUE, FALSE, stop_event_name.c_str()); if (stop_event_handle == nullptr) { std::lock_guard lock(state_mutex_); secure_input_helper_last_error_ = "create_secure_input_helper_stop_event_failed"; secure_input_helper_last_error_code_ = GetLastError(); return false; } std::wstring command_line = QuoteWindowsArgument(helper_path) + L" --secure-input-helper --session-id " + std::to_wstring(session_id) + L" --stop-event " + QuoteWindowsArgument(stop_event_name); std::wstring mutable_command_line = command_line; STARTUPINFOW startup_info{}; startup_info.cb = sizeof(startup_info); startup_info.lpDesktop = const_cast(L"winsta0\\Winlogon"); PROCESS_INFORMATION process_info{}; BOOL created = FALSE; if (console_mode_ && process_session_id_ == session_id) { created = CreateProcessW(helper_path.c_str(), mutable_command_line.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &startup_info, &process_info); } else { HANDLE primary_token = nullptr; ScopedEnvironmentBlock environment_block; DWORD error = 0; if (!CreateSessionSystemToken(session_id, &primary_token, &error)) { CloseHandle(stop_event_handle); std::lock_guard lock(state_mutex_); secure_input_helper_last_error_ = "create_session_system_token_failed"; secure_input_helper_last_error_code_ = error; return false; } CreateEnvironmentBlock(&environment_block.environment, primary_token, FALSE); created = CreateProcessAsUserW( primary_token, helper_path.c_str(), mutable_command_line.data(), nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, environment_block.environment, nullptr, &startup_info, &process_info); error = created ? ERROR_SUCCESS : GetLastError(); CloseHandle(primary_token); if (!created) { CloseHandle(stop_event_handle); std::lock_guard lock(state_mutex_); secure_input_helper_last_error_ = "create_secure_input_helper_failed"; secure_input_helper_last_error_code_ = error; return false; } } CloseHandle(process_info.hThread); { std::lock_guard lock(state_mutex_); secure_input_helper_process_handle_ = process_info.hProcess; secure_input_helper_stop_event_ = stop_event_handle; secure_input_helper_process_id_ = process_info.dwProcessId; secure_input_helper_session_id_ = session_id; secure_input_helper_exit_code_ = STILL_ACTIVE; secure_input_helper_last_error_code_ = 0; secure_input_helper_last_error_.clear(); secure_input_helper_running_ = true; secure_input_helper_started_at_tick_ = GetTickCount64(); } LOG_INFO("Secure input helper started: session_id={}, pid={}", session_id, process_info.dwProcessId); return true; } void CrossDeskServiceHost::EnsureSessionHelper() { ReapSessionHelper(); DWORD target_session_id = 0xFFFFFFFF; bool has_active_user = false; bool already_running = false; { std::lock_guard lock(state_mutex_); target_session_id = active_session_id_; has_active_user = !prelogin_; already_running = session_helper_running_ && session_helper_session_id_ == target_session_id; } if (already_running) { return; } if (target_session_id == 0xFFFFFFFF || !has_active_user) { StopSessionHelper(); std::lock_guard lock(state_mutex_); session_helper_last_error_ = target_session_id == 0xFFFFFFFF ? "no_active_console_session" : "no_active_user_session"; session_helper_last_error_code_ = 0; return; } StopSessionHelper(); LaunchSessionHelper(target_session_id); } void CrossDeskServiceHost::RefreshSessionHelperReportedState() { DWORD target_session_id = 0xFFFFFFFF; bool helper_running = false; { std::lock_guard lock(state_mutex_); target_session_id = session_helper_session_id_; helper_running = session_helper_running_; } if (!helper_running || target_session_id == 0xFFFFFFFF) { std::lock_guard lock(state_mutex_); ResetSessionHelperReportedStateLocked("helper_not_running", 0); return; } std::string response = QueryNamedPipeMessage( GetCrossDeskSessionHelperPipeName(target_session_id), kCrossDeskSessionHelperStatusCommand, 300); Json json = Json::parse(response, nullptr, false); if (json.is_discarded()) { std::lock_guard lock(state_mutex_); ResetSessionHelperReportedStateLocked("invalid_helper_status_json", 0); return; } if (!json.value("ok", false)) { std::lock_guard lock(state_mutex_); const std::string error = json.value("error", std::string("helper_status_failed")); ResetSessionHelperReportedStateLocked( error.c_str(), json.value("code", static_cast(0))); return; } std::lock_guard lock(state_mutex_); session_helper_status_ok_ = true; session_helper_status_error_.clear(); session_helper_status_error_code_ = 0; session_helper_report_session_id_ = json.value("session_id", static_cast(0xFFFFFFFF)); session_helper_report_process_id_ = json.value("process_id", 0u); session_helper_report_session_locked_ = json.value("session_locked", false); session_helper_report_input_desktop_available_ = json.value("input_desktop_available", false); session_helper_report_input_desktop_error_code_ = json.value("input_desktop_error_code", 0u); session_helper_report_input_desktop_ = json.value("input_desktop", std::string()); session_helper_report_lock_app_visible_ = json.value("lock_app_visible", false); session_helper_report_logon_ui_visible_ = json.value("logon_ui_visible", false); session_helper_report_secure_desktop_active_ = json.value("secure_desktop_active", false); session_helper_report_credential_ui_visible_ = json.value("credential_ui_visible", false); session_helper_report_unlock_ui_visible_ = json.value("unlock_ui_visible", false); session_helper_report_interactive_stage_ = json.value("interactive_stage", std::string()); session_helper_report_state_age_ms_ = json.value("state_age_ms", 0ull); session_helper_report_uptime_ms_ = json.value("uptime_ms", 0ull); } void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type, DWORD session_id) { { std::lock_guard lock(state_mutex_); last_session_event_type_ = event_type; last_session_event_session_id_ = session_id; active_session_id_ = WTSGetActiveConsoleSessionId(); DWORD process_session_id = 0xFFFFFFFF; if (ProcessIdToSessionId(GetCurrentProcessId(), &process_session_id)) { process_session_id_ = process_session_id; } logon_ui_visible_ = IsLogonUiRunningInSession(active_session_id_); InputDesktopInfo desktop_info = GetInputDesktopInfo(); input_desktop_available_ = desktop_info.available; input_desktop_error_code_ = desktop_info.error_code; input_desktop_name_ = desktop_info.name; secure_desktop_active_ = _stricmp(input_desktop_name_.c_str(), "Winlogon") == 0; std::wstring username; bool username_available = GetSessionUserName(active_session_id_, &username); prelogin_ = !username_available || username.empty(); if (!QuerySessionLockState(active_session_id_, &session_locked_)) { if (event_type == WTS_SESSION_LOCK) { session_locked_ = true; } else if (event_type == WTS_SESSION_UNLOCK || event_type == WTS_SESSION_LOGON) { session_locked_ = false; } else if (logon_ui_visible_ || secure_desktop_active_) { session_locked_ = !prelogin_; } } } LOG_INFO("Session event: type={}, session_id={}, active_session_id={}", SessionEventToString(event_type), session_id, active_session_id_); EnsureSessionHelper(); if (!secure_desktop_active_ && !logon_ui_visible_) { StopSecureInputHelper(); } } std::string CrossDeskServiceHost::HandleIpcCommand(const std::string& command) { std::string normalized = ToLower(Trim(command)); if (normalized == "ping") { return "{\"ok\":true,\"reply\":\"pong\"}"; } if (normalized == "status") { return BuildStatusResponse(); } if (normalized == "sas") { return SendSecureAttentionSequence(); } int key_code = 0; bool is_down = false; if (ParseSecureDesktopKeyboardIpcCommand(normalized, &key_code, &is_down)) { return SendSecureDesktopKeyboardInput(key_code, is_down); } return BuildErrorJson("unknown_command"); } std::string CrossDeskServiceHost::BuildStatusResponse() { ReapSecureInputHelper(); ReapSessionHelper(); RefreshSessionState(); EnsureSessionHelper(); RefreshSessionHelperReportedState(); bool keep_secure_input_helper = false; bool launch_secure_input_helper = false; DWORD secure_input_target_session_id = 0xFFFFFFFF; { std::lock_guard lock(state_mutex_); secure_input_target_session_id = active_session_id_; keep_secure_input_helper = ShouldKeepSecureInputHelperLocked(secure_input_target_session_id); launch_secure_input_helper = keep_secure_input_helper && (!secure_input_helper_running_ || secure_input_helper_session_id_ != secure_input_target_session_id); } if (keep_secure_input_helper) { if (launch_secure_input_helper) { StopSecureInputHelper(); LaunchSecureInputHelper(secure_input_target_session_id); } } else { StopSecureInputHelper(); } std::lock_guard lock(state_mutex_); std::wstring username; GetSessionUserName(active_session_id_, &username); std::string username_utf8 = EscapeJsonString(WideToUtf8(username)); std::string input_desktop = EscapeJsonString(input_desktop_name_); std::string last_sas_error = EscapeJsonString(last_sas_error_); std::string session_helper_last_error = EscapeJsonString(session_helper_last_error_); std::string session_helper_status_error = EscapeJsonString(session_helper_status_error_); std::string session_helper_path = EscapeJsonString(WideToUtf8(GetSessionHelperPath())); std::string secure_input_helper_path = EscapeJsonString(WideToUtf8(GetSecureInputHelperPath())); std::string helper_input_desktop = EscapeJsonString(session_helper_report_input_desktop_); std::string secure_input_helper_last_error = EscapeJsonString(secure_input_helper_last_error_); bool interactive_state_ready = session_helper_status_ok_; const char* interactive_state_source = interactive_state_ready ? "session-helper" : "service-host"; const bool effective_session_locked = GetEffectiveSessionLockedLocked(); const bool interactive_lock_screen_visible = interactive_state_ready ? (effective_session_locked && IsHelperReportingLockScreenLocked()) : false; bool credential_ui_visible = interactive_state_ready ? session_helper_report_credential_ui_visible_ : logon_ui_visible_; bool unlock_ui_visible = interactive_state_ready ? session_helper_report_unlock_ui_visible_ : (logon_ui_visible_ || secure_desktop_active_); bool interactive_secure_desktop_active = interactive_state_ready ? session_helper_report_secure_desktop_active_ : secure_desktop_active_; bool interactive_logon_ui_visible = interactive_state_ready ? session_helper_report_logon_ui_visible_ : logon_ui_visible_; bool interactive_session_locked = effective_session_locked || interactive_lock_screen_visible || unlock_ui_visible; std::string interactive_input_desktop = EscapeJsonString( interactive_state_ready ? session_helper_report_input_desktop_ : input_desktop_name_); std::string interactive_stage = EscapeJsonString(DetermineInteractiveStage( interactive_lock_screen_visible, credential_ui_visible, interactive_secure_desktop_active)); std::ostringstream stream; stream << "{\"ok\":true,\"service\":\"CrossDeskService\"" << ",\"active_session_id\":" << active_session_id_ << ",\"process_session_id\":" << process_session_id_ << ",\"session_locked\":" << (session_locked_ ? "true" : "false") << ",\"interactive_state_ready\":" << (interactive_state_ready ? "true" : "false") << ",\"interactive_state_source\":\"" << interactive_state_source << "\"" << ",\"interactive_session_locked\":" << (interactive_session_locked ? "true" : "false") << ",\"interactive_stage\":\"" << interactive_stage << "\"" << ",\"interactive_input_desktop\":\"" << interactive_input_desktop << "\"" << ",\"interactive_lock_screen_visible\":" << (interactive_lock_screen_visible ? "true" : "false") << ",\"interactive_logon_ui_visible\":" << (interactive_logon_ui_visible ? "true" : "false") << ",\"interactive_secure_desktop_active\":" << (interactive_secure_desktop_active ? "true" : "false") << ",\"unlock_ui_visible\":" << (unlock_ui_visible ? "true" : "false") << ",\"credential_ui_visible\":" << (credential_ui_visible ? "true" : "false") << ",\"password_box_visible\":" << (credential_ui_visible ? "true" : "false") << ",\"logon_ui_visible\":" << (logon_ui_visible_ ? "true" : "false") << ",\"secure_desktop_active\":" << (secure_desktop_active_ ? "true" : "false") << ",\"input_desktop_available\":" << (input_desktop_available_ ? "true" : "false") << ",\"input_desktop_error_code\":" << input_desktop_error_code_ << ",\"input_desktop\":\"" << input_desktop << "\"" << ",\"prelogin\":" << (prelogin_ ? "true" : "false") << ",\"session_user\":\"" << username_utf8 << "\"" << ",\"session_helper_path\":\"" << session_helper_path << "\"" << ",\"session_helper_running\":" << (session_helper_running_ ? "true" : "false") << ",\"session_helper_pid\":" << session_helper_process_id_ << ",\"session_helper_session_id\":" << session_helper_session_id_ << ",\"session_helper_exit_code\":" << session_helper_exit_code_ << ",\"session_helper_last_error\":\"" << session_helper_last_error << "\"" << ",\"session_helper_last_error_code\":" << session_helper_last_error_code_ << ",\"session_helper_status_ok\":" << (session_helper_status_ok_ ? "true" : "false") << ",\"session_helper_status_error\":\"" << session_helper_status_error << "\"" << ",\"session_helper_status_error_code\":" << session_helper_status_error_code_ << ",\"session_helper_report_session_id\":" << session_helper_report_session_id_ << ",\"session_helper_report_process_id\":" << session_helper_report_process_id_ << ",\"session_helper_report_session_locked\":" << (session_helper_report_session_locked_ ? "true" : "false") << ",\"session_helper_report_input_desktop_available\":" << (session_helper_report_input_desktop_available_ ? "true" : "false") << ",\"session_helper_report_input_desktop_error_code\":" << session_helper_report_input_desktop_error_code_ << ",\"session_helper_report_input_desktop\":\"" << helper_input_desktop << "\"" << ",\"session_helper_report_lock_app_visible\":" << (session_helper_report_lock_app_visible_ ? "true" : "false") << ",\"session_helper_report_logon_ui_visible\":" << (session_helper_report_logon_ui_visible_ ? "true" : "false") << ",\"session_helper_report_secure_desktop_active\":" << (session_helper_report_secure_desktop_active_ ? "true" : "false") << ",\"session_helper_report_credential_ui_visible\":" << (session_helper_report_credential_ui_visible_ ? "true" : "false") << ",\"session_helper_report_unlock_ui_visible\":" << (session_helper_report_unlock_ui_visible_ ? "true" : "false") << ",\"session_helper_report_interactive_stage\":\"" << EscapeJsonString(session_helper_report_interactive_stage_) << "\"" << ",\"session_helper_report_state_age_ms\":" << session_helper_report_state_age_ms_ << ",\"session_helper_report_uptime_ms\":" << session_helper_report_uptime_ms_ << ",\"session_helper_uptime_ms\":" << (session_helper_started_at_tick_ >= started_at_tick_ ? (GetTickCount64() - session_helper_started_at_tick_) : 0) << ",\"secure_input_helper_path\":\"" << secure_input_helper_path << "\"" << ",\"secure_input_helper_running\":" << (secure_input_helper_running_ ? "true" : "false") << ",\"secure_input_helper_pid\":" << secure_input_helper_process_id_ << ",\"secure_input_helper_session_id\":" << secure_input_helper_session_id_ << ",\"secure_input_helper_exit_code\":" << secure_input_helper_exit_code_ << ",\"secure_input_helper_last_error\":\"" << secure_input_helper_last_error << "\"" << ",\"secure_input_helper_last_error_code\":" << secure_input_helper_last_error_code_ << ",\"secure_input_helper_uptime_ms\":" << (secure_input_helper_started_at_tick_ >= started_at_tick_ ? (GetTickCount64() - secure_input_helper_started_at_tick_) : 0) << ",\"last_sas_success\":" << (last_sas_success_ ? "true" : "false") << ",\"last_sas_error\":\"" << last_sas_error << "\"" << ",\"last_sas_error_code\":" << last_sas_error_code_ << ",\"last_sas_uptime_ms\":" << (last_sas_tick_ >= started_at_tick_ ? (last_sas_tick_ - started_at_tick_) : 0) << ",\"last_session_event\":\"" << SessionEventToString(last_session_event_type_) << "\"" << ",\"last_session_event_id\":" << last_session_event_type_ << ",\"last_session_id\":" << last_session_event_session_id_ << ",\"uptime_ms\":" << (GetTickCount64() >= started_at_tick_ ? (GetTickCount64() - started_at_tick_) : 0) << "}"; return stream.str(); } std::string CrossDeskServiceHost::SendSecureAttentionSequence() { RefreshSessionState(); LOG_INFO("Received SAS request for session_id={}", active_session_id_); SasResult result = SendSasNow(); { std::lock_guard lock(state_mutex_); last_sas_tick_ = GetTickCount64(); last_sas_success_ = result.success; last_sas_error_code_ = result.error_code; last_sas_error_ = result.error; } if (!result.success) { return BuildErrorJson(result.error.c_str(), result.error_code); } return "{\"ok\":true,\"sent\":\"sas\"}"; } std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(int key_code, bool is_down) { RefreshSessionState(); ReapSecureInputHelper(); EnsureSessionHelper(); DWORD target_session_id = 0xFFFFFFFF; bool helper_running = false; bool can_inject = false; { std::lock_guard lock(state_mutex_); target_session_id = active_session_id_; helper_running = secure_input_helper_running_ && secure_input_helper_session_id_ == target_session_id; can_inject = GetEffectiveSessionLockedLocked() || HasSecureInputUiLocked(); } if (target_session_id == 0xFFFFFFFF) { return BuildErrorJson("no_active_console_session"); } if (!can_inject) { return BuildErrorJson("secure_input_not_active"); } if (!helper_running) { StopSecureInputHelper(); if (!LaunchSecureInputHelper(target_session_id)) { std::lock_guard lock(state_mutex_); return BuildErrorJson(secure_input_helper_last_error_.c_str(), secure_input_helper_last_error_code_); } } return QueryNamedPipeMessage( GetCrossDeskSecureInputHelperPipeName(target_session_id), BuildSecureInputHelperKeyboardCommand(key_code, is_down), 1000); } bool InstallCrossDeskService(const std::wstring& binary_path) { SC_HANDLE manager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); if (manager == nullptr) { LOG_ERROR("OpenSCManagerW failed, error={}", GetLastError()); return false; } std::wstring service_command = L"\"" + binary_path + L"\" --service"; SC_HANDLE service = CreateServiceW( manager, kCrossDeskServiceName, kCrossDeskServiceDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service_command.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr); if (service == nullptr) { DWORD error = GetLastError(); if (error != ERROR_SERVICE_EXISTS) { LOG_ERROR("CreateServiceW failed, error={}", error); CloseServiceHandle(manager); return false; } service = OpenServiceW(manager, kCrossDeskServiceName, SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS); if (service == nullptr) { LOG_ERROR("OpenServiceW failed, error={}", GetLastError()); CloseServiceHandle(manager); return false; } if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE, service_command.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr, kCrossDeskServiceDisplayName)) { LOG_ERROR("ChangeServiceConfigW failed, error={}", GetLastError()); CloseServiceHandle(service); CloseServiceHandle(manager); return false; } } CloseServiceHandle(service); CloseServiceHandle(manager); return true; } bool StartCrossDeskService() { SC_HANDLE manager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (manager == nullptr) { LOG_ERROR("OpenSCManagerW failed, error={}", GetLastError()); return false; } SC_HANDLE service = OpenServiceW(manager, kCrossDeskServiceName, SERVICE_START); if (service == nullptr) { LOG_ERROR("OpenServiceW failed, error={}", GetLastError()); CloseServiceHandle(manager); return false; } bool success = StartServiceW(service, 0, nullptr) != FALSE; DWORD error = success ? ERROR_SUCCESS : GetLastError(); if (!success && error != ERROR_SERVICE_ALREADY_RUNNING) { LOG_ERROR("StartServiceW failed, error={}", error); } CloseServiceHandle(service); CloseServiceHandle(manager); return success || error == ERROR_SERVICE_ALREADY_RUNNING; } bool StopCrossDeskService(DWORD timeout_ms) { SC_HANDLE manager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (manager == nullptr) { LOG_ERROR("OpenSCManagerW failed, error={}", GetLastError()); return false; } SC_HANDLE service = OpenServiceW(manager, kCrossDeskServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS); if (service == nullptr) { DWORD error = GetLastError(); CloseServiceHandle(manager); if (error == ERROR_SERVICE_DOES_NOT_EXIST) { return true; } LOG_ERROR("OpenServiceW failed, error={}", error); return false; } SERVICE_STATUS_PROCESS status{}; DWORD bytes_needed = 0; QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, reinterpret_cast(&status), sizeof(status), &bytes_needed); if (status.dwCurrentState != SERVICE_STOPPED && status.dwCurrentState != SERVICE_STOP_PENDING) { SERVICE_STATUS service_status{}; if (!ControlService(service, SERVICE_CONTROL_STOP, &service_status)) { DWORD error = GetLastError(); if (error != ERROR_SERVICE_NOT_ACTIVE) { LOG_ERROR("ControlService failed, error={}", error); CloseServiceHandle(service); CloseServiceHandle(manager); return false; } } } DWORD deadline = GetTickCount() + timeout_ms; while (GetTickCount() < deadline) { if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, reinterpret_cast(&status), sizeof(status), &bytes_needed)) { LOG_ERROR("QueryServiceStatusEx failed, error={}", GetLastError()); CloseServiceHandle(service); CloseServiceHandle(manager); return false; } if (status.dwCurrentState == SERVICE_STOPPED) { CloseServiceHandle(service); CloseServiceHandle(manager); return true; } Sleep(200); } CloseServiceHandle(service); CloseServiceHandle(manager); return false; } bool UninstallCrossDeskService() { SC_HANDLE manager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); if (manager == nullptr) { LOG_ERROR("OpenSCManagerW failed, error={}", GetLastError()); return false; } SC_HANDLE service = OpenServiceW(manager, kCrossDeskServiceName, DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); if (service == nullptr) { DWORD error = GetLastError(); CloseServiceHandle(manager); if (error == ERROR_SERVICE_DOES_NOT_EXIST) { return true; } LOG_ERROR("OpenServiceW failed, error={}", error); return false; } StopCrossDeskService(); bool success = DeleteService(service) != FALSE; if (!success) { LOG_ERROR("DeleteService failed, error={}", GetLastError()); } CloseServiceHandle(service); CloseServiceHandle(manager); return success; } std::string QueryCrossDeskService(const std::string& command, DWORD timeout_ms) { return QueryNamedPipeMessage(kCrossDeskServicePipeName, command, timeout_ms); } std::string SendCrossDeskSecureDesktopKeyInput(int key_code, bool is_down, DWORD timeout_ms) { return QueryCrossDeskService( BuildSecureDesktopKeyboardIpcCommand(key_code, is_down), timeout_ms); } std::string SendCrossDeskSecureDesktopMouseInput(int x, int y, int wheel, int flag, DWORD timeout_ms) { return QueryCrossDeskService( BuildSecureDesktopMouseIpcCommand(x, y, wheel, flag), timeout_ms); } } // namespace crossdesk