You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows 10远程控制安全桌面(锁屏、UAC)失败问题求助

解决Windows安全桌面(锁屏/UAC)键鼠控制问题

针对你开发远程键鼠控制程序时遇到的安全桌面访问限制问题,结合Windows安全机制和你的测试情况,给出以下具体解决方案:

核心问题分析

Windows安全桌面(锁屏、UAC弹窗所在的WinSta0\Winlogon桌面)属于高权限隔离环境,普通进程/管理员进程默认无法直接访问,你的代码存在以下关键问题:

  1. 错误指定了桌面名称(用了default而非Winlogon)
  2. 权限申请不足,未获取窗口站和桌面的必要访问权限
  3. 线程关联桌面的方式错误(原线程已绑定普通桌面,导致ERROR_BUSY)
  4. uiAccess=true的程序未满足系统要求的生效条件

具体解决方案

1. 正确打开安全桌面并关联线程

安全桌面的窗口站是WinSta0,桌面名称是Winlogon,必须先打开窗口站再打开桌面,且需在新线程中执行关联操作(避免原线程桌面占用导致ERROR_BUSY):

#include <windows.h>
#include <wtsapi32.h>
#pragma comment(lib, "wtsapi32.lib")

HDESK OpenSecureDesktop() {
    // 打开WinSta0窗口站,申请必要权限
    HWINSTA hWinSta = OpenWindowStation(L"WinSta0", FALSE, 
        WINSTA_ACCESSCLIPBOARD | WINSTA_ENUMDESKTOPS | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES);
    if (!hWinSta) {
        printf("OpenWindowStation failed: %lu\n", GetLastError());
        return NULL;
    }

    // 设置进程窗口站为WinSta0
    if (!SetProcessWindowStation(hWinSta)) {
        printf("SetProcessWindowStation failed: %lu\n", GetLastError());
        CloseWindowStation(hWinSta);
        return NULL;
    }

    // 打开Winlogon安全桌面,申请键鼠操作所需权限
    HDESK hDesk = OpenDesktop(L"Winlogon", 0, FALSE, 
        DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_HOOKCONTROL);
    if (!hDesk) {
        printf("OpenDesktop failed: %lu\n", GetLastError());
        CloseWindowStation(hWinSta);
        return NULL;
    }

    CloseWindowStation(hWinSta);
    return hDesk;
}

// 新线程中执行安全桌面控制
DWORD WINAPI SecureDesktopControlThread(LPVOID lpParam) {
    HDESK hDesk = OpenSecureDesktop();
    if (!hDesk) return 1;

    // 关联线程到安全桌面
    if (!SetThreadDesktop(hDesk)) {
        printf("SetThreadDesktop failed: %lu\n", GetLastError());
        CloseDesktop(hDesk);
        return 1;
    }

    // 执行键鼠操作示例:移动光标+模拟回车
    POINT pt;
    if (GetCursorPos(&pt)) {
        SetCursorPos(pt.x - 20, pt.y - 20);
    }

    INPUT input = {0};
    input.type = INPUT_KEYBOARD;
    input.ki.wVk = VK_RETURN;
    SendInput(1, &input, sizeof(INPUT));

    CloseDesktop(hDesk);
    return 0;
}

// 调用方式
// CreateThread(NULL, 0, SecureDesktopControlThread, NULL, 0, NULL);

2. 正确配置uiAccess=true程序

uiAccess权限需要满足三个硬性条件,缺一不可:

  • 程序必须放在受保护路径:C:\Program Files或C:\Program Files (x86)
  • 必须用EV代码签名证书签名(自签名/普通代码签名无效,企业内部CA需导入到系统受信任根证书)
  • 清单文件正确配置:
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level="asInvoker" uiAccess="true"/>
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly>

3. 修复CreateProcessAsUser调用

启动子进程到安全桌面时,需指定正确的桌面名称,并调整令牌权限:

BOOL CreateProcessOnSecureDesktop() {
    DWORD sessionId = WTSGetActiveConsoleSessionId();
    HANDLE hToken = NULL;
    if (!WTSQueryUserToken(sessionId, &hToken)) {
        printf("WTSQueryUserToken failed: %lu\n", GetLastError());
        return FALSE;
    }

    // 启用CreateProcessAsUser所需权限
    LUID luidAssign, luidQuota;
    if (!LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &luidAssign) ||
        !LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &luidQuota)) {
        printf("LookupPrivilegeValue failed: %lu\n", GetLastError());
        CloseHandle(hToken);
        return FALSE;
    }

    TOKEN_PRIVILEGES tp = {0};
    tp.PrivilegeCount = 2;
    tp.Privileges[0].Luid = luidAssign;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    tp.Privileges[1].Luid = luidQuota;
    tp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;

    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
        printf("AdjustTokenPrivileges failed: %lu\n", GetLastError());
        CloseHandle(hToken);
        return FALSE;
    }

    STARTUPINFO si = {sizeof(si)};
    si.lpDesktop = L"WinSta0\\Winlogon"; // 指定安全桌面
    PROCESS_INFORMATION pi = {0};

    wchar_t appPath[MAX_PATH];
    wcscpy_s(appPath, L"C:\\Program Files\\YourApp\\client.exe"); // 必须在受保护路径

    if (!CreateProcessAsUser(hToken, appPath, NULL, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
        printf("CreateProcessAsUser failed: %lu\n", GetLastError());
        CloseHandle(hToken);
        return FALSE;
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    CloseHandle(hToken);
    return TRUE;
}

解决方案总结:

  • 安全桌面是WinSta0\Winlogon,必须明确指定打开
  • 所有桌面关联操作必须在新线程中执行
  • uiAccess程序严格满足路径、签名、清单要求
  • 权限调整是核心,窗口站、桌面、令牌的权限必须申请到位

内容的提问来源于stack exchange,提问作者Erz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 22:20:12