4.5 管理员以标准用户权限运行时
4.5.1访问控制模型ACM(Access Control Model)
(1)进(线)程在访问对象时,系统会根据线程的访问令牌与需要访问的对象所持有的“安全描述符”进行匹配认证,来决定是否被允许访问。
(2)访问令牌属进(线程)范围的,相当于进(线)程的身份证(即用户SID,可理解为一把有具有ID的钥匙)。
(3)“安全描述符”属被访问对象的,相当于给对象加把锁。要访问该对象时,只有经过身份认证并且持有正确钥匙的进(线)程才有成功访问该对象。
4.5.2 访问令牌与进(线)程权限
(1)访问令牌(Token)的数据结构
①访问令牌一般由两个部分:一个是令牌所表示的用户(包括会话ID、用户及其所属的组),这些信息告诉Token属于哪个用户的;另一部分是“特权列表”(Privilege),这用来告诉进程能够进行特定的系统操作,如关闭系统、修改系统时间、加载设备驱动等,拥有的特权越多当然就越牛B!
②从vista开始,当使用高特权的管理员帐号登录时,系统会同时创建两个访问令牌,其中一个是完全的管理员访问令牌,另一个是经过“过滤”的标准用户访问令牌(filtered token),并把后者Shell进程(explorer.exe),以后系统启动的所有新进程关联都会继承这个被“筛选令牌”,权限受限制的进程无法访问需要更高权限才能访问的安全资源。注意,虽然以管理员登录,但应用程序默认是以标准用户权限的运行。
③每个进程都有一个Token(被称为主令牌)。可以从父进程处继承(也可以修改,但其最高权限不会超过登录账户的最高权限。即只能在进程的边界上提升权限!要获得更多的权限,就要以管理员的身份运行!)
④线程默认下会使用进程的令牌来访问对象。但它也可以拥有自己的访问令牌(被称为模仿令牌(ImperonaitonToken),但该令牌不是必须的,线程可以不拥有模仿令牌。模仿令牌的用处,如当一个线程要找开一个文件,但该文件只有User2才能打开,而主令牌属于User1,这时线程就可以模仿一个User2令牌来打开这个文件。
(2)访问令牌的特权
管理员身份运行(未筛选的令牌) 以标准用户权限运行(筛选令牌)
①以上实验是同一程序在相同登录帐号下,以管理员身份和直接运行时的权限比较。
②从两图可以得到:管理员身份运行下,应用程序的权限一般要比直接运行的要多。
③所谓的应用程序提权,有两种情况:
A、获得更多种的权限,如管理员身份运行下,有23种。而直接运行则只有5种。比如管理身份运行下有SeSystemEnvironmentPrivilege等权限,在直接运行(标准用户权限)时没有。
B、第2种情况是上面两种图,都有多种特权在默认下是没开启的,可以通过AdjustTokenPrivileges(或SetTokenInformation)来开启。如上图的“筛选令牌”中还有4种没开启。
④一个Token中的特权列表中,已经指明该Token可以拥有哪些特权。但不并是所有特权都开启的,如果要使用该特权,需要使用SetTokenInformation修改。注意,特权列表中没有的特权,是不能开启的。比如在直接运行下,其Token中有SeShutdownPrivilege的特权,所以是可以开启的,列表中没有SeSecurityPrivilege特权,所以是不能开启的,即只能在进程的边界上提升权限。(当然你非要添加的话还是有办法的)!
⑤特权列表:用TOKEN_PRIVILEGES结构体表示
字段 |
描述 |
DWORD PrivilegeCount |
特权数量(数组的长度) |
LUID_AND_ATTRIBUTES privilegs[N] |
特权数组。元素类型表示一种特权:由两部分组成 ①Luid:局部唯一标识符,代表某特权的值 ②Attributes:特权的属性,其值如下 SE_PRIVILEGE_ENABLED表示启用该特权 SE_PRIVILEGE_ENABLED_BY_DEFAULT表示使特权默认有效 SE_PRIVILEGE_REMOVED:表示禁用特权 SE_PRIVILEGE_USED_FOR_ACCESS:取得对象或服务的访问权 |
4.5.3 安全描述符(SecurityDescriptor)——是访问控制模型的灵魂,每个内核对象中都一个SecurityDescriptor结构体的指针,指向一个SD,该SD的结构体如下
(1)安全描述符的数据结构:
①SD中描述了该对象的拥有者Owner、用户Sid、SACL和DACL等信息。
②SACL:系统访问控制列表,用来记录某个安全对象被访问的情况,一般不用关心。
③DACL:自主访问控制列表,记录了哪些用户可以(/不可以)以何种方式访问该对象(重要)。
(2)ACE及安全访问机制
①DACL中包括访问控制项列表(AccessControl Entries,ACE) ,每个ACE指明哪个用户或组可以对该对象能否操作及何种操作。如上图的Tom用户不能读、写、执行。(注意SD上说的权限是指访问该对象的能力,而Token中所说的权限是指特权,如关机等系统操作)
②当打开一个对象时,会从线程所拥有的Token中(如果存在模仿令牌则使用模仿令牌,否则使用主令牌)获取用户名和用户所在组列表,与对象的安全描述符中的DACL比较,其匹配算法如下:
A、如果被访问对象的DACL为NULL,则线程拥有完全的访问权限。
B、对象的DACL不为NULL,但是AceCount ==0(ACE,访问控制项),则拒绝任何线程访问。
C、遍历DACL,找到与Token中用户或组一致的ACE,如果该ACE没有提供线程要访问的那项权限(如Group1组的用户要求写入时),则直接退出安全检查函数,并拒绝该线程访问。
D、遍历DACL,没找到跟令牌中用户或组一致的ACE,并拒绝该线程访问。
E、遍历DACL,找到与令牌中用户或组一致的ACE,如果该ACE中进(线)程要求的访问权限(如读写操作),结束安全检查,并允许该线程访问。
★注意:以上列表中的ACE排列顺序很重要,在匹配时会从列表的第1项开始,一旦匹配,就不再检查后面的ACE项。比如线程1要求写入该对象时,由于第1项列表指明不能写,所以该线程是不可访问这个对象,尽管Tom1用户属Group1用户组的(该组是允许写的),当然,如果将ACE中的第2项Group1移动到第1项时,Tom也是可写该对象的。
4.5.4 与访问令牌Token操作相关API
(1)API参数中对Token可能进行的操作——以下值一般作为API参数被传入
值 |
描述 |
TOKEN_ADJUST_DEFAULT |
修改令牌所有者、主组或访问控制列表DACL |
TOKEN_ADJUST_GROUPS |
修改令牌的组属性 |
TOKEN_ADJUST_PRIVILEGES |
Enable或Disable令牌的特权 |
TOKEN_ADJUST_SESSIONID |
调整令牌的Session ID。进程需要 SE_TCB_NAME 特权。 |
TOKEN_ASSIGN_PRIMARY |
为进程分配主令牌。需要 SE_ASSIGNPRIMARYTOKEN_NAME特权。 |
TOKEN_DUPLICATE |
复制令牌 |
TOKEN_EXECUTE |
合并 STANDARD_RIGHTS_EXECUTE 和 TOKEN_IMPERSONATE |
TOKEN_IMPERSONATE |
附加一个模拟令牌到进程 |
TOKEN_QUERY |
查询令牌 |
TOKEN_QUERY_SOURCE |
查询令牌源 |
TOKEN_READ |
合并STANDARD_RIGHTS_READ 和TOKEN_QUERY |
TOKEN_WRITE |
合并 STANDARD_RIGHTS_WRITE, TOKEN_ADJUST_PRIVILEGES, TOKEN_ADJUST_GROUPS, 和 TOKEN_ADJUST_DEFAULT |
TOKEN_ALL_ACCESS |
合并所以可能的操作 |
(2)操作Token的API
①OpenProcessToken、OpenThreadToken
②AdjustTokenPrivileges、AdjustTokenGroups
③GetTokenInformation、SetTokenInformation
④LookupPrivilegeValue、PrivilegeCheck
④LookupPrivilegeDisplayName、LookupPrivilegeName
4.5.5 与安全描述符有关API
(1)安全标识符SID
①SID的组成
A、简访为S-R-I-S-S........
B、S表示数字系列、R表示修订级、I表示标识符权限值,后面S为子权限值。
C、SID实际上包含两个值:发行SID的机构的值,一个32位的相对标识符(RID)。RID指明了用户和组的信息。
如:S-1-5-21-310440588-250036847-580389505-500
我们来先分析这个重要的SID。第一项S表示该字符串是SID;第二项是SID的版本号,对于2000来说,这个就是1;然后是标志符的颁发机构(identifier authority),对于2000内的帐户,颁发机构就是NT,值是5。然后表示一系列的子颁发机构,前面几项是标志域的,最后一个标志着域内的帐户和组
②操作SID的相关API
A、AllocateAndInitializeSid、FreeSid
B、CopySid
C、EqualSid、EqualPrefixSid
D、GetLengthSid、GetSidLengthRequired
E、 GetSidIdentifierAuthority、GetSidSubAuthority、GetSidSubAuthorityCount
F、InitializeSid、IsValidSid
G、LookupAccountName、LookUpAccountSid
(2)安全描述符SD
①SD的格式
A、绝对格式:可用InitializeSecurityDescriptor得到
B、相对格式:SECURITE_DESCRIPTOR定义的
C、将绝对格式转为相对格式:MakeSelfRelativeSD
②创建SD的方法
//"D:(D;;GR;;;WD)(A;;GW;;;WD)" 的意思是: DACL添加Everyone用户,拒绝读,允许写权限
InitSecurityDescriptor( sa, TEXT("D:(D;;GR;;;WD)(A;;GW;;;WD)") );
//D:(A;;GA;;;WD) 的意思是: DACL添加Everyone用户,并让其拥有全部权限
InitSecurityDescriptor( sa, TEXT("D:(A;;GA;;;WD)") );
(3)操作ACL的API
①GetAclInformation、SetAclInformation
②GetSecurityDescriptorDacl、SetSecurityDescriptorDacl
③GetSecurityDescriptorSacl、SetSecurityDescriptorSacl
④InitializeAcl、IsValidAcl
【TokenInfo程序】显示访问令牌信息 (直接运行和以管理员身份运行时,会出现不同的结果!)
/******************************************************** 显示本进程访问令牌(Token)的信息 *********************************************************/ #include <windows.h> #include <tchar.h> #include <sddl.h> #include <stdio.h> #define CheckAndLocalFree(ptr) if (ptr != NULL) { LocalFree(ptr); ptr = NULL; } //获取指定项目的令牌信息 LPVOID RetrieveTokenInfomationClass( HANDLE hToken, TOKEN_INFORMATION_CLASS InfoClass, LPDWORD pdwSize) { LPVOID pInfo = NULL; BOOL fSuccess = FALSE; *pdwSize = 0; __try { //第1次调用,获得返回令牌信息的所需的内存大小 GetTokenInformation(hToken, InfoClass, NULL, 0, pdwSize); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { _tprintf(_T("调用GetTokenInformation函数失败,错误代码:%d "), GetLastError()); __leave; } //分配内存,以获取指定项目的令牌信息 pInfo = LocalAlloc(LPTR, *pdwSize); if (pInfo == NULL) { _tprintf(_T("调用LocaAlloc失败,错误代码:%d "), GetLastError()); __leave; } if (!GetTokenInformation(hToken, InfoClass, pInfo, *pdwSize, pdwSize)) { _tprintf(_T("调用GetTokenInformation函数失败,错误代码:%d "), GetLastError()); __leave; } fSuccess = TRUE; } __finally { if (fSuccess == FALSE) { CheckAndLocalFree(pInfo); } } return pInfo; } //显示用户SID BOOL DisplayUserInfo(HANDLE hToken) { PTOKEN_USER pUserInfo = NULL; DWORD dwSize = 0; PTSTR pName = NULL; //从令牌获取用户信息 pUserInfo = (PTOKEN_USER)RetrieveTokenInfomationClass(hToken, TokenUser, &dwSize); if (NULL == pUserInfo) return FALSE; ConvertSidToStringSid(pUserInfo->User.Sid, &pName); if (NULL == pName) { CheckAndLocalFree(pUserInfo); return FALSE; } _tprintf(_T("User: %s "), pName); CheckAndLocalFree(pUserInfo); CheckAndLocalFree(pName); return TRUE; } //显示所有者信息 BOOL DisplayOwnerInfo(HANDLE hToken) { PTOKEN_OWNER pOwnerInfo = NULL; DWORD dwSize = 0; PTSTR pName = NULL; //从令牌获取所有者信息 pOwnerInfo = (PTOKEN_OWNER)RetrieveTokenInfomationClass(hToken, TokenOwner, &dwSize); if (NULL == pOwnerInfo) return FALSE; ConvertSidToStringSid(pOwnerInfo->Owner, &pName); if (NULL == pName) { CheckAndLocalFree(pOwnerInfo); return FALSE; } _tprintf(_T("Owner: %s "), pName); CheckAndLocalFree(pOwnerInfo); CheckAndLocalFree(pName); return TRUE; } //显示主用户组信息 BOOL DisplayPrimaryGroupInfo(HANDLE hToken) { PTOKEN_PRIMARY_GROUP ptgr = NULL; DWORD dwSize = 0; PTSTR pName = NULL; //从令牌获取所有者信息 ptgr = (PTOKEN_PRIMARY_GROUP)RetrieveTokenInfomationClass(hToken, TokenPrimaryGroup, &dwSize); if (NULL == ptgr) return FALSE; ConvertSidToStringSid(ptgr->PrimaryGroup, &pName); if (NULL == pName) { CheckAndLocalFree(ptgr); return FALSE; } _tprintf(_T("PrimaryGroup: %s "), pName); CheckAndLocalFree(ptgr); CheckAndLocalFree(pName); return TRUE; } //显示源信息 BOOL DisplaySource(HANDLE hToken) { PTOKEN_SOURCE pSrc = NULL; DWORD dwSize = 0; PTSTR pName = NULL; //从令牌获取源的信息 pSrc = (PTOKEN_SOURCE)RetrieveTokenInfomationClass(hToken, TokenSource, &dwSize); if (NULL == pSrc) return FALSE; printf(("Source: %s SourceIdentifier:%08X%08X "), pSrc->SourceName, pSrc->SourceIdentifier.HighPart, pSrc->SourceIdentifier.LowPart); CheckAndLocalFree(pSrc); CheckAndLocalFree(pName); return TRUE; } //DisplayStatistics BOOL DisplayStatistice(HANDLE hToken) { PTOKEN_STATISTICS pst = NULL; DWORD dwSize = 0; PTSTR pName = NULL; //从令牌获取统计的信息 pst = (PTOKEN_STATISTICS)RetrieveTokenInfomationClass(hToken, TokenStatistics, &dwSize); if (NULL == pst) return FALSE; //列出统计信息 printf("TokenID: %08X%08X ", pst->TokenId.HighPart, pst->TokenId.LowPart); printf("GroupCount: %d ", pst->GroupCount); printf("PrivilegeCount: %d ", pst->PrivilegeCount); printf("ImpersonationLevel:%d ", pst->ImpersonationLevel); if (pst->TokenType == TokenPrimary) { printf("TokenType: TokenPrimary "); } else printf("TokenType: TokenImpersonation "); CheckAndLocalFree(pst); CheckAndLocalFree(pName); return TRUE; } //显示用户组信息 BOOL DisplayGroupsInfo(HANDLE hToken) { PTOKEN_GROUPS pgrp = NULL; DWORD dwSize = 0; PTSTR pName = NULL; //从令牌获取用户组信息 pgrp = (PTOKEN_GROUPS)RetrieveTokenInfomationClass(hToken, TokenGroups, &dwSize); if (NULL == pgrp) return FALSE; printf("共找到token(0x%08x)的%d个Sid: ", hToken, pgrp->GroupCount); for (DWORD dwIndex = 0; dwIndex < pgrp->GroupCount; dwIndex++) { ConvertSidToStringSid(pgrp->Groups[dwIndex].Sid, &pName); _tprintf(_T(" %d SID: %s"), dwIndex, pName); CheckAndLocalFree(pName); if ((pgrp->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) { dwSize = GetLengthSid(pgrp->Groups[dwIndex].Sid); _tprintf(_T(" (This is LogonID)")); } _tprintf(_T(" ")); } CheckAndLocalFree(pName); CheckAndLocalFree(pgrp); return TRUE; } //显示特权信息 BOOL DisplayPrivileges(HANDLE hToken) { PTOKEN_PRIVILEGES ppv = NULL; DWORD dwSize = 0; PTSTR pName = NULL; TCHAR privilegeName[500] = {}; TCHAR displayName[500] = {}; DWORD privilegeNameSize; DWORD displayNameSize; DWORD langID = GetUserDefaultLangID(); //从令牌获取特权信息 ppv = (PTOKEN_PRIVILEGES)RetrieveTokenInfomationClass(hToken, TokenPrivileges, &dwSize); if (NULL == ppv) return FALSE; printf("token(0x%08x)的共有%d种权限: ", hToken, ppv->PrivilegeCount); for (DWORD dwIndex = 0; dwIndex < ppv->PrivilegeCount; dwIndex++) { privilegeNameSize = sizeof(privilegeName) / sizeof(privilegeName[0]); displayNameSize = sizeof(displayName) / sizeof(displayName[0]); LookupPrivilegeName(NULL, &ppv->Privileges[dwIndex].Luid, privilegeName, &privilegeNameSize); LookupPrivilegeDisplayName(NULL, privilegeName, displayName, &displayNameSize, &langID); _tprintf(_T("%-31s "), privilegeName); if (ppv->Privileges[dwIndex].Attributes &(SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT)) { _tprintf(_T("Enabled ")); } else _tprintf(_T("Disabled ")); } CheckAndLocalFree(pName); CheckAndLocalFree(ppv); return TRUE; } //显示Token的用户名、所有者、主用户组、源、权限及统计信息 BOOL DisplayTokenInformation(HANDLE hToken) { //显示用户名 DisplayUserInfo(hToken); //显示所有者信息 DisplayOwnerInfo(hToken); //显示主用户组信息 DisplayPrimaryGroupInfo(hToken); //显示源信息 DisplaySource(hToken); //用户组信息 DisplayGroupsInfo(hToken); //用户权限 DisplayPrivileges(hToken); //显示统计信息 DisplayStatistice(hToken); return TRUE; } //显示进程的访问令牌内容 BOOL DisplayCallerAccessTokenInformation() { HANDLE hToken = NULL; BOOL bResult = FALSE; //使用OpenThreadToken()函数判断线程运行的状态 bResult = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, TRUE, &hToken); if (bResult == FALSE && GetLastError() == ERROR_NO_TOKEN) { //打开线程访问令牌失败后,尝试使用进程入口标志 bResult = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &hToken); } if (bResult) { bResult = DisplayTokenInformation(hToken); CloseHandle(hToken); } else { _tprintf(_T("OpenThread/ProcessToken failed with %d "), GetLastError()); } return bResult; } int main() { DisplayCallerAccessTokenInformation(); _tsystem(_T("PAUSE")); return 0; }
【修改Token的特权】 直接运行和以管理员身份运行时,会出现不同的结果!
#include <windows.h> #include <tchar.h> #include <locale.h> #include <sddl.h> #include <stdio.h> #include <Shlobj.h> //提升(修改)权限 BOOL SetPrivilege(HANDLE hToken, LPTSTR pszPrivilege, BOOL bEnablePrivilege) { BOOL rv; TOKEN_PRIVILEGES tp; //函数查看系统权限的特权值,返回信息到一个LUID结构体里 rv = LookupPrivilegeValue( NULL,//表示所要查看的系统,本地系统直接用NULL pszPrivilege,//指向一个以零结尾的字符串,指定特权的名称,此参数可 //指定常数,如宏SE_SHUTDOWN_NAME对应的字符串为"SeShutdownPrivilege" &tp.Privileges[0].Luid); //用来接收所返回的制定特权名称的信息 if (!rv) { _tprintf(_T("LookupPrivilegeValue error:%d "), GetLastError()); return FALSE; } tp.PrivilegeCount = 1; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; //启用或禁止 指定访问令牌的特权 //返回值:ERROR_SUCCESS——成功 // ERROR_NOT_ALL_ASSIGNED——这个令牌没有参数要修改的那一项或多项的权限。(1个或多个没有修改) rv = AdjustTokenPrivileges( hToken,//要修改权限的访问令牌句柄 FALSE, //禁用所有权限标志 &tp, //新特权信息缓冲区的指针(结构体) sizeof(TOKEN_PRIVILEGES), //缓冲数据大小,以字节为单位 0, //接收被改变特权当前状态的Buffer 0); //接收当前状态缓冲区所需的大小 if (!rv) { _tprintf(_T("AdjustPrivilegeValue error:%d "), GetLastError()); return FALSE; } if (ERROR_NOT_ALL_ASSIGNED == GetLastError()) { wprintf(L"分配指定的特权(%s)失败! ", pszPrivilege); return FALSE; } return TRUE; } LPTSTR g_pPrivileges[] = { SE_SHUTDOWN_NAME, SE_BACKUP_NAME, SE_DEBUG_NAME, SE_TIME_ZONE_NAME, SE_TCB_NAME }; //用来返回提升类型和指出进程是否正在以管理员身份运行 BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE* pElevationType, BOOL* pIsAdmin) { HANDLE hToken = NULL; BOOL bResult = FALSE; DWORD dwSize; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) return FALSE; //获取提升权限的类型 //TokenElevationTypeDefault 默认运行用户运行,UAC被禁用 //TokenElevationTypeFull 权限被成功提升, 而且令牌没有被筛选过 //TokenElevationTypeLimited 进程以受限的权限运行, 它对应于一个筛选过的令牌 if (GetTokenInformation(hToken, TokenElevationType, pElevationType, sizeof(TOKEN_ELEVATION_TYPE),&dwSize)) { //创建管理员组SID BYTE adminSID[SECURITY_MAX_SID_SIZE]; dwSize = sizeof(adminSID); CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize); //进程是否受限的权限运行, 它对应于一个筛选过的令牌 if (*pElevationType == TokenElevationTypeLimited) { //获取链接令牌(未筛选Token?)的句柄 HANDLE hUnfilterToken = NULL; GetTokenInformation(hToken, TokenLinkedToken, (LPVOID)&hUnfilterToken, sizeof(HANDLE), &dwSize); //检测令牌是否包含管理员SID if (CheckTokenMembership(hUnfilterToken,&adminSID,pIsAdmin)) { bResult = TRUE; } CloseHandle(hUnfilterToken); } else { //不是受限用户,则判断是否管理员 *pIsAdmin = IsUserAnAdmin(); //须包含Shlobj.h bResult = TRUE; } } CloseHandle(hToken); return bResult; } int main() { setlocale(LC_ALL, "chs"); HANDLE hToken = NULL; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken); int iCnt = sizeof(g_pPrivileges) / sizeof(g_pPrivileges[0]); for (int i = 0; i < iCnt; i++) { if (SetPrivilege(hToken, g_pPrivileges[i], TRUE)) { wprintf(L"特权%s打开成功! ", g_pPrivileges[i]); } } TOKEN_ELEVATION_TYPE ElevationType; BOOL IsAdmin; GetProcessElevation(&ElevationType, &IsAdmin); //当前进程权限上下文 wprintf(L" "); if (IsAdmin) wprintf(L"当前是以管理员帐号登录! "); else wprintf(L"当前是以非管理员帐号登录! "); if (ElevationType == TokenElevationTypeLimited) wprintf(L"进程以受限的权限(筛选令牌)运行! "); else if (ElevationType == TokenElevationTypeFull) wprintf(L"进程以完全权限(未筛选令牌)运行! "); else wprintf(L"默认用户运行,UAC被禁用! "); _tsystem(_T("PAUSE")); return 0; }
4.5.6 以管理员身份运行程序的方法
(1)应用程序→右键→“以管理员身份运行”
(2)自动提升进程的权限——VS2013下
①找到工程属性→“配置属性”→“链接器”→“清单文件”
②在“生成清单”中选择“是”,启动用户帐户控制(UAC)选“是”,UAC执行级别中选择“requireAdministrator”
★如果只是为了在VS中调试程序,可以“管理员身份”运行VS,但这只能在调试中提升权限。生成的应用程序本身并没有被改变过来。
(3)手动提升进程的权限——函数ShellExcuteEx(LPSHELLEXECUTEINFO pExecInfo)
参数 |
描述 |
PSHELLEXECUTEINFO pExecInfo |
SHELLEXECUTEINFO结构体设置 ①lpVert字段:必须设为TEXT("runas") ②lpFile字段:TEXT("xxx.exe") ③nShow字段:SW_SHOWNORMAL ④lpParameters:用来传递给子进程的命令行参数 |
返回值 |
成功——TRUE 失败——FALSE,如果用户拒绝提升权限,GetLastError返回ERROR_CANCELLED |
★当以管理员身份运行后,表示进程访问令牌的特权就越多,但很多特权默认是禁用的,所以还得通过AdjustTokenPrivileges等函数来启用这些特权。
4.5.7完整性级别(Integrity levels)
(1)完整性级别简介
①在进行对进程“访问令牌”与“安全描述符”的匹配时,要先进行完整性级别的比较,注意这个比较是在检查DACL之前完成的。所以,即便进程拥有访问资源的权限,但由于进程的完整性级别低于资源所要求的完整性级别,访问仍会被拒绝!
②信任级别
级别 |
应用程序示例 |
低 |
应用程序以低信任级别运行,就无法访问高任信级别的资源,如保护模式下的IE,就是运行在低级别的,目的就是拒绝从网上下载代码修改Windows环境。 |
中 |
默认情况下,所有应用程序都以“中”信任级别来启动,并使用一个“筛选令牌”来运行 |
高 |
如果应用程序以提升后的权限来启动,则以“高”信任级别来运行 |
系统 |
只有以LocalSystem或LocalService身份运行的进程,才能获得这个信任级别。 |
(2)进程完整性级别
①获取和设置进程完整性级别
PTOKEN_MANDATORY_LABEL pTokenInfo;
GetTokenInformation(hToken,TokenIntegrityLevel,pTokenInfo,dwNeededSize,&dwNeededSize));
②LookupAccountSid 函数得到完整性级别的可显示名称
(3)资源完整性级别(Resource integrity levels)
①资源的完整性级别存放在安全描述符的系统访问控制表(SACL)中的一个特殊的访问控制项(ACE)中。
②看到大多数资源并没有显式地设定其完整性级别。系统将没有显式声明完整性级别的资源看作带有中等完整性级别。
③操作的API
A、获取和设置: GetNamedSecurityInfo、SetNamedSecurityInfo——调用的参数LABEL_SECURITY_INFORMATION。
B、增加:InitializeAcl、AddMandatoryAce