从NT6.0开始,微软为了提高安全防护增加了非常多的新机制,其中对普通用户层开发人员影响最大的就是”用户账户控制系统(User Account Control , UAC)”
如果想让自己的程序默认以指定权限运行,可以通过修改VS工程的属性来达到目地:[属性]>[链接器]>[清单文件]>[UAC执行级别]
值 | 说明 |
---|---|
requireAdministrator | 应用程序须以管理员权限运行否则拒绝运行 |
highestAvailable | 应用程序以当前可用的最高权限运行 如果当前用户为管理员,则弹出提示框 如果当前用户为普通用户,则不会提示 |
asInvoker | 应用程序以与主调应用程序相同的权限启动 |
以管理员权限启动程序:
SHELLEXECUTEINFO sei = {sizeof(SHELLEXECUTEINFO)};
//请求提高权限
sei.lpVerb = TEXT("runas");
//需要提升权限的应用程序
sei.lpFile = TEXT("cmd.exe");
sei.nShow = SW_SHOWNORMAL;
if (!ShellExecuteEx(&sei)){
DWORD dwStatus = GetLastError();
if (dwStatus == ERROR_CANCELLED) {
printf("ShellExecute Cancel..");
}
else if(dwStatus == ERROR_FILE_NOT_FOUND)
{
printf("File Not Found");
}
}
以管理员权限启动进程步骤(注:进程启动的权限,在进程启动前已经设置好,所以程序启动之后,已经有启动权限了):
1. 打开当前进程的令牌
2. 查看当前进程是否具有管理员权限
3. 如果无管理员权限,则以管理员权限重新启动本进程
#include
#include
bool runAsAdmin()
{
//1. 获取进程令牌
HANDLE hToken = NULL;
if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) )
return false;
//2. 获取提升类型
TOKEN_ELEVATION_TYPE ElevationType = TokenElevationTypeDefault;
BOOL bIsAdmin = false;
DWORD dwSize = 0;
if (GetTokenInformation(hToken, TokenElevationType, &ElevationType,
sizeof(TOKEN_ELEVATION_TYPE), &dwSize))
{
//2.1 创建管理员组对应的SID
BYTE adminSID(SECURITY_MAX_SID_SIZE);
dwSize = sizeof(adminSID);
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);
//2.2 判断当前进程运行角色是否为管理员
if (ElevationType == TokenElevationTypeLimited){
//a. 获取连接令牌的句柄
HANDLE hUnfilteredToken = NULL;
GetTokenInformation(hToken, TokenLinkedToken, (PVOID)&hUnfilteredToken,
sizeof(HANDLE), &dwSize);
//b. 检查这个原始的令牌是否包含管理员的SID
if (!CheckTokenMembership(hUnfilteredToken, &adminSID, &bIsAdmin))
return false;
CloseHandle(hUnfilteredToken);
} else {
bIsAdmin = IsUserAnAdmin();
}
CloseHandle(hToken);
}
// 3. 判断具体的权限状况
BOOL bFullToken = false;
switch(ElevationType)
{
case TokenElevationTypeDefault: /*默认的用户或UAC被禁用*/
if (IsUserAnAdmin()) bFullToken = true; //默认用户有管理员权限
else bFullToken = false; //默认用户不是管理员组
break;
case TokenElevationTypeFull: /*已经成功提高进程权限*/
if (IsUserAnAdmin()) bFullToken = true; //当前以管理员权限运行
else bFullToken = false; //当前未以管理员权限运行
break;
case TokenElevationTypeLimited: /*进程在以有限的权限运行*/
if (bIsAdmin) bFullToken = false; //用户有管理员权限,但进程权限有限
else bFullToken = false; //用户不是管理员组,且进程权限有限
}
//4. 根据权限的不同控制按钮的显示
if (!bFullToken)
Button_SetElevationRequiredState(GetDlgItem(hWnd, 控件ID), !bFullToken);
else
ShowWindow(GetDlgItem(hWnd, 控件ID), SW_HIDE);
// 1. 隐藏当前窗口
ShowWindow(hWnd, SW_HIDE);
// 2. 获取当前程序路径
WCHAR szApplication[MAX_PATH] = {0};
DWORD cchLength = _countof(szApplication);
QueryFullProcessImageName(GetCurrentProcess(), 0,
szApplication, &cchLength);
// 3. 以管理员权限重新打开进程
SHELIEXECUTEINFO sei = { sizeof(SHELIEXECUTEINFO) };
sei.lpVerb = L"runas"; //请求提升权限
sei.lpFile = szApplication; // 可执行文件路径
sei.lpParameters = NULL; // 不需要参数
sei.nShow = SW_SHOWNORMAL; // 正常显示窗口
if (ShellExecuteEx(&sei))
ExitProcess(0);
else
ShowWindow(hWnd, SW_SHOWNORMAL);
}
TOKEN_ELEVATION_TYPE值:
值 | 描述 |
---|---|
TokenElevationTypeDefault | 进程以默认用户运行,或UAC被禁止 |
TokenElevationTypeFull | 进程的权限被成功提升,而且令牌没有被筛选过 |
TokenElevationTypeLimited | 进程使用和一个筛选过的令牌对应的受限的权限运行 |
LookupPrivilegeValue(
_In_opt_ LPCTSTR lpSystemName, //系统的名字,如果为NULL,则是本地系统
_In_ LPCTSTR lpName, //权限的名字
_Out_ LPUID lpLuid //返回LUID表示
);
lpName:
#define SE_BACKUP_NAMETEXT("SeBackupPrivilege")
#define SE_RESTORE_NAMETEXT("SeRestorePrivilege")
#define SE_SHUTDOWN_NAMETEXT("SeShutdownPrivilege")
#define SE_DEBUG_NAMETEXT("SeDebugPrivilege")
typedef struct _LUID_AND_ATTRIBUTES
{
LUID Luid; //不同的权限类型
DWORD Attributes; //权限描述
} LUID_AND_ATTRIBUTES;
BOOL WINAPI AdjustTokenPrivileges(
_In_ HANDLE TokenHandle, //进程令牌
_In_ BOOL DisableAllPrivileges, //是否禁用所有的权限
_In_opt_ PTOKEN_PRIVILEGES NewState, //新权限
_In_ DWORD BufferLength, //第三个参数的字节长度
_Out_opt_ PTOKEN_PRIVILEGES PreviousState, //原本的权限
_Out_opt_ PDWORD ReturnLength
);
#include
#include
void main()
{
BOOL retn;
HANDLE hToken;
retn = OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);
if(retn != TRUE)
{
printf("获取令牌句柄失败!\n");
return;
}
TOKEN_PRIVILEGES tp; //新特权结构体
LUID Luid;
retn = LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&Luid);
if(retn != TRUE)
{
printf("获取Luid失败\n");
return;
}
//给TP和TP里的LUID结构体赋值
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid = Luid;
AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL);
if(GetLastError() != ERROR_SUCCESS)
{
printf("修改特权不完全或失败!\n");
}
else
{
printf("修改成功!\n");
}
}
方法二提升的权限,需要在访问令牌中具有的权限,如果本身没有关联该权限,调用AdjustTokenPrivileges函数时会出现 ERROR_NOT_ALL_ASSIGNED错误码。
进程具有管理员权限之后,在创建其他进程时,默认是以管理员权限启动。