Windows Via C/C++:vista下的应用程序权限控制

when adminstrator runs as a standard user

Windows Vista采用了一些新技术提高了终端用户使用系统时的安全性。其中对开发人员而言最为重要的应该是用户账户控制(User Account Control - UAC)。

在Windows系统中,许多用户都是以管理员账号登录的,由于管理员账号被赋于极高的权限,因此以该账号登录的用户几乎可以不受限制的访问所有系统资源。用户以管理员身份登录Vista之前的系统时,系统会为其创建一个安全令牌(security token)用于访问受保护的系统资源。安全令牌将和用户启动的所有新进程相关联,首先是Windows Explorer(资源管理器,登录后系统自动创建,用户创建的所有进程几乎都是其子进程),然后是Windows Explorer的所有后代进程。在这种情形下,用户从网上下载或来自邮件的恶意脚本进程会继承管理员用户的所有权限,因此它可以随心所欲的对系统进行更改或者创建拥有同样权限的恶意进程。

当用户登录Vista时,除了和该用户关联的安全令牌,系统会创建一个只拥有标准用户权限的过滤令牌(filtered token)并将其与当前用户关联。该过滤令牌将和用户启动的所有新进程(包括Windows Explorer)关联,也就是说,当前用户使用的实际上是只拥有标准用户权限的过滤令牌。那么,与过滤令牌关联的进程如何访问需要更高权限的受限资源呢?答应是:没有任何方法可以做到这一点,除了使用用户账户控制(UAC),本节下面的内容将讨论开发人员如何利用UAC实现用户权限的提升。

首先,你可以请求操作系统提升权限,但这只能发生在进程边界。也就是说,进程开始运行时会默认和当前用户的过滤令牌关联。如果要提升进程权限,在进程开始运行之前,你需要告诉操作系统获取用户对此次权限提升操作的确认信息。终端用户可以使用右键菜单中的“以管理员身份运行”(Run as Administrator)命令提升应用的权限,如下图所示:

如果当前用户以管理员身份登录,执行Run as Administrator命令时Windows会弹出一个确认对话框,要求用户确认此次权限提升操作。确认对话框的种类有三种。如果要提升权限的应用是操作系统的一部分,会出现下面的对话框:

如果应用是经过签名的,会出现下面的对话框:

如果应用是未签名的,会出现下面的对话框:

如果当前用户是以标准用户身份登录的,在执行Run as Administrator命令时,还会弹出对话框要求用户输入管理员账号和密码,允许标准用户在某些情况下执行需要更高权限的任务,这种机制被称为“越肩登录”(over-the-shoulder logon)。 

除了Windows Explorer在其右键菜单中提供的Run as Administrator命令,我相信你应该注意到Vista中的许多链接和按钮旁边都有一个盾形图标,这意味着单击链接/按钮时将进行权限提升动作,通常会弹出上面列出的确认对话框。本章最后的例子The process information展示了如何在你自己的应用程序按钮上添加盾形图标。

让我们来看一个简单的例子,当打开任务管理器时,你会发现盾形图标显示在“显示所有用户进程”按钮上:

记下此时任务管理器进程(taskmgr.exe)的进程ID,然后点击“显示所有用户进程”并确认,这时任务管理器会消失一会儿并重新显示,并且一个复选框代替了原来的盾形图标:

现在再来观察任务管理器(taskmgr.exe)的进程ID,我们会发现它和权限提升操作之前的ID并不相同。这是否说明权限提升之后的任务管理器并非是原先的任务管理器进程,而是一个新的实例呢?是的,答案永远是“是的”:Windows只会在进程边界提升进程的权限,进程一旦开始执行,就没有任何方法可以获得更高权限了。然而,权限较低的进程可以产生一个拥有较高权限的子进程,子进程中含有COM服务端,这样父子进程之间就可以进行进程间通信,而不用重新启动应用的新实例。

自动提升进程权限

