windows服务程序创建具有管理员权限的界面程序,并且界面进程属于当前登录用户

因为工作需要,windows服务进程需要能够创建出带界面的进程,并且此界面进程需要管理员权限,之前找到两种方法解决这个问题。

1.服务进程里面复用winlogon.exe的令牌,并且加入窗口站,用CreateProcessAsUser创建界面进程。
winlogon.exe是个很神奇的进程,一方面其session id不为0(服务用户所在session id为0),并且跟登录用 户属于同一个session。这样利用其进程token,并且加入窗口站,就能创建出具有管理员权限的界面程序,但是由于winlogon.exe是属于SYSTEM用户的,所以利用其token创建出的界面进程也是属于SYSTEM用户的。
下面是大致的代码,其中dwWinlogon是winlogon.exe进程的id。

			dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
			si.cb = sizeof(STARTUPINFO);
			si.lpDesktop = "winsta0\\default";///加入窗口站
			hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwWinlogon);
			bResult = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
				| TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID
				| TOKEN_READ | TOKEN_WRITE, &hPToken);
			if (FALSE == bResult)
			{
				dwErrorCode = ::GetLastError();
				LOG_DEBUG("WTSQueryUserToken failed, dwErrorCode is %u", dwErrorCode);
				break;
			}

			// 创建一个新的访问令牌来复制一个已经存在的标记
			bResult = DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, NULL,
				SecurityIdentification, TokenPrimary, &hUserTokenDup);
			if (FALSE == bResult)
			{
				dwErrorCode = ::GetLastError();
				LOG_DEBUG("DuplicateTokenEx failed, dwErrorCode is %u", dwErrorCode);
				break;
			}

			// 创建环境信息
			LPVOID pEnv = NULL;
			bResult = CreateEnvironmentBlock(&pEnv, hUserTokenDup, TRUE);
			dwErrorCode = ::GetLastError();
			if (bResult)
			{
				dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
			}
			else
			{
				pEnv = NULL;
			}

			CString command = L"\"" + processPath_ + L"\"";
			if (arguments_.GetLength() != 0)
			{
				command += L" " + arguments_;
			}
			// 通过winlogon创建一个进程
			bResult = CreateProcessAsUser(
				hUserTokenDup,   // 令牌
				NULL,    // 程序全路径
				(LPSTR)(LPCSTR)(command),    // 程序命令行(和前者二选一)
				NULL,            // 进程安全属性
				NULL,            // 线程安全属性
				FALSE,           // 句柄不可继承
				dwCreationFlags, // 创建标识
				pEnv,            // 环境信息
				NULL,            // 当前路径
				&si,             // 新进程的主窗口特性
				&pi              // 新创建的进程相关信息
				);

2.以当前服务进程的token为基准,创建界面程序
用当前服务进程的token,加入窗口站,用CreateProcessAsUser创建,结果出现了交互式服务检测弹框,读者可以看我的另外一篇博客,代码层面剖析交互式服务检测由来

此时修改token的session为当前登录用户的session id即可。代码大概如下所示:

	DWORD dwSessionId = WTSGetActiveConsoleSessionId();
	BOOL bRet = SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD));

上面两种创建出的界面进程都是SYSTEM用户下,不是当前登录用户下的,能否创建当前登录用户下,具有管理员权限的界面进程呢,答案是肯定的。

UAC开启时,当前用户拥有两个token,分别是受限的token和不受限的token。explorer.exe进程的token就属于受限的token。
在服务程序中,可以用下面代码获取到受限的token。

HANDLE GetCurrentUserToken()
{
    PWTS_SESSION_INFO pSessionInfo = 0;
    DWORD dwCount = 0;
    ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
    int session_id = 0;
    for (DWORD i = 0; i < dwCount; ++i)
    {
        WTS_SESSION_INFO si = pSessionInfo[i];
        if (WTSActive == si.State)
        {
            session_id = si.SessionId;
            break;
        }
    }
    ::WTSFreeMemory(pSessionInfo);
    HANDLE current_token = 0;
    BOOL bRet = ::WTSQueryUserToken(session_id, &current_token);
    int errorcode = GetLastError();
    if (bRet == FALSE)
    {
		LOG_ERROR("WTSQueryUserToken  errorcode: %d ", errorcode);
        return 0;
    }
    HANDLE primaryToken = 0;
    bRet = ::DuplicateTokenEx(current_token, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &primaryToken);
    errorcode = GetLastError();
	::CloseHandle(current_token);
    if (bRet == FALSE)
    {
		LOG_ERROR("DuplicateTokenEx  errorcode: %d ", errorcode);
        return 0;
    }

    return primaryToken;
}

然后由此token可以得到不受限的token,代码如下所示。

	HANDLE primaryToken = GetCurrentUserToken();
	HANDLE hUnfilteredToken = NULL;
	DWORD dwSize = 0;
	BOOL bRet = GetTokenInformation(primaryToken, TokenLinkedToken,(VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);

获取到的hUnfilteredToken就是不受限的token,以token作为CreateProcessAsUser的第一个参数,就可以创建出具有管理员权限,并且属于当前用户的界面程序了,并且这种情况下,不需要加入窗口站。
相关代码如下:

BOOL RunAdminPrivilege()  
{
    HANDLE primaryToken = GetCurrentUserToken();
    if (primaryToken == 0)
    {
		LOG_ERROR("GetCurrentUserToken  fail : ");
        return FALSE;
    }
	HANDLE hUnfilteredToken = NULL;
	DWORD dwSize = 0;
	BOOL bRet = GetTokenInformation(primaryToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);

	STARTUPINFO StartupInfo = {0};
    PROCESS_INFORMATION processInfo;
    StartupInfo.cb = sizeof(STARTUPINFO);

    CString command = L"\"" + processPath_ + L"\"";
    if (arguments_.GetLength() != 0)
    {
        command += L" " + arguments_;
    }
    void* lpEnvironment = NULL;
    BOOL resultEnv = ::CreateEnvironmentBlock(&lpEnvironment, hUnfilteredToken, FALSE);
    if (resultEnv == 0)
    {                                
        long nError = GetLastError();    
		LOG_ERROR("CreateEnvironmentBlock  errorcode: %d ", nError);
    }
    LOG_DEBUG("CreateProcessAsUser  command: %s ", command);

    BOOL result = ::CreateProcessAsUser(hUnfilteredToken, 0, (LPSTR)(LPCSTR)(command), NULL, NULL, FALSE, CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, 0, &StartupInfo, &processInfo);

	if(!result)
	{
		 long nError = GetLastError();   
		 LOG_DEBUG("CreateProcessAsUser error : %d ", nError);   
	}

	if(lpEnvironment != NULL)
	{
      ::DestroyEnvironmentBlock(lpEnvironment);
	}
    ::CloseHandle(primaryToken);

	LOG_DEBUG("CreateProcessAsUser  result: %d ", result);

    return result;
}

猜你喜欢

转载自blog.csdn.net/tusong86/article/details/106556988