赞
踩
因为工作需要,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, ¤t_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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。