C++ 实现提升访问令牌权限
在我们编程实现一些系统操作的时候,往往要求我们执行操作的进程拥有足够的权限方可成功操作。比如,我们使用 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;
}
提权
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;
}
调用
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);
}