windows服务启动一个当前用户的进程

windows服务启动一个当前用户的进程

首先,为什么会使用windows服务?
大多数Windows服务是以SYSTEM用户启动的。SYSTEM用户是系统中权限最高的用户,可以操作注册表中Local Machine、系统目录,不需要UAC就能以管理员权限启动一个进程等等。
在windows中,每一个用户会有一个Session ,Session 0专用于服务和其他不与用户交互的应用程序。
第一个登录进来,可以进行交互式操作的用户被连到Session 1上。第二个登录进行的用户被分配给Session 2,以此类推。
因为Service在Session 0中,而我们登陆系统以后看到的桌面属于另一个Session,如果采取在服务进程中启动子进程来显示对话框,子对话框将无法在我们这个session中显示。
PS:
如果Service 中想显示MessageBox,需要使用API :WTSSendMessage

言归正传,那么如何在Service为当前用户登陆的session启动一个进程呢?

我们可以使用API:CreateProcessAsUser
但是使用这个API需要一系列的准备
比如复制当前激活session 用户的Token,创建环境变量等等。


BOOL MyCreateProcessAsUser(const HANDLE& hCurrentToken,LPCTSTR strPath,LPTSTR lpCmdLine)
{
	HANDLE hTokenDup = NULL;
	if (!DuplicateTokenEx(hCurrentToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification,
		TokenPrimary, &hTokenDup))
	{
#ifdef _DEBUG
		int iError = GetLastError();
		CString strLog;
		strLog.Format(_T("DuplicateTokenEx fail,Error code:%d"), iError);
		OutputDebugString(strLog);
#endif
		return FALSE;
	}
	DWORD dwSessionID = WTSGetActiveConsoleSessionId();

	if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionID, sizeof(DWORD)))
	{
		SafeCloseHandle(hTokenDup);
		return FALSE;
	}
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	si.cb = sizeof(STARTUPINFO);
	si.lpDesktop = L"WinSta0\\Default";
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = HIDE_WINDOW;

	LPVOID pEnv = NULL;
	DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
	if (!CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE))
	{
		SafeCloseHandle(hTokenDup);
		return FALSE;
	}

	if (!CreateProcessAsUser(hTokenDup, strPath, lpCmdLine, NULL, NULL, FALSE, dwCreationFlags, pEnv, NULL, &si, &pi))
	{

		SafeCloseHandle(hTokenDup);
		if (pEnv != NULL)
			DestroyEnvironmentBlock(pEnv);
		return FALSE;
	}

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	CloseHandle(hTokenDup);
	if (pEnv != NULL)
		DestroyEnvironmentBlock(pEnv);
	return TRUE;


}
这样就可以在service中启动一个当前用户 权限的进程了。

另一个问题来了,如何获取当前激活session 用户的Token?
WTSGetActiveConsoleSessionId();在这种情况下通常是失败的。
一种常见的方法就是 打开explorer.exe进程,取它的Token ...没办法,谁让我service权限高呢,咩哈哈哈

BOOL GetCurrentLogonUserToken(HANDLE& hToken)
{
	DWORD dwCurSessionId = WTSGetActiveConsoleSessionId();
	if (TRUE == WTSQueryUserToken(dwCurSessionId, &hToken))
		return TRUE;
	DWORD err = GetLastError();
	BOOL bRet = FALSE;
	HANDLE hProcessSnap = NULL;
	PROCESSENTRY32 pe32;
	DWORD dwSessionId = -1;

	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}

	pe32.dwSize = sizeof(PROCESSENTRY32);
	if (Process32First(hProcessSnap, &pe32))
	{
		do
		{
			if (!_tcsicmp(pe32.szExeFile, _T("explorer.exe")))
			{
				::ProcessIdToSessionId(pe32.th32ProcessID, &dwSessionId);
				if (dwSessionId != dwCurSessionId)
					continue;
				//{
				HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
				DWORD err = GetLastError();
				bRet = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
				if (bRet == 0)
				{
				}

				CloseHandle(hProcessSnap);
				return TRUE;
				//}	 
			}

		} while (Process32Next(hProcessSnap, &pe32));

		bRet = TRUE;
	}
	else
	{
		bRet = FALSE;
	}
	CloseHandle(hProcessSnap);

	return bRet;
}


以上就是全文了~



  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
在C++ Windows服务中,使用CreateProcessAsUser函数启动一个进程,并指定Local System权限,可以通过以下步骤实现: 1. 获取Local System用户的访问令牌。使用OpenProcessToken函数获取当前进程的访问令牌,然后使用DuplicateTokenEx函数创建一个访问令牌的副本。在DuplicateTokenEx函数中指定TokenPrimary参数,以便创建一个主访问令牌,这样后续可以使用该访问令牌来启动进程。 ```c++ HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { // 获取当前进程的访问令牌失败 // TODO:处理错误 return; } HANDLE hTokenDup = NULL; if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hTokenDup)) { // 创建访问令牌的副本失败 // TODO:处理错误 CloseHandle(hToken); return; } CloseHandle(hToken); ``` 2. 设置访问令牌的安全属性。使用SetTokenInformation函数设置TOKEN_MANDATORY_LABEL信息,以便指定访问令牌的安全属性。 ```c++ // 设置SDDL字符串,表示为Local System用户创建一个低完整性级别的安全标签 LPCTSTR szSDDL = _T("S-1-16-4096"); PSECURITY_DESCRIPTOR pSD = NULL; if (!ConvertStringSecurityDescriptorToSecurityDescriptor(szSDDL, SDDL_REVISION_1, &pSD, NULL)) { // 转换SDDL字符串为安全描述符失败 // TODO:处理错误 CloseHandle(hTokenDup); return; } TOKEN_MANDATORY_LABEL tml = { 0 }; tml.Label.Attributes = SE_GROUP_INTEGRITY; tml.Label.Sid = NULL; if (!ConvertStringSidToSid(szSDDL, &(tml.Label.Sid))) { // 转换SDDL字符串为SID失败 // TODO:处理错误 CloseHandle(hTokenDup); LocalFree(pSD); return; } if (!SetTokenInformation(hTokenDup, TokenIntegrityLevel, &tml, sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(tml.Label.Sid))) { // 设置访问令牌的安全属性失败 // TODO:处理错误 CloseHandle(hTokenDup); LocalFree(pSD); return; } LocalFree(pSD); ``` 3. 使用CreateProcessAsUser函数创建一个进程,并使用访问令牌的副本来启动进程。在CreateProcessAsUser函数中指定CREATE_NEW_CONSOLE标志,以便在新控制台窗口中启动进程。 ```c++ STARTUPINFO si = { 0 }; si.cb = sizeof(si); si.lpDesktop = _T("winsta0\\default"); PROCESS_INFORMATION pi = { 0 }; if (!CreateProcessAsUser(hTokenDup, NULL, _T("cmd.exe"), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { // 使用访问令牌启动进程失败 // TODO:处理错误 CloseHandle(hTokenDup); return; } ``` 4. 关闭访问令牌的副本和新进程的句柄。 ```c++ CloseHandle(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(hTokenDup); ``` 希望这些步骤能够帮助您在C++ Windows服务中使用CreateProcessAsUser函数启动一个进程,并指定Local System权限。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值