C++ 实现提升访问令牌权限
2021/7/16 12:35:34
本文主要是介绍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); }
这篇关于C++ 实现提升访问令牌权限的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-22怎么通过控制台去看我的页面渲染的内容在哪个文件中呢-icode9专业技术文章分享
- 2024-12-22el-tabs 组件只被引用了一次,但有时会渲染两次是什么原因?-icode9专业技术文章分享
- 2024-12-22wordpress有哪些好的安全插件?-icode9专业技术文章分享
- 2024-12-22wordpress如何查看系统有哪些cron任务?-icode9专业技术文章分享
- 2024-12-21Svg Sprite Icon教程:轻松入门与应用指南
- 2024-12-20Excel数据导出实战:新手必学的简单教程
- 2024-12-20RBAC的权限实战:新手入门教程
- 2024-12-20Svg Sprite Icon实战:从入门到上手的全面指南
- 2024-12-20LCD1602显示模块详解
- 2024-12-20利用Gemini构建处理各种PDF文档的Document AI管道