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\n"), GetLastError());
__leave;
}
//分配内存,以获取指定项目的令牌信息
pInfo = LocalAlloc(LPTR, *pdwSize);
if (pInfo == NULL)
{
_tprintf(_T("调用LocaAlloc失败,错误代码:%d\n"), GetLastError());
__leave;
}
if (!GetTokenInformation(hToken, InfoClass, pInfo, *pdwSize, pdwSize))
{
_tprintf(_T("调用GetTokenInformation函数失败,错误代码:%d\n"), 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:\t\t%s\n"), 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:\t\t%s\n"), 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:\t%s\n"), 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:\t\t%s\tSourceIdentifier:%08X%08X\n"), 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:\t%08X%08X\n", pst->TokenId.HighPart, pst->TokenId.LowPart);
printf("GroupCount:\t%d\n", pst->GroupCount);
printf("PrivilegeCount:\t%d\n", pst->PrivilegeCount);
printf("ImpersonationLevel:%d\n", pst->ImpersonationLevel);
if (pst->TokenType == TokenPrimary)
{
printf("TokenType:\tTokenPrimary\n");
}
else
printf("TokenType:\tTokenImpersonation\n");
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:\n", 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("\n"));
}
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种权限:\n", 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\t"), privilegeName);
if (ppv->Privileges[dwIndex].Attributes &(SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT))
{
_tprintf(_T("Enabled\n"));
}
else
_tprintf(_T("Disabled\n"));
}
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\n"), 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\n"), 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\n"), GetLastError());
return FALSE;
}
if (ERROR_NOT_ALL_ASSIGNED == GetLastError())
{
wprintf(L"分配指定的特权(%s)失败!\n", 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打开成功!\n", g_pPrivileges[i]);
}
}
TOKEN_ELEVATION_TYPE ElevationType;
BOOL IsAdmin;
GetProcessElevation(&ElevationType, &IsAdmin);
//当前进程权限上下文
wprintf(L"\n");
if (IsAdmin)
wprintf(L"当前是以管理员帐号登录!\n");
else
wprintf(L"当前是以非管理员帐号登录!\n");
if (ElevationType == TokenElevationTypeLimited)
wprintf(L"进程以受限的权限(筛选令牌)运行!\n");
else if (ElevationType == TokenElevationTypeFull)
wprintf(L"进程以完全权限(未筛选令牌)运行!\n");
else
wprintf(L"默认用户运行,UAC被禁用!\n");
_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