当前位置: 首页 > news >正文

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

适合用于本地服务程序(以 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 IDDWORD 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;// 复制 tokenif (!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 进程

相关文章:

  • QEMU学习之路(10)— RISCV64 virt 使用Ubuntu启动
  • c++set和pair的使用
  • 小白的进阶之路系列之十六----人工智能从初步到精通pytorch综合运用的讲解第九部分
  • docker mysql启动后时间慢8小时问题
  • 24. 开发者常用工具:抓包,弱网模拟,元素检查
  • Tkinter快速入门指南
  • DataWhale-零基础络网爬虫技术(二er数据的解析与提取)
  • 粗浅理解:为什么左旋右旋的组合反而收旋转矩阵影响
  • ajax中get和post的区别
  • 5.基于神经网络的时间序列预测
  • Git 命令全景图:从 clone 到 merge 的完整流程解析
  • 【时时三省】(C语言基础)善于利用指针
  • 统计一个区间内的素数并求和
  • 3D Gaussian Splatting算法安装与实测
  • android 渲染流水线中的两个重要阶段:swapBuffers 和 DrawFrames
  • 大模型Text2SQL之在CentOS上使用yum安装与使用MySQL
  • 【Golang学习】1-基于mysql增删改查
  • 工具:Autosar:DBC转ARXML
  • 《Go语言圣经》利用结构体和接口实现更优雅的Go错误处理
  • Linux之线程同步与互斥
  • 税务局网站模板/百度400电话
  • wordpress php7 速度/扬州网络优化推广
  • 西安东郊网站建设公司/百度登录
  • 传媒有限公司/百度seo快速见效方法
  • 开家网站建设培训学校/网络营销策划与推广
  • 做电商网站的上海公司/搜索引擎市场份额2023