有不少朋友对Windows Vista的UAC功能不满意,所以特将以前给《程序员》杂志写过的一篇文章贴到这里,意图介绍一下Windows Vista这个安全功能的底层原理,也希望读者朋友能够理解Windows安全设计人员的苦心。

如果看过Mark Russinovich的博客,那么就会明白,UAC的主旨并不是要替代杀毒软件等工具,并不是为了消除病毒、***等恶意软件,UAC是安全纵深防御体系中的一环,其主要设计目的是尽可能让应用程序运行在标准用户权限下,以减少所有应用程序(不管是合法软件,还是恶意软件)有意或者无意,对系统造成的危害。
 
盆盆评注 笔者在探索和学习Windows Vista安全理论的过程,深受《Windows Internals》一书的启发。该书由Mark Russinovich和David Solomon所著,堪称IT Pro和Dev的圣经。

参考

用户帐户控制(UAC),是Windows Vista新引入的安全机制。管理员登录Windows时,系统会同时创建两个访问令牌,其中一个是完全的管理员访问令牌(Full Token),另一个是经过“过滤”的访问令牌,叫做标准用户访问令牌,如附图所示。当Windows系统启动Shell进程(Explorer.exe)时,LSA会把标准用户访问令牌连接到Shell进程,所以Windows Vista启动的用户进程,默认都只具有标准用户权限。如果某个进程需要管理员权限,则系统会提示权限提升,得到用户亲自确认后,系统会把完全的管理员访问令牌连接到该进程上。
 单击看大图

准备知识

在《Windows Internals》的第八章“安全”部分,介绍了一个实例:当我们在Windows XP里启动“日期和时间”控制面板组件时,实际上启动的是“rundll32”进程,由该进程加载“Timedate.cpl”控制面板组件。用Process Explorer检查该“rundll32”进程的访问令牌,可以发现“SeSystemTimePrivilege”特权处于启用状态,如附图所示。
单击看大图

“SeSystemTimePrivilege”是“更改系统时间”特权的内部名称,所有特权都保存在LSA的策略数据库中,该数据库会加载到HKLM\SECURITY注册表分支,用户登录系统时,LSA会读取这些特权值。
 
对于Windows XP专业版,我们可以通过“本地安全策略”管理单元查看用户所具有的特权。可以看到,“更改系统时间”特权仅授予管理员组和Power Users组用户,如附图所示,这就是为什么在Windows XP下,标准用户无法修改系统时间和时区的原因。
单击看大图

盆盆评注 笔者曾经写过一个连载,介绍帐户的登录权利所对应的HKLM\SECURITY注册表键值。可以用同样方法得出特权所对应的注册表键值。

实验准备

现在我们已经知道,要修改系统时间,当前帐户必须具备“更改系统时间”特权。但是在Windows Vista下,默认不是只有标准用户权限吗,这时候应该没有“更改系统时间”特权!那么系统是如何获得“更改系统时间”特权的呢?我们用实验来进行验证。

1. 实验工具

本实验在Windows Vista中文旗舰版上进行,测试帐户为Admin,是管理员组成员。为了查看进程的访问令牌,需要采用先前提到的Process Explorer工具,下载地址如下:
[url]http://www.microsoft.com/technet/sysinternals/utilities/ProcessExplorer.mspx[/url]
还有一个Process Monitor工具,下载地址如下:
[url]http://www.microsoft.com/technet/sysinternals/utilities/processmonitor.mspx[/url]

2. 禁用安全桌面

由于“用户帐户控制”对话框默认运行在“安全桌面”上,这里的安全桌面就是我们按“Ctrl+Alt+Del”组合键所进入的桌面。但是为了改善用户体验,UAC的安全桌面并不是蓝绿色的背景,而是当前用户桌面桌面背景的截图快照(呈暗色显示)。
盆盆评注 参考盆盆翻译自Mark Russinovich的文章,了解如何强制用户必须按“Ctrl+Alt+Del”组合键,才能打开UAC的凭据输入对话框,这样可以确保凭据的安全。

之所以启用“安全桌面”,主要是因为考虑到安全因素,以防其他进程可以对“用户帐户控制”对话框进行操作。但是由于本实验需要用工具对UAC的过程进行全程监控,所以需要临时禁用“安全桌面”功能,方法如下。

运行“secpol.msc”并回车,打开“本地安全策略”对话框,然后在左侧的控制台树里定位到本地策略→安全选项,在右侧详细窗格里双击“用户帐户控制:提示提升时切换到安全桌面”策略项,并选中“已禁用”选项,如附图所示。
单击看大图

实验记录
 
1.查看rundll32进程访问令牌
单击任务栏通知区域的时钟图标,然后单击底部的“更改日期和时间设置”链接,即可打开“日期和时间”窗口。正如您所预料的,这时候还不能对系统时间进行任何修改。同时可以在Process Explorer窗口中看到新增一个进程rundll32(呈绿色显示)。

