浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第4章 进程(3)

Posted on 2015-07-24 13:52  浅墨浓香  阅读(1190)  评论(0编辑  收藏  举报

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