概述:降权操作,Windows服务进程中以低权限创建进程
[toc]
背景说明
在服务程序中调用了某一个程序的安装程序,由于权限的问题,这个安装程序也继承了服务的 SYSTEM
权限,导致安装程序与预期不符合。
解决方案
最终实现的目标就是在服务中以普通用户的权限去启动安装程序,要以普通用户去启动,就涉及到降权的问题,需要获取用户的信息。在任务管理器详细信息中可以看到,资源管理器是以普通用户的身份启动的,因此可以在服务中以 Explore.exe
的权限去调用安装程序。
实现逻辑
此逻辑也可以用于解决 UAC
弹窗的问题。
- 获取token
- 通过token获取用户的会话ID
- 通过token和ID启动进程
代码
接口
最终调用的接口为: CreateProcessAsUser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int ExecutePackage(LPCSTR pszPath, LPCSTR pszParams) { if (nullptr == pszPath) return -1;
wstring szPath = stringToWstring(pszPath);
wstring szParams; if (pszParams) { szParams = stringToWstring(pszParams); }
CreateProcessWithAdmin(szPath.c_str(), szParams.c_str()); return 0; }
|
实现
实现如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
bool CreateProcessWithAdmin(LPCWSTR lpExePath, LPCWSTR lpParam) { HANDLE hExplorerToken = GetExplorerToken(); HANDLE hTokenDup = NULL; LPVOID pEnvironment = nullptr; bool res{ false };
char szErr[256] = { 0 }; int iErrCode = 0; do { if (hExplorerToken == NULL) { iErrCode = GetLastError(); break; } DWORD dwReturnBytes = 0; DWORD dwReturnLen = 0; DWORD dwTokenSessionId = 0; if (::GetTokenInformation(hExplorerToken, TokenSessionId, &dwTokenSessionId, sizeof(DWORD), &dwReturnLen) == FALSE) { break; }
- res = _CreateProcessAsSystemBySession(lpExePath, lpParam, NULL, dwTokenSessionId, hExplorerToken); + HANDLE hNewToken = NULL; + if (GetElevatedToken(hExplorerToken, &hNewToken) && hNewToken) + { + res = CreateProcessByToken(hNewToken, hExplorerToken, lpExePath, lpParam, NULL, FALSE); + CloseHandle(hNewToken); + } + else + { + res = CreateProcessAsSystemBySession(lpExePath, lpParam, NULL, dwTokenSessionId, hExplorerToken); + } + CloseHandle(hExplorerToken); } while (false);
return res; }
|
判断token是否被提升:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| BOOL IsElevatedToken(HANDLE hToken, PBOOL pbElevated) { DWORD dwReturnBytes = 0; DWORD dwElevateionType = 0; BOOL bElevated = FALSE;
if (hToken && pbElevated) { if (GetTokenInformation(hToken, TokenElevationType, &dwElevateionType, sizeof(dwElevateionType), &dwReturnBytes)) { if (dwElevateionType == TokenElevationTypeFull) bElevated = TRUE; else if (dwElevateionType == TokenElevationTypeDefault) { TOKEN_ELEVATION te; ZeroMemory(&te, sizeof(te));
if (GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnBytes)) { if (te.TokenIsElevated) bElevated = TRUE; } } }
if (pbElevated) *pbElevated = bElevated;
return TRUE; }
return FALSE; }
BOOL GetElevatedToken(HANDLE hToken, PHANDLE phNewToken) { BOOL bElevated = FALSE;
IsElevatedToken(hToken, &bElevated);
if (bElevated) { return DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, phNewToken); } else { DWORD dwReturnBytes = 0; return GetTokenInformation(hToken, TokenLinkedToken, phNewToken, sizeof(HANDLE), &dwReturnBytes); }
return FALSE; }
|
获取资源管理器的 token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| HANDLE GetExplorerToken() { PromotePrivilege();
HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { return NULL; }
HANDLE hExplorerToken = NULL; PROCESSENTRY32 pe = { 0 }; pe.dwSize = sizeof(pe);
BOOL bMore = ::Process32First(hSnapshot, &pe); while (bMore) { if (StrCmpI(L"explorer.exe", pe.szExeFile) == 0) { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID); if (hProcess == NULL) { continue; } if (OpenProcessToken(hProcess, TOKEN_QUERY, &hExplorerToken)) { CloseHandle(hProcess); break; } } bMore = ::Process32Next(hSnapshot, &pe); } CloseHandle(hSnapshot);
return hExplorerToken; }
|
给本进程特权,以便访问系统进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
BOOL PromotePrivilege() { HANDLE hToken; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { LUID uID; if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID)) { CloseHandle(hToken); return FALSE; } TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = uID; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) { CloseHandle(hToken); return FALSE; } CloseHandle(hToken); return TRUE; } return FALSE; }
|
设置当前进程的会话信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| bool CreateProcessAsSystemBySession(LPCTSTR pszAppName, LPCTSTR pszCmd, LPCTSTR pszCwd, DWORD dwSession, HANDLE hEnvToken) { if (pszCmd == NULL) return false;
bool bRet = false;
HANDLE hTokenThis = NULL; bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &hTokenThis); if (!bRet || hTokenThis == NULL) return false;
HANDLE hTokenDup = NULL; bRet = DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup); CloseHandle(hTokenThis); if (!bRet || hTokenDup == NULL) return false;
if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSession, sizeof(DWORD))) { CloseHandle(hTokenDup); return false; }
bRet = CreateProcessByToken(hTokenDup, hEnvToken, pszAppName, pszCmd, pszCwd, TRUE); CloseHandle(hTokenDup);
return bRet; }
|
调用接口创建进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| typedef BOOL(STDMETHODCALLTYPE FAR* LPFNCREATEENVIRONMENTBLOCK) (LPVOID* lpEnvironment, HANDLE hToken, BOOL bInherit); typedef BOOL(STDMETHODCALLTYPE FAR* LPFNDESTROYENVIRONMENTBLOCK) (LPVOID lpEnvironment);
bool CreateProcessByToken(HANDLE hToken, HANDLE hEnvToken, LPCTSTR pszAppName, LPCTSTR pszCmd, LPCTSTR pszCwd, BOOL bWndHide) { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi = { 0 };
si.lpDesktop = (LPWSTR)L"Winsta0\\Default";
if (bWndHide) { si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; } DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; LPVOID pEnvironment = NULL; LPFNCREATEENVIRONMENTBLOCK lpfnCreateEnvironmentBlock = NULL; LPFNDESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = NULL; HMODULE hUserEnvLib = NULL; hUserEnvLib = LoadLibrary(L"userenv.dll"); if (NULL != hUserEnvLib) { lpfnCreateEnvironmentBlock = (LPFNCREATEENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "CreateEnvironmentBlock"); lpfnDestroyEnvironmentBlock = (LPFNDESTROYENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "DestroyEnvironmentBlock"); }
if (NULL != lpfnCreateEnvironmentBlock) { if (lpfnCreateEnvironmentBlock(&pEnvironment, hEnvToken, FALSE)) { dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT; } else pEnvironment = NULL; }
bool bRet = false; BOOL bDisableRedirect = FALSE;
if (CreateProcessAsUser(hToken, pszAppName, (LPTSTR)pszCmd, NULL, NULL, FALSE, dwCreationFlag, pEnvironment, pszCwd, &si, &pi)) { CloseHandle(pi.hThread);
DWORD dwRet = WaitForSingleObject(pi.hProcess, 2 * 60 * 60 * 1000); if (WAIT_TIMEOUT == dwRet) {
} else if (WAIT_OBJECT_0 == dwRet) {
bRet = true; } CloseHandle(pi.hProcess); }
if (NULL != lpfnDestroyEnvironmentBlock) lpfnDestroyEnvironmentBlock(pEnvironment); if (NULL != hUserEnvLib) FreeLibrary(hUserEnvLib);
return bRet; }
|