双击该rundll32进程,即可打开其属性对话框。在“Image”标签页的“Command Line”文本框里可以看到“timedate.cpl”(“时间和日期”的控制面板扩展文件),如附图所示。这说明该rundll32.exe就是“时间和日期”控制面板组件的宿主进程。
单击看大图

切换到“Security”标签页里,就可以看到该rundll32进程的访问令牌,可以看到在下方的特权列表里,只有少得可怜的五个个特权,而其中并没有SeSystemTimePrivilege特权!这就是为什么在默认情况下,无法在管理员环境下修改系统时间的原因。
 单击看大图

2.提升权限
要能够修改系统时间,只需单击“日期和时间”窗口右下侧的“更改日期和时间”按钮,即可打开 “用户帐户控制”对话框,单击“继续”按钮,如附图所示。现在应该可以修改系统时间了。
 单击看大图

看来这个“用户帐户控制”对话框负责向系统传递消息,以便系统确认用户同意提升操作权限。其本质似乎应该是修改相关进程的访问令牌,就本例来说,应该是在rundll32进程的访问令牌里添加SeSystemTimePrivilege特权。那么果真是这样吗?

结果很让人沮丧,重新打开rundll32进程的属性对话框,并切换到“Security”标签页,发现其访问令牌没有任何改变(并没有新增SeSystemTimePrivilege特权)!

这是为什么呢?

3.查看dllhost进程的访问令牌
反复重新做实验后,终于发现在“用户帐户控制”对话框上单击“继续”按钮后,系统会新增一个dllhost进程,在其进程属性对话框的“Image”标签页的“Command Line”文本框里可以看到“/Processid: {9DF523B0-A6C0-4EA9-B5F1-F4565C3AC8B8}”参数,如附图所示。
单击看大图

搜索注册表得知,{9DF523B0-A6C0-4EA9-B5F1-F4565C3AC8B8}就是timedate.cpl的AppID,如附图所示。
单击看大图

在dllhost进程的属性对话框上,切换到“Security”标签页,不出所料,dllhost进程的访问令牌里果然有SeSystemTimePrivilege特权,而且是启用状态,如附图所示。
单击看大图

现在真相大白了,原来Windows Vista表面上让rundll32进程“明修栈道”,背地里却让dllhost进程“暗渡陈仓”,把完全的管理员令牌赋予该dllhost进程,真正让我们可以修改系统时间的是dllhost进程!

4.系统背后的动作

为了更好地对系统背后的动作进行监测,我们再启动Process Monitor,然后再重复上述的操作。
结果在Process Monitor里监测发现,单击“日期和时间”窗口右下侧的“更改日期和时间”按钮,发现由svchost进程(进程ID为1924)启动consent.exe进程,这个consent.exe进程就是“用户帐户控制”对话框,如附图所示。
单击看大图

盆盆评注 在Windows Vista下,应该用Process Monitor代替Filemon和Regmon,Process Monitor除了可以监控注册表和文件活动外,还可以监控进程的活动,包括其堆栈信息。

双击这条记录,在打开的属性对话框上切换到“stack”标签页。在其堆栈信息中发现一个“appinfo.dll”模块,其描述为“应用程序信息服务”,如附图所示。原来这就是Windows Vista系统的“Application Information”服务,当系统确认进程需要管理员权限时,就会由“Application Information”服务负责启动consent进程,以便打开“用户帐户控制”对话框。
单击看大图

在Process Explorer中打开consent进程的属性对话框,在“Image”标签页也可以发现consent的父进程的进程ID为“1924”(svchost进程),如附图所示。
单击看大图

打开进程ID为1924的svchost进程的属性对话框,并切换到“Services”标签页,可以看到其下有“Application Information”服务,该服务以“appinfo.dll”的形式运行在svchost进程中,如附图所示。
单击看大图

在“用户帐户控制”对话框上单击“继续”按钮后,系统获得来自用户的确认信息,接下来我们可以通过Process Monitor监测到由另一个svchost进程(进程ID为1376)加载dllhost的进程映像文件,如附图所示。
单击看大图

双击这条记录,在打开的属性对话框上切换到“stack”标签页,在堆栈信息中发现一个“rpcss.dll”模块,其描述为“分布式COM服务”,如附图所示。原来这就是Windows Vista系统的“DCOM Server Process Launcher”服务,由该服务负责启动dllhost进程。
单击看大图

在Process Explorer中双击打开进程ID为1376的svchost进程的属性对话框,并切换到“Services”标签页,可以看到其下有“DCOM Server Process Launcher”服务,该服务以“rpcss.dll”的形式运行在svchost进程中,如附图所示。
单击看大图
盆盆评注 “Remote Procedure Call (RPC)”服务也是以“rpcss.dll”的形式运行在另外一个svchost进程中。但是两个并没有服务共享同一个svchost进程,这是因为这两个服务的启动帐户不同。“Remote Procedure Call (RPC)”服务的启动帐户是NETWORK SERVICE,而“DCOM Server Process Launcher”服务的启动帐户是SYSTEM。