如果你的应用总是需要管理员级别的权限,比如安装程序运行时如需要访问受限资源,系统会自动提示用户当前应用需要提升权限。那么Windows的UAC组件依据什么来提示用户呢?如果应用的可执行文件中内嵌了指定的清单资源(RT_MANIFEST),系统将在其中寻找节点并解析其内容,下面是含有节点清单的例子:
level属性的取值见下表(表4-8):

除了在可执行文件中嵌入清单资源,还可以将其存放在可执行文件所在的文件夹中,其文件名与可执行文件名相同,只是后缀改为manifest。外部清单文件必须在进程开始执行前就配置好,否则不会起作用。同样,可执行文件中内嵌的清单资源会屏蔽外部清单文件。

手动提升进程权限

在前面章节中讨论的CreateProcess并没有提供任何手动提升进程权限的能力,但是ShellExecuteEx可以做到这一点:

使用ShellExecuteEx提升权限时,必须将其参数的lpVerb成员设置为“runas”,lpFile必须是要以管理员权限执行的可执行文件的路径,下面的代码段展示了这一点:
上面代码执行时会弹出权限提升确认对话框,如果用户拒绝操作,ShellExecuteEx会返回FALSE,GetLastError会返回ERROR_CANCELLED。

注意如果进程以管理员权限开始运行,它调用CreateProcess创建的所有子进程都会继承其父进程的权限——也就是说没有必要再次调用ShellExecuteEx提升子进程的权限了。假如进程运行时依然使用过滤令牌,当其调用CreateProcess创建需要更高权限的子进程时,CreateProcess调用会失败并且GetLastError返回ERROR_ELEVATION_REQUIRED。

开发Windows Vista下的应用时,大多数情况下你应该将应用设计为只需要使用标准用户权限,如果需要提升权限,你应该在激发高权限任务的UI处添加盾形图标。由于提升进程权限是通过执行新进程完成的,你应该把所有需要更高权限的操作都置于另一个应用中,并使用ShellExecuteEx打开它。本章最后的例子Process Information将展示这一做法。

判断当前权限上下文

在上文任务管理器的例子中,任务管理器运行在标准用户权限和管理员权限上下文中时,它会分别显示一个盾形图标和一个复选框——在不同的权限上下文中,应用程序的行为是不同的,那么如何确定当前权限上下文是标准用户模式还是管理员模式呢?

下面的GetProcessElevation函数返回当前进程的原始权限类型以及是否以管理员权限运行当前进程:

代码中用到了GetTokenInformation,其第1个参数用来标识目标令牌的句柄,第2个参数指定为TokenElevationType时表示函数将取得目标令牌的权限信息,并写入到pElevationType参数中,pElevationType是TOKEN_ELEVATION_TYPE的指针,TOKEN_ELEVATION_TYPE是一个枚举类型,其可能的取值如表4-9所示:

从TOKEN_ELEVATION_TYPE的枚举值可以知道和当前进程相关联的令牌权限。接着我们来确定运行该进程的用户是否是管理员(以Run as Administrator运行)。如果TOKEN_ELEVATION_TYPE的值是TokenElevationTypeFull,表示当前进程权限已被成功提升,与其关联的令牌并不是过滤令牌,此时用IsUserAdmin函数判断当前用户是否为管理员既可。如果TOKEN_ELEVATION_TYPE取值是TokenElevationTypeLimited,表示和当前进程关联的令牌依然是过滤令牌,其权限为标准用户权限,此时我们需要获取当前登录用户的安全令牌(向GetTokenInformation传递TokenLinkedToken)并据此检查当前用户是否属于管理员组(使用CreateWellKnownSid和CheckTokenMembership)。

下一节的Process Information例子会用到该函数,Process Information在处理WM_INITDIALOG消息时调用了该函数,在窗口标题栏上显示当前进程的权限类别并决定是否显示盾形图标。

枚举系统中的所有进程

[因为这个例子程序要在Vista下才跑得了,我机器用的还是xp,等有空装了Vista验证后再发上来]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值