jehn

UAC权限的提权与降权

原文地址:http://www.codeproject.com/KB/vista-security/VistaElevator.aspx

有空就回复一个哈
源代码分享在CSDN上的链接:
http://download.csdn.net/detail/wsyjz/3934006  C++代码
演示程序分享在CSDN上的链接:
http://download.csdn.net/detail/wsyjz/3934004
若是积分不够,可以在原文地址中找到下载链接。

 

另外添加2个相关文章只有提权,没有提到降低权限):

UAC 编程入门 1:进程Mandatory Level检查和自我提升权限  C#代码,英文  -->VB.Net代码

[C#]Enable UAC Shield icons and run as administrator 比较简短的中文说明,C#代码


欢迎转载,但最好请注明  Jero 翻译。

  • 已提权、已经提升权限的进程——可以理解为使用管理员权限运行的。
  • 未提权、没有提升权限的进程——可以理解为使用 非 管理员权限(既普通用户权限)运行的。

UAC机制是由Vista引出,并且由于Windows 7的内核与Vista框架相同,所以本文中提到的Vista的UAC权限相关对于Windows 7完全适用。

正文中斜体都是我添加的说明。

                                            

正文

  当你为Windows Vista开发程序的时候,你常常遇到一个问题:如何使用编程技巧来控制一个应用程序的执行权限(是否从普通用户提权到管理员,或是从管理员降级到普通用户)。当用户运行一个程序的时候,它的执行权限是由应用程序清单(manifest)中的requestedExecutionLevel属性值所决定的,以及Vista/7的用户帐户控制(UAC)根绝需要来采取适当的行为(例如弹出UAC提权确认的窗口,等)。然而,如果一个程序需要执行一个新的不同权限的程序应该如何?

例如:

  • 一个程序普通运行的时候是采用非管理员的权限(没有经过提升权限,普通用户的权限),然后在运行过程中检测到了程序有新版本可用。为了能够让自身更新,它需要启动一个单独的进程来提升自身的权限,这样才能正确执行升级。在这种情况下,一个普通用户的权限需要建立一个新的提升到至少是管理员权限的程序。
  • 大多数安装程序让用户选择在安装结束以后运行其程序。安装程序是以提权过后的进程运行的,但是新的程序需要以普通的,未经过提升的权限来执行。

译者注:简单理解就是以普通用户的权限运行了A程序,然后用A程序运行B程序,但是B程序被提升到管理员权限,而不是继承A程序的权限(如果打开UAC的话,这个提权过程系统会弹出窗口进行确认的)

或是:C程序是通过管理员权限执行的,然后用C程序运行D程序,但是D程序不继承C程序的管理员权限,而是降级到一个普通用户的权限。(对于降级,系统是不会提示的)


  微软已经提供了一个相对简单的方法来完成上面提到的第一个任务(即A程序使B程序提升权限),通过指定ShellExecuteEx API的一个参数为“runas”。但是,由于某些原因,微软并没有提供一个相似的方法来执行一个相反的过程:从一个已提权的程序来启动一个未提权的程序(即C程序使D程序降级)。这篇文章中,我将会展示如何解决这个问题,以及相关的问题。


检测当前程序的执行权限

首先,如何检测一个程序当前的执行权限?源码中的 VistaTools.cxx 文件包含的两个函数对这个问题得到了答案。

第一个函数是 GetElevationType(),它使用了 Win32 API GetTokenInformation() 来获得当前程序其令牌(token)的权限类型,它所可能返回的值为:

    • TokenElevationTypeDefault - 用户没有使用一个分隔的令牌(权限机制)。这个值说明了UAC已经禁用,或者是程序是有一个非管理员组的普通用户运行的。
    • TokenElevationTypeFull - 程序已经获得提权(至少是管理员权限的)
    • TokenElevationTypeLimited - 程序没有经过提权(普通权限运行的)
注意:只有当UAC开启的时候,后面2个值才能返回,而且当前用户是管理员组中的一员(就是这个用户有一个分隔的令牌<既是当前用户是管理员,可以执行普通用户权限的程序,也可以运行管理员权限的程序>
第二个函数是 IsElevated() 它也调用了 GetTokenInformation() API,但是它获取的是TokenElevation 类的信息。它只能返回下面两项之一:
    • S_OK - 当前进程已经提权过。这个数值表明了UAC已经开启,而且当前程序是由管理员提权的;或者UAC已经禁用了,但是当前用户是管理员组中的一员。
    • S_FALSE - 当前进程没有经过提权(受限的)。这个数值表明了UAC已经开启了,而且当前进程只是普通的执行,没有经过提权;或是UAC已经禁用了,进程只是由一个普通用户执行。

使用这两个函数,一个程序可以确定它执行的(权限的)确切情况。


 

运行一个提权的程序

  如果一个未提权的程序需要运行一个提权的程序,所有它需要做的是调用 ShellExecuteEx() API ,然后指示一个参数为"runas",源码中的函数 RunElevated() 就是这样达到提权目的的:

 

BOOL
RunElevated(    HWND hwnd,
        LPCTSTR pszPath,
        LPCTSTR pszParameters = NULL,
        LPCTSTR pszDirectory = NULL )
{
    SHELLEXECUTEINFO shex;

    memset( &shex, 0, sizeof( shex) );

    shex.cbSize        = sizeof( SHELLEXECUTEINFO );
    shex.fMask        = 0;
    shex.hwnd        = hwnd;
    shex.lpVerb        = _T("runas");
    shex.lpFile        = pszPath;
    shex.lpParameters    = pszParameters;
    shex.lpDirectory    = pszDirectory;
    shex.nShow        = SW_NORMAL;

    return ::ShellExecuteEx( &shex );
}

 

从一个已提权的进程来执行一个没有提权的进程

  从相反的方向(从一个已经提权的进程来执行一个没有提权的进程)变得非常复杂。如果父进程已经提权了的,那么它所执行的所有子程序都将继承它的已经提权的权限,而且无论子程序的清单(manifest)中的 requestedExecutionLevel 属性值是如何指定的。由于某些原因,微软并没有提供一个API来直接降低进程的权限,所以我们需要想出一个间接的办法来达到目的。
  这个诀窍是使用Windows Vista自带的任务计划程序(Task Scheduler)来建立以低权限执行的任务,并且要求这个任务建立以后应当立即执行。最终的结果是相同的,犹如进程直接启动一样。

  本文的源码包含的函数 RunAsStdUser() 正是这么做的。它是基于MSDN样本“Registration Trigger Example(注册任务以后立即执行的例子)”,而且它涉及了十几个COM接口与任务计划程序交流,以及设立一个任务通过普通(未提权)的权限。我没有将这些源码的函数包含在这里,因为那是相当乏味的;你可以在 VistaTools.cxx 文件中找到它。


通过事实来证明以上

  这个演示程序(VistaElevator)说明了如何通过编程方法来执行提权和降权。当你运行它的时候,它显示了一个对话框来展示进程运行权限的相关信息,通过 GetElevationType() 和 IsElevated() 这两个函数来获取(函数说明看上面)。它也提供了你两个如何重启进程的选择,提权或是取消。基于你的选择,VistaElevator 调用 RunElevated() 或是 RunAsStdUser() 函数 (依旧看上面) 来重启自身,并在要求的权限下。


 

作者Andrei Belogortseff

posted on 2012-05-14 13:15  jehn  阅读(4766)  评论(0编辑  收藏  举报

导航