2.1 Windows核心编程-进程UAC下以管理员权限运行

从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);
}
  • 关键Windows API:
    • OpenProcessToken
    • GetTokenInformation
    • CreateWellKnownSid
    • CheckTokenMembership
    • IsUserAnAdmin
    • ShellExecuteEx

TOKEN_ELEVATION_TYPE值:

描述
TokenElevationTypeDefault 进程以默认用户运行,或UAC被禁止
TokenElevationTypeFull 进程的权限被成功提升,而且令牌没有被筛选过
TokenElevationTypeLimited 进程使用和一个筛选过的令牌对应的受限的权限运行

方法二

  • 相关Windows API:
    • OpenProcessToken
    • LookupPrivilegeValue
    • AdjustTokenPrivileges
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错误码。

进程具有管理员权限之后,在创建其他进程时,默认是以管理员权限启动。

你可能感兴趣的:(windows核心编程)