判断进程是否以管理员权限运行(***)

来源:互联网 发布:数据报送管理办法 编辑:程序博客网 时间:2024/06/06 15:36

        有时我们需要判断进程是否以管理员权限运行,比如在运行安装包时需要安装包进程以管理员权限运行,因为安装包将执行写注册表、注册组件等需要管理员权限的操作。如果没有申请到管理员权限,这些需要管理员权限的操作都会执行失败,则会导致安装失败。以QQ7.1安装包为例,如果当前以标准用户登录到系统中,并且UAC关闭,双击运行时将申请不到管理员权限,QQ会弹出如下的提示框:


    我们的TL安装程序可以参考QQ的做法,避免出现没有管理员权限导致安装失败的问题,即如果没有申请到管理员权限,则直接弹出如上类似的提示。要弹出提示,则要判断当前安装程序的进程是否以管理员权限运行。那应该如何判断呢?

一、判断函数的实现

    经查阅相关资料得知,调用GetTokenInformation函数,获取TOKEN_ELEVATION结构体信息,通过结构体中的TokenIsElevated字段就能判断出来,相关的代码如下:

BOOL IsRunasAdmin() {BOOL bElevated = FALSE;  HANDLE hToken = NULL;  // Get current process tokenif ( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )return FALSE;TOKEN_ELEVATION tokenEle;DWORD dwRetLen = 0;  // Retrieve token elevation informationif ( GetTokenInformation( hToken, TokenElevation, &tokenEle, sizeof(tokenEle), &dwRetLen ) ) {  if ( dwRetLen == sizeof(tokenEle) ) {bElevated = tokenEle.TokenIsElevated;  }}  CloseHandle( hToken );  return bElevated;  } 

二、管理员权限与当前用户的类型、UAC开关的关系

    对于需要管理员权限的程序,可以通过内嵌manifest文件的方式,设置requireAdministrator(需要管理员权限),这样在程序启动时会申请管理员权限。在VS2010中,可以在工程的属性中进行配置,如下图所示:


    程序在什么情况下可以申请到管理员权限,在什么情况下申请不到,在这里就各种场景简单的说明一下。考虑当前的登入的用户类型、UAC的打开与关闭、程序本身有没有设置requireAdministrator属性。

   1、UAC打开

    此种情况只讨论设置requireAdministrator属性如何申请到管理员权限的情形。对于没有设置requireAdministrator属性的程序,肯定是以非管理员权限运行的。

    (1)登录的是超级管理员Administrator

    默认情况下,超级管理员Administrator是禁用的,可以通过这样的途径来开启:右键计算机 ->管理 ->系统工具 ->本地用户和组 ->用户 ->右键Administrator ->属性 ->取消账户禁用 ->注销(不行就重启)->登陆Administrator即可。超级管理员在用户管理中是可以重命名的。

    对于设置了requireAdministrator属性的程序,启动时能申请到管理员权限。由于Administrator是超级管理员,权限最高的用户,所以在提权的时候不会弹出UAC提示窗口。

    (2)登录的是管理员(非超级管理员Administrator)

    在启动设置了requireAdministrator属性的程序时,会弹出UAC提示框,如下所示:


     (3)登录的是标准用户(非管理员)

     在启动设置了requireAdministrator属性的程序时,会弹出输入管理员密码的UAC提示框,如下:


    输入管理员密码后,才能正常的启动程序。

   2、UAC关闭

    UAC关闭,应该只是关闭了UAC提示。此种情况设置了requireAdministrator属性的程序和没有设置requireAdministrator属性的程序都要嫁衣说明。

    (1)登录的是超级管理员Administrator或者管理员(普通管理员)

    在启动设置了requireAdministrator属性的程序,会申请到管理员权限,且不会弹出UAC提示框。对于没有设置requireAdministrator属性的程序,启动时会以管理员权限运行还是标准权限运行呢?经研究,发现也会以管理员权限运行。比如在运行中输入cmd,打开cmd窗口,窗口的标题中会加上“管理员”的字样,如下所示:


当然还有很多示例可以拿过来加以验证。

    (2)登录的是标准用户

    在启动设置了requireAdministrator属性的程序,还会弹出提示输入管理员密码的提示框吗?UAC已经关闭,不会再弹出提示要输入管理员密码的提示框,那程序权限是如何处理的呢?查看系统关于UAC关闭时的说明如下:

    由上可知,程序是申请不到管理员权限的,程序是可以启动的,但是所有执行需要管理员权限的操作都会返回失败这就是QQ在申请不到管理员权限时弹出无法安装的提示的原因所在了。

三、IsRunasAdmin函数的说明

    IsRunasAdmin已经在上面提到的所有的场景中验证通过。之前在《Windows核心编程》第5版的第4章第5节“管理员以标准用户权限运行时”看到,书中也给出了一个方法,如下所示:
BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE* pElevationType, BOOL* pIsAdmin) {   HANDLE hToken = NULL;   DWORD dwSize;    // Get current process token   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))      return(FALSE);   BOOL bResult = FALSE;   // Retrieve elevation type information    if (GetTokenInformation(hToken, TokenElevationType,       pElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {      // Create the SID corresponding to the Administrators group      byte adminSID[SECURITY_MAX_SID_SIZE];      dwSize = sizeof(adminSID);      CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID,          &dwSize);      if (*pElevationType == TokenElevationTypeLimited) {         // Get handle to linked token (will have one if we are lua)         HANDLE hUnfilteredToken = NULL;         GetTokenInformation(hToken, TokenLinkedToken, (VOID*)             &hUnfilteredToken, sizeof(HANDLE), &dwSize);         // Check if this original token contains admin SID         if (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin)) {            bResult = TRUE;         }         // Don't forget to close the unfiltered token         CloseHandle(hUnfilteredToken);      } else {         *pIsAdmin = IsUserAnAdmin();         bResult = TRUE;      }   }   // Don't forget to close the process token   CloseHandle(hToken);   return(bResult);}
根据文中的意思,通过传出参数pIsAdmin可以得知进程是否以管理员权限运行。经测试,在标准用户登录且UAC打开时是有问题的,经调试,每次都会走到如下的分支:
*pIsAdmin = IsUserAnAdmin();
这样就有问题,IsUserAnAdmin是判断当前登录用户是否是管理员用户。而在标准用户登录且UAC打开时,启动requireAdministrator的程序,会弹出输入管理员密码的提示框,也可以以管理员权限运行的,即使当前登录的用户不是管理员用户。

四、判断其他的进程是否以管理员权限运行

    上面的代码稍加改动一下,就可以实现判断其他进程是否以管理员权限运行了。假设我们知道目标进程的进程id(在测试时,我们可以到任务管理器中查看目标进程的进程id),则可以调用OpenProcess得到进程句柄,然后再将这个句柄传递给OpenProcessToken。相关代码如下所示:

        DWORD dwPid = 2337; // 目标进程的进程idHANDLE hProcess = ::OpenProcess( /*PROCESS_ALL_ACCESS*/PROCESS_QUERY_INFORMATION, FALSE, dwPid );if ( hProcess == NULL ){strTip.Format( _T("OpenProcess to get the process handle failed, possible reason: the process id doesn't exsit, GetLastError: %d"), GetLastError() );AfxMessageBox( strTip );return;}BOOL bRunAsAdmin = IsRunasAdmin( hProcess );if ( bRunAsAdmin ){strTip.Format( _T("Pid(%d) run as admin!"), dwPid );}else{strTip.Format( _T("Pid(%d) don't run as admin!"), dwPid );}AfxMessageBox( strTip );

BOOL IsRunasAdmin( HANDLE hProcess ){    BOOL bElevated = FALSE;      HANDLE hToken = NULL;      CString strTip;    // Get target process token    if ( !OpenProcessToken( hProcess/*GetCurrentProcess()*/, TOKEN_QUERY, &hToken ) )    {        strTip.Format( _T("OpenProcessToken failed, GetLastError: %d"), GetLastError() );        AfxMessageBox( strTip );        return FALSE;    }    TOKEN_ELEVATION tokenEle;    DWORD dwRetLen = 0;      // Retrieve token elevation information    if ( GetTokenInformation( hToken, TokenElevation, &tokenEle, sizeof(tokenEle), &dwRetLen ) )    {          if ( dwRetLen == sizeof(tokenEle) )        {            bElevated = tokenEle.TokenIsElevated;          }    }      else    {        strTip.Format( _T("GetTokenInformation failed, GetLastError: %d"), GetLastError() );        AfxMessageBox( strTip );    }    CloseHandle( hToken );      return bElevated;  }
    注意,在调用OpenProcess获取进程句柄时,不要传递全权限的参数PROCESS_ALL_ACCESS,传递较低权限的参数PROCESS_QUERY_INFORMATION,避免因为程序的权限不够导致OpenProcess函数执行失败。至于为什么要使用PROCESS_QUERY_INFORMATION参数,可以参见MSDN中说明:

                                                       
因为获取的进程句柄是给OpenProcessToken函数使用的,所以根据MSDN中的说明,使用PROCESS_QUERY_INFORMATION参数。

4 0