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;
}


以上就是全文了~



猜你喜欢

转载自blog.csdn.net/linlin003/article/details/77089765