第4章 进程(3)

时间:2021-04-03 20:02:58

4.5 管理员以标准用户权限运行时

4.5.1访问控制模型ACM(Access Control Model)

第4章 进程(3) 

(1)进(线)程在访问对象时,系统会根据线程的访问令牌与需要访问的对象所持有的“安全描述符”进行匹配认证,来决定是否被允许访问。

(2)访问令牌属进(线程)范围的,相当于进(线)程的身份证(即用户SID,可理解为一把有具有ID的钥匙)。

(3)“安全描述符”属被访问对象的,相当于给对象加把锁。要访问该对象时,只有经过身份认证并且持有正确钥匙的进(线)程才有成功访问该对象。

4.5.2 访问令牌与进(线)程权限

(1)访问令牌(Token)的数据结构 

第4章 进程(3)

    ①访问令牌一般由两个部分:一个是令牌所表示的用户(包括会话ID、用户及其所属的组),这些信息告诉Token属于哪个用户的;另一部分是“特权列表”(Privilege),这用来告诉进程能够进行特定的系统操作,如关闭系统、修改系统时间、加载设备驱动等,拥有的特权越多当然就越牛B!

  ②从vista开始,当使用高特权的管理员帐号登录时,系统会同时创建两个访问令牌,其中一个是完全的管理员访问令牌,另一个是经过“过滤”的标准用户访问令牌(filtered token),并把后者Shell进程(explorer.exe),以后系统启动的所有新进程关联都会继承这个被“筛选令牌”,权限受限制的进程无法访问需要更高权限才能访问的安全资源。注意,虽然以管理员登录,但应用程序默认是以标准用户权限的运行。 

  ③每个进程都有一个Token(被称为主令牌)。可以从父进程处继承(也可以修改,但其最高权限不会超过登录账户的最高权限。即只能在进程的边界上提升权限!要获得更多的权限,就要以管理员的身份运行!)

  ④线程默认下会使用进程的令牌来访问对象。但它也可以拥有自己的访问令牌(被称为模仿令牌(ImperonaitonToken),但该令牌不是必须的,线程可以不拥有模仿令牌。模仿令牌的用处,如当一个线程要找开一个文件,但该文件只有User2才能打开,而主令牌属于User1,这时线程就可以模仿一个User2令牌来打开这个文件。

(2)访问令牌的特权

第4章 进程(3)        第4章 进程(3)

管理员身份运行(未筛选的令牌)                                     以标准用户权限运行(筛选令牌)   

  ①以上实验是同一程序相同登录帐号下,以管理员身份直接运行时的权限比较。

  ②从两图可以得到:管理员身份运行下,应用程序的权限一般要比直接运行的要多。

  ③所谓的应用程序提权,有两种情况:

    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)安全描述符的数据结构:

第4章 进程(3) 

  ①SD中描述了该对象的拥有者Owner、用户Sid、SACL和DACL等信息。

  ②SACL:系统访问控制列表,用来记录某个安全对象被访问的情况,一般不用关心。

  ③DACL:自主访问控制列表,记录了哪些用户可以(/不可以)以何种方式访问该对象(重要)

(2)ACE及安全访问机制

第4章 进程(3) 

  ①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程序】显示访问令牌信息  (直接运行和以管理员身份运行时,会出现不同的结果!)

第4章 进程(3)

/********************************************************
显示本进程访问令牌(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的特权】  直接运行和以管理员身份运行时,会出现不同的结果!

第4章 进程(3)          第4章 进程(3)

#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