关于Windows的用户帐户控制

本文介绍了Windows Vista及后续版本中的用户账户控制(UAC)机制,详细解释了进程权限筛选和提升的过程,并提供了示例代码来展示如何检测进程权限状态以及如何以管理员身份运行进程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《Windows核心编程》这本书第四章提到Windows操作系统用户权限的概念。虽然这本书的第五版是基于Vista这个神奇的系统的,但是这个XP没有的概念我觉得有必要记录一下!

所谓UAC就是Windows中的用户帐户控制(User Account Control)。我们对这项技术最为直观的体会莫过于:当我们安装一些某些程序时会弹出一个窗口询问我们是否要以管理员身份运行这个程序。不知道大家有没有想过这样一个问题:“我就是以管理员身份登录的呀?为什么还要小爷授权??”

在Vista之前的操作系统中,操作系统会分发一个最高权限的安全令牌给管理员用户,之后每当用户运行一个进程,这个令牌就会被授予进程。微软认为这样的机制并不安全,因为这样用户可能会无意中授予恶意软件很高的权限。因此,在Vista中微软采取了新机制:即用户创建进程时授予进程的是筛选后的令牌,这个令牌只有标准用户的权限。如果程序需要管理员权限才能良好运行,那么它必须提醒用户,让用户显式的授予这个权限。

现在我们知道了,虽然我们是以管理员身份登录的,但是我们运行的程序只有标准用户的权限!那么如何才能提升我们应用程序的权限呢?

简单来说大概有三种方法:

1.右键你的exe文件,然后以管理员身份运行!

2.为程序设定一个XML清单,里面写明运行时的权限!

3.在exe兼容性选项中设定!


有一点我们必须要注意:进程的权限只能在边界提升,也就是说我们必须在进程开始运行之前提升他的权限。一旦进程开始运行了,我们在提升他的权限就晚了!同时,通过Create方法启动的进程的权限是可以被继承的!


上面的提到的提升权限的方法与编程无关,换句话说都是用户要主动的提升进程的权限!下面的函数能让我们的程序发出提升权限请求!

函数ShellExecuteEx能以管理员身份运行一个进程!

下面的程序以管理员方式打开了一个cmd

int _tmain(int argc,TCHAR**argv)
{
	SHELLEXECUTEINFO sei={sizeof(SHELLEXECUTEINFO)};//这个结构很复杂
	sei.lpVerb=TEXT("runas");
	sei.lpFile=TEXT("cmd.exe");
	sei.nShow=SW_SHOWDEFAULT;
	ShellExecuteEx(&sei);
	return 0;
}

有时后我们需要知道我们的进程是否被管理员提升了权限,又是如何提升的?同时我们的用户是否是有管理员权限,下面的函数实现这个功能

/************************************************************************/
/* TOKEN_ELEVATION_TYPE表示进程令牌的权限枚举
		TokenElevationTypeDefault表示没有令牌?
		TokenElevationTypeFull表示提升权限的
		TokenElevationTypeLimited表示筛选令牌,即标准用户权限	
*/
//以下函数pElevationType返回进程提升权限的方式,pIsAdmin返回进程是否为管理员
/************************************************************************/
BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE* pElevationType,BOOL* pIsAdmin)
{
	HANDLE hToken=NULL;//当前程序使用的令牌
	DWORD dwSize;
	if(!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken))//获取当前的程序令牌
		return (FALSE);
	BOOL bResult=FALSE;
	if(GetTokenInformation(hToken,TokenElevationType,pElevationType,sizeof(TokenElevationType),&dwSize))
	{
		BYTE adminSID[SECURITY_MAX_SID_SIZE];
		dwSize=sizeof(adminSID);
		//创建一个安全描述符
		CreateWellKnownSid(WinBuiltinAdministratorsSid,//安全描述符的类型
						NULL,//表示本地计算机的安全描述符
						&adminSID,&dwSize//输出
						);
		if(*pElevationType==TokenElevationTypeLimited)//当前程序如果是标准用户权限
		{
			HANDLE hUnfilteredToken=NULL;
			GetTokenInformation(hToken,TokenLinkedToken,//表示获取用户的安全令牌信息,即进程令牌还没被筛选之前
							(VOID*)&hUnfilteredToken,sizeof(HANDLE),&dwSize);
			if(CheckTokenMembership(hUnfilteredToken,&adminSID,pIsAdmin))//确定这个未筛选令牌是否属于管理员用户组
				bResult=TRUE;
			CloseHandle(hUnfilteredToken);
		}else
		{//当前程序如果是管理员权限
			*pIsAdmin=IsUserAnAdmin();// 测试当前用户是否属于管理员组
			bResult=TRUE;
		}
	}
	CloseHandle(hToken);
	return bResult;
}

上面的程序以不同方式运行得到如下结果

以管理员帐户登录,以管理员方式运行:isAdmin=TRUE   ElevationType=TokenElevationTypeFull    //进程的权限没有被阉割

以管理员帐户登录,以普通方式运行:isAdmin=false   ElevationType=TokenElevationTypeLitmited   //进程权限被阉割

以普通用户登录,以管理员方式运行:isAdmin=TRUE   ElevationType=TokenElevationTypeFull    //进程的权限被提升了

以普通用户登录,以普通方式运行:isAdmin=false   ElevationType=TokenElevationTypeDefault   //进程权限和用户相同,没有被提升

打开计算机管理,发现计算机里有一个被禁用的帐户,叫Administrator

这个帐户是被禁用的,他非常牛逼,所有他的进程的权限都不会被阉割也就是说上面的返回值总是这样的

isAdmin=TRUE   ElevationType=TokenElevationTypeDefault  //进程权限和用户相同,但是已经有了最高权限


上面的程序是摘自书本的,我有一个疑问:经过一些测试我发现isAdmin的值完全能通过IsUserAnAdmind得到,程序是不是要写的这么复杂呢?苦思很久也没有明白为什么需要下面这几行代码,而函数的返回值到底表示什么意思


另外下面这个函数,为我们的进程能使用某种功能,当然用户必须被授予了使用这种功能的权利!

static BOOL EnablePrivilege(PCTSTR pszPrivName, BOOL bEnable = TRUE) {

      BOOL bOk = FALSE;    // Assume function fails
      HANDLE hToken;

      // Try to open this process' access token
      if (OpenProcessToken(GetCurrentProcess(), 
         TOKEN_ADJUST_PRIVILEGES, &hToken)) {

         // Attempt to modify the "Lock pages in Memory" privilege
         TOKEN_PRIVILEGES tp = { 1 };
         LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid);
         tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
         AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
         bOk = (GetLastError() == ERROR_SUCCESS);
         CloseHandle(hToken);
      }
      return(bOk);
   }

比如下面的调用让我们的程序可以锁定内存页

EnablePrivilege(SE_LOCK_MEMORY_NAME, TRUE);










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值