接下来在Process Monitor中,我们可以看到dllhost进程检查HKCR\AppID\{9DF523B0-A6C0-4EA9-B5F1-F4565C3AC8B8}注册表键值,如附图所示。以便知道应该加载“Timedate.cpl”注册表组件,并最终允许修改系统时间。
单击看大图

5.比较前后访问令牌的SID列表

仔细观察前后两个进程(rundll32进程和dllhost进程)访问令牌中的SID列表,如附图所示,会发现有以下两个显著的不同。
单击看大图

(1) rundll32进程:Administrators组SID被标记为Deny,这表明该进程实际上不属于管理员组。同时访问令牌里只有五个特权,其中并没有“SeSystemTimePrivilege”特权。
访问令牌里包含一个名为“Mandatory Label\Medium Mandatory Level”的标签SID。
(2)dllhost进程:Administrators组SID被标记为Owner,这表明该进程属于管理员组。同时访问令牌里包含“SeSystemTimePrivilege”特权。
访问令牌里包含一个名为“Mandatory Label\High Mandatory Level” 的标签SID。

6.修改系统时区

大家知道,在Windows XP下,我们无法在标准用户下修改系统时区。但是在Windows Vista却可以,无需提升权限,不知道读者朋友有没有考虑过这个问题。

原来这是因为Windows Vista的标准用户多了一个“SeTimeZonePrivilege”(更改时区)特权,所以无需提升权限,就可以修改系统的时区,这给标准用户带来方便。

进一步研究发现,在打开“日期和时间”对话框时,其宿主进程rundll32的访问令牌中,虽然有“SeTimeZonePrivilege”,但是其状态为“Disabled”(如上图所示)。
而只有在单击“更改时区”按钮时,rundll32进程的“SeTimeZonePrivilege”特权才会被激活,如附图所示。
单击看大图

实验结论

1.UAC的实质

当管理员登录时,Explorer进程会获得一个“缩水”的访问令牌,也叫标准用户(Standard User)访问令牌。由于用户进程大多数都是由Explorer启动,所以这些进程会自动继承这份“缩水”的访问令牌(如本例的rundll32进程)。

凭借缩水的访问令牌,用户可以完成绝大多数工作,同时由于用户权限被大大缩小,这样用户进程就不会有意无意破坏系统的完整性,这样就可以大大减少受***面。
如果需要执行管理任务,系统会提醒用户进行确认,一旦认可,将会获得完全版本的管理员访问令牌(如本例的dllhost进程)。

用户帐户控制好比给Windows Vista穿上一件铁布衫,有了它的庇护,Windows系统不再奉行“不抵抗”政策,恶意网页胆敢再来“骚扰”,将被毫不犹豫地阻止。同时最终用户不再需要接受额外的培训,一切都由系统自动完成,只需要做出选择即可,您就没事偷着乐吧!

2.标签SID

至于前后两个进程的访问令牌中新出现的标签SID,其中一个是“Mandatory Label\Medium Mandatory Level”,另一个是“Mandatory Label\High Mandatory Level”。这两个帐户,既不能用来登录,似乎也不能用来设置安全权限,那么到底派什么用场呢?

原来这是用来标记访问令牌,这样系统看到访问令牌中包含“Mandatory Label\Medium Mandatory Level”的标签SID,马上就可以知道该令牌属于标准用户访问令牌;同样如果令牌中包含“Mandatory Label\High Mandatory Level”的标签SID,马上就知道其属于完全的管理员访问令牌。

那么,为什么不在访问令牌中新增一个标志位?例如等于0时就是标准用户令牌,等于1时就是完全权限令牌?这样的话,就不需要新增标签SID了。也许是为了兼容性,毕竟增加一个标志位,就等于要修改访问令牌的数据结构,这可不是一个好主意,而添加几个SID,则相对简单得多。

标签SID的作用很大,当进程需要管理员权限时,系统会根据其父进程的标签SID,来判定是否需要弹出“用户帐户控制”对话框。如果其父进程的标签SID是“Mandatory Label\Medium Mandatory Level”,系统就会弹出“用户帐户控制”对话框。如果父进程的标签SID是“Mandatory Label\High Mandatory Level”,就不会弹出“用户帐户控制”对话框,而是直接运行该目标进程。

由于绝大多数用户进程的父进程是Explorer,所以会弹出“用户帐户控制”对话框。
除此之外, Windows安全子系统还用标签SID来帮助判断一个进程是否可以访问特定的资源对象,是否可以访问某个特定的进程。
盆盆评注 有关标签SID的详细作用,可以参考盆盆发表在ITECN博客上的文章《Windows Vista有趣的标签SID》:[url]http://blogs.itecn.net/blogs/ahpeng/archive/2007/02/27/labelsid.aspx[/url]