Open
Description
Windows 降权
[toc]
背景说明
在服务程序中调用了某一个程序的安装程序,由于权限的问题,这个安装程序也继承了服务的 SYSTEM
权限,导致安装程序与预期不符合。
解决方案
最终实现的目标就是在服务中以普通用户的权限去启动安装程序,要以普通用户去启动,就涉及到降权的问题,需要获取用户的信息。在任务管理器详细信息中可以看到,资源管理器是以普通用户的身份启动的,因此可以在服务中以 Explore.exe
的权限去调用安装程序。
实现逻辑
此逻辑也可以用于解决 UAC
弹窗的问题。
- 获取token
- 通过token获取用户的会话ID
- 通过token和ID启动进程
代码
接口
最终调用的接口为: CreateProcessAsUser
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;
}
实现
实现如下所示:
/*
* @fn
* @brief CreateProcessWithAdmin
* @param[in]
* strpath: 程序路径
* strParams: 程序执行命令
* @param[out]
* @return
*
* @detail
*/
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;
// 通过token获取sessionId
if (::GetTokenInformation(hExplorerToken, TokenSessionId, &dwTokenSessionId, sizeof(DWORD), &dwReturnLen) == FALSE)
{
break;
}
// 通过 SessionId 和 Token运行程序
- res = _CreateProcessAsSystemBySession(lpExePath, lpParam, NULL, dwTokenSessionId, hExplorerToken); // 改动如下所示
// 改动
// 判断当前特权token是否已提升,如果已经提升,则直接通过当前的token启动,如果未提升,则通过提升后的token启动
+ 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是否被提升:
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
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;
}
给本进程特权,以便访问系统进程
/*
* @fn PromotePrivilege
* @brief 调整进程权限
*
* @detail 将进程权限提升成具有调试权限的进程,这个权限应该是进程所能具有的最大权限
* 前提启动这个进程的账户必须是一个管理员,否则没法提升
*/
BOOL PromotePrivilege()
{
// 附给本进程特权,以便访问系统进程
HANDLE hToken;
// 打开一个进程的访问令牌
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
// 取得特权名称为"SetDebugPrivilege"的LUID
LUID uID;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID))
{
CloseHandle(hToken);
return FALSE;
}
// 调整特权级别
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1; // 只启动调试权限,所以权限的个数是一个
tp.Privileges[0].Luid = uID;
//当Attributes = SE_PRIVILEGE_ENABLE时,激活权限
//当Attributes = 0时,关闭权限
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// AdjustTokenPrivileges函数激活或者关闭tp中给定的权限
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL))
{
CloseHandle(hToken);
return FALSE;
}
// 关闭访问令牌句柄
CloseHandle(hToken);
return TRUE;
}
return FALSE;
}
设置当前进程的会话信息:
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;
}
调用接口创建进程:
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/* = FALSE*/)
{
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; // must specify
}
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;
}
blog link Windows 降权