C++ AdjustTokenPrivileges 提升访问令牌权限

背景

在我们编程实现一些系统操作的时候,往往要求我们执行操作的进程拥有足够的权限方可成功操作。比如,我们使用 ExitWindows 函数实现关机或重启操作的时候,就要求我们的进程要有 SE_SHUTDOWN_NAME 的权限,否则,会忽视不执行操作。这时,我们唯一能够做的,就是按照要求,提升我们进程的权限。

函数介绍

/* 
    打开与进程关联的访问令牌。
    如果函数成功,则返回值不为零。
*/ 
    BOOL WINAPI OpenProcessToken(
         _In_  HANDLE  ProcessHandle,            // 打开与进程关联的访问令牌。
         _In_  DWORD   DesiredAccess,            // 指定一个访问掩码,指定访问令牌的请求类型。
         _Out_ PHANDLE TokenHandle              // 指向一个句柄的指针,用于标识当函数返回时新打开的访问令牌。
    );


/*
    查看系统权限的特权值,返回信息到一个LUID结构体里。
    如果函数成功,函数将返回非零值
*/
    BOOL WINAPI LookupPrivilegeValue(
         _In_opt_ LPCTSTR lpSystemName,      // 指向以NULL结尾的字符串的指针,该字符串是指向要获取特权值的系统名称
         _In_     LPCTSTR lpName,            // 指向空终止字符串的指针,指定特权的名称
         _Out_    PLUID   lpLuid            // 指向LUID变量的指针,该变量接收由lpSystemName参数指定的系统上已知权限的LUID。
    );

/*
    启用或禁用指定的访问令牌中的权限
    如果函数成功,则返回值不为零
*/
    BOOL WINAPI AdjustTokenPrivileges(
         _In_      HANDLE            TokenHandle,                // 访问令牌的句柄,其中包含要修改的权限
         _In_      BOOL              DisableAllPrivileges,       // 指定该功能是否禁用所有令牌的权限
         _In_opt_  PTOKEN_PRIVILEGES NewState,                   // 指向TOKEN_PRIVILEGES结构的指针,该结构指定特权数组及其属性
         _In_      DWORD             BufferLength,               // 指定由PreviousState参数指向的缓冲区的大小
         _Out_opt_ PTOKEN_PRIVILEGES PreviousState,              // 接收修改权限的完整列表
         _Out_opt_ PDWORD            ReturnLength               // 接收由PreviousState参数指向的缓冲区所需的大小
    );

实现过程

首先,我们需要调用 OpenProcessToken 函数打开指定进程令牌,并获取 TOKEN_ADJUST_PRIVILEGES 权限的令牌句柄。之所以要获取进程令牌权限为 TOKEN_ADJUST_PRIVILEGES,是因为 AdjustTokenPrivileges 函数,要求要有此权限,方可修改进程令牌的访问权限。

其中,第 1 个参数表示要打开进程令牌的进程句柄;第 2 个参数表示我们对进程令牌具有的权限,TOKEN_ADJUST_PRIVILEGES就表示,我们有修改进程令牌的权限;第 3 个参数表示返回的进程令牌句柄。

    //打开进程令牌并获取具有 TOKEN_ADJUST_PRIVILEGES 权限的进程令牌句柄
    bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
    if (FALSE == bRet)
    {
        ShowError("OpenProcessToken");
        return FALSE;
    }

然后,我们调用 LookupPrivilegeValue 函数,获取本地系统指定特权名称的LUID值,这个LUID值就相当于该特权的身份标号。

其中,第 1 个参数表示系统,NULL表示本地系统,即要获取本地系统的指定特权的LUID值;第 2 个参数表示特权名称;第 3 个参数表示获取到的LUID返回值。

    // 获取本地系统的 pszPrivilegesName 特权的LUID值
    bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
    if (FALSE == bRet)
    {
        ShowError("LookupPrivilegeValue");
        return FALSE;
    }

接着,我们就开始对 TOKEN_PRIVILEGES 进程令牌特权结构体进行赋值设置,设置设置新特权的数量、特权对应的LUID值以及特权的属性状态。其中,tokenPrivileges.PrivilegeCount表示设置新特权的特权数量;tokenPrivileges.Privileges[i].Luid表示第 i 个特权对应的LUID值;tokenPrivileges.Privileges[0].Attributes表示特权的属性;SE_PRIVILEGE_ENABLED就表示启用该特权。

    // 设置提升权限信息
    tokenPrivileges.PrivilegeCount = 1;
    tokenPrivileges.Privileges[0].Luid = luidValue;
    tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

最后,我们调用 AdjustTokenPrivileges 函数对进程令牌的特权进行修改,将上面设置好的新特权设置到进程令牌中。

其中,第 1个参数表示进程令牌;第 2 个参数表示能是否禁用所有令牌的权限,FALSE则不禁用;第 3个参数是新设置的特权,指向设置好的令牌特权结构体;第 4 个参数表示返回上一个特权数据缓冲区的大小,不获取,则可以设为 0;第 5 个参数表示返回上一个特权数据缓冲区,不接收返回数据,可以设为 NULL;第 6 个参数表示接收返回上一个特权数据缓冲区应该有的大小。

    // 提升进程令牌访问权限
    bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
    if (FALSE == bRet)
    {
        ShowError("AdjustTokenPrivileges");
        return FALSE;
    }

但是,需要注意的是,AdjustTokenPrivileges 返回 TRUE,并不代表特权就设置成功,还需要使用 GetLastError 来判断错误吗返回值。若错误码返回值为ERROR_SUCCESS,则所有特权设置成功;若为 ERROR_NOT_ALL_ASSIGNED,则表示并不是所有特权都设置成功。

    dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        ShowError("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }

换句话说,如果你只提升了一个特权,且错误码为ERROR_NOT_ALL_ASSIGNED,那么这就是说明提升失败了。如果程序运行在 Win7 或者 Win7 以上版本的操作系统,可以试着以管理员身份运行程序,这样就可以成功提升进程令牌的访问权限。

编码实现

    BOOL EnbalePrivileges(HANDLE hProcess, char *pszPrivilegesName)
    {
        HANDLE hToken = NULL;
        LUID luidValue = {0};
        TOKEN_PRIVILEGES tokenPrivileges = {0};
        BOOL bRet = FALSE;
        DWORD dwRet = 0;
        // 打开进程令牌并获取进程令牌句柄
        bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
        if (FALSE == bRet)
        {
            ShowError("OpenProcessToken");
            return FALSE;
        }
        // 获取本地系统的 pszPrivilegesName 特权的LUID值
        bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
        if (FALSE == bRet)
        {
            ShowError("LookupPrivilegeValue");
            return FALSE;
        }
        // 设置提升权限信息
        tokenPrivileges.PrivilegeCount = 1;
        tokenPrivileges.Privileges[0].Luid = luidValue;
        tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        // 提升进程令牌访问权限
        bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
        if (FALSE == bRet)
        {
            ShowError("AdjustTokenPrivileges");
            return FALSE;
        }
        else
        {
            // 根据错误码判断是否特权都设置成功
            dwRet = ::GetLastError();
            if (ERROR_SUCCESS == dwRet)
            {
                return TRUE;
            }
            else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
            {
                ShowError("ERROR_NOT_ALL_ASSIGNED");
                return FALSE;
            }
        }
        return FALSE;
    }

原文链接: https://www.cnblogs.com/csnd/p/15613452.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++ AdjustTokenPrivileges 提升访问令牌权限

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/360718

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 下午1:27
下一篇 2023年3月2日 下午1:28

相关推荐