C/C++ 实现提升访问令牌权限

2022-12-28 17:48:30 浏览数 (3)

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

函数介绍

代码语言:javascript复制
/* 
	打开与进程关联的访问令牌。
	如果函数成功,则返回值不为零。
*/ 
    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 个参数表示返回的进程令牌句柄。

代码语言:javascript复制
    //打开进程令牌并获取具有 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返回值。

代码语言:javascript复制
    // 获取本地系统的 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就表示启用该特权。

代码语言:javascript复制
    // 设置提升权限信息
    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 个参数表示接收返回上一个特权数据缓冲区应该有的大小。

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

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

代码语言:javascript复制
    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 以上版本的操作系统,可以试着以管理员身份运行程序,这样就可以成功提升进程令牌的访问权限。

编码实现

代码语言:javascript复制
    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;
    }

提权

代码语言:javascript复制
class CEnablePriv
{
public:            //公有(对外开放的接口)

//
//设置当前进程优先级为最高(实时)
//
	BOOL SetRealTimePriority();

	//
	//提升当前进程权限函数("SeShutdownPrivilege"关机权限)
	//
	BOOL EnableShutdownPriv();

	//
	//提升当前进程权限函数("SeDebugPrivilege"读、写控制权限)
	//
	BOOL EnableDebugPriv();

	//
	//提升当前进程权限函数("SeBackupPrivilege"注册表备份权限)
	//
	BOOL EnableBackupPriv();

	//
	//提升当前进程权限函数("SeRestorePrivilege"恢复数据权限)
	//
	BOOL EnableRestorePriv();

private:           //私有(内部使用的接口)

};


//
//设置当前进程优先级为最高(实时)
//
//返回值:“false”是失败,“true”是成功。
BOOL CEnablePriv::SetRealTimePriority()
{
	if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS))
	{
		return false;
	}

	return true;
}

//
//提升当前进程权限函数("SeShutdownPrivilege"关机权限)
//
//返回值:“false”是失败,“true”是成功。
BOOL CEnablePriv::EnableShutdownPriv()
{
	HANDLE hToken;
	LUID sedebugnameValue;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(),
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		return false;

	if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &sedebugnameValue))
	{
		CloseHandle(hToken);
		return false;
	}

	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Luid = sedebugnameValue;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
		CloseHandle(hToken);

	return true;
}

//
//提升当前进程权限函数("SeDebugPrivilege"读、写控制权限)
//
//返回值:“false”是失败,“true”是成功。
BOOL CEnablePriv::EnableDebugPriv()
{
	HANDLE hToken;
	LUID sedebugnameValue;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(),
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		return false;

	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
	{
		CloseHandle(hToken);
		return false;
	}

	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Luid = sedebugnameValue;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
		CloseHandle(hToken);

	return true;
}

//
//提升当前进程权限函数("SeBackupPrivilege"备份数据权限)
//
//返回值:“false”是失败,“true”是成功。
BOOL CEnablePriv::EnableBackupPriv()
{
	HANDLE hToken;
	LUID sedebugnameValue;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(),
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		return false;

	if (!LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &sedebugnameValue))
	{
		CloseHandle(hToken);
		return false;
	}

	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Luid = sedebugnameValue;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
		CloseHandle(hToken);

	return true;
}

//
//提升当前进程权限函数("SeRestorePrivilege"恢复数据权限)
//
//返回值:“false”是失败,“true”是成功。
BOOL CEnablePriv::EnableRestorePriv()
{
	HANDLE hToken;
	LUID sedebugnameValue;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(),
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		return false;

	if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &sedebugnameValue))
	{
		CloseHandle(hToken);
		return false;
	}

	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Luid = sedebugnameValue;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
		CloseHandle(hToken);

	return true;
}

调用

代码语言:javascript复制
		CEnablePriv a;
		if (a.EnableBackupPriv())
		{
			MessageBox(NULL, L"EnableBackupPriv success", L"", NULL);
		}
		if (a.EnableDebugPriv())
		{
			MessageBox(NULL, L"EnableDebugPriv success.", L"", NULL);
		}
		if (a.EnableRestorePriv())
		{
			MessageBox(NULL, L"EnableRestorePriv success .", L"", NULL);
		}
		if (a.EnableShutdownPriv())
		{
			MessageBox(NULL, L"EnableShutdownPriv success.", L"", NULL);
		}
		if (a.SetRealTimePriority())
		{
			MessageBox(NULL, L"SetRealTimePriority success.", L"", NULL);
		}

0 人点赞