VC++ 服务守护qt用户级UI进程

发布于:2025-06-21 ⋅ 阅读:(14) ⋅ 点赞:(0)

适合用于本地服务程序(以 WinMainServiceMain 模式运行)

一、示例 

#include <windows.h>
#include <userenv.h>
#include <wtsapi32.h>
#include <tlhelp32.h>
#include <string>
#include <vector>
#include <thread>

#pragma comment(lib, "Wtsapi32.lib")
#pragma comment(lib, "Userenv.lib")

bool IsProcessRunning(const std::wstring& exeName) {
    PROCESSENTRY32 pe = { sizeof(pe) };
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return false;
    bool found = false;
    if (Process32First(snap, &pe)) {
        do {
            if (_wcsicmp(pe.szExeFile, (exeName + L".exe").c_str()) == 0) {
                found = true; break;
            }
        } while (Process32Next(snap, &pe));
    }
    CloseHandle(snap);
    return found;
}

void LaunchAsActiveUser(const std::wstring& fullPath) {
    DWORD sessionId = WTSGetActiveConsoleSessionId();
    HANDLE userToken = NULL, primaryToken = NULL;
    if (!WTSQueryUserToken(sessionId, &userToken)) return;
    if (!DuplicateTokenEx(userToken, MAXIMUM_ALLOWED, NULL,
        SecurityImpersonation, TokenPrimary, &primaryToken)) return;

    LPVOID env = nullptr;
    CreateEnvironmentBlock(&env, primaryToken, FALSE);

    STARTUPINFOW si = { sizeof(si) };
    si.lpDesktop = const_cast<LPWSTR>(L"winsta0\\default");
    PROCESS_INFORMATION pi = {};

    CreateProcessAsUserW(primaryToken, NULL, const_cast<LPWSTR>(fullPath.c_str()),
        NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT,
        env, NULL, &si, &pi);

    if (pi.hProcess) CloseHandle(pi.hProcess);
    if (pi.hThread) CloseHandle(pi.hThread);
    if (env) DestroyEnvironmentBlock(env);
    CloseHandle(userToken);
    CloseHandle(primaryToken);
}

void ProtectUserProcesses(const std::vector<std::wstring>& names, const std::wstring& folder) {
    std::thread([=]() {
        while (true) {
            for (auto& name : names) {
                if (!IsProcessRunning(name)) {
                    OSVERSIONINFO vi = { sizeof(vi) };
                    GetVersionEx(&vi);
                    std::wstring exePath = folder + L"\\" + name + L".exe";
                    if (vi.dwMajorVersion <= 5) {
                       LaunchProcessAsCurrentUserXP(exePath,folder)
                    } else {
                        LaunchAsActiveUser(exePath);
                    }
                }
            }
            Sleep(5000);
        }
    }).detach();
}


bool LaunchProcessAsCurrentUserXP(const std::wstring& applicationPath, const std::wstring& workingDirectory)
{
    HANDLE hToken = NULL;
    HANDLE hTokenDup = NULL;
    bool success = false;

    // 获取当前活动 Session ID
    DWORD sessionId = WTSGetActiveConsoleSessionId();

    // 遍历进程,寻找 explorer.exe(登录用户拥有的进程)
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
        return false;

    PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
    if (Process32First(hSnapshot, &pe32)) {
        do {
            if (_wcsicmp(pe32.szExeFile, L"explorer.exe") == 0) {
                // 打开 explorer.exe 进程
                HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
                if (hProcess) {
                    if (OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
                        CloseHandle(hProcess);
                        break; // 找到 token 就退出
                    }
                    CloseHandle(hProcess);
                }
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);

    if (!hToken)
        return false;

    // 复制 token
    if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &hTokenDup)) {
        CloseHandle(hToken);
        return false;
    }

    // 启动信息
    STARTUPINFO si = { sizeof(STARTUPINFO) };
    PROCESS_INFORMATION pi = { 0 };
    si.lpDesktop = const_cast<LPWSTR>(L"winsta0\\default");

    // 创建进程(使用用户权限)
    success = CreateProcessAsUserW(
        hTokenDup,
        applicationPath.c_str(),
        NULL,
        NULL,
        NULL,
        FALSE,
        CREATE_NEW_CONSOLE,
        NULL,
        workingDirectory.c_str(),
        &si,
        &pi
    ) == TRUE;

    if (success) {
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    CloseHandle(hToken);
    CloseHandle(hTokenDup);
    return success;
}

二、使用

// C++
ProtectUserProcesses({ L"MyTrayApp", L"UserAgent" }, L"C:\\Program Files\\MyApp"); 

三、注意

在 Windows XP SP3 环境中,当通过 ShellExecute 从服务进程启动新进程时,该进程会继承服务进程的 SYSTEM 权限。由于大多数 UI 进程需要用户级权限,就需要降权处理。然而 Windows XP 不支持 WTSQueryUserToken 函数(该功能仅适用于 Windows Server 2003 及更高版本),因此解决方案是获取 explorer.exe 的 Token 来启动 UI 进程


网站公告

今日签到

点亮在社区的每一天
去签到