让您的应用程序更好地适应 Windows Vista 的用户帐户控制
2009-02-09 17:43:56 来源:WEB开发网本文讨论:
用户帐户控制
设计以标准用户身份运行的应用程序
必要时提升的权限
本文使用了以下技术:
Windows Vista
目录
登录时有效
创建经过滤的令牌
设计使用经过滤的令牌运行的应用程序
设计需要管理员权限的应用程序
安装时的权限
为 MSI 标记权限
以管理员权限运行应用程序
使用应用程序指令清单来标记所需权限
令牌 COM 对象
提醒用户需要提升的权限
不同安全环境中进程间的通信
低权限的 Internet Explorer
确保 Windows Vista 中 Windows 服务的安全
常见的应用程序兼容性问题
安装时出现的问题
典型的运行时应用程序问题
总结
在过去的一年中,我与太多的开发人员有过合作的经历,他们最初对用户帐户控制 (UAC) 的工作方式会怀有疑问,而最终又得以理解,这历历在目的过往情节交织在一起,使 UAC 像一团巨大的迷雾。诚然,毫无疑问的是,学习如何编写适用于标准用户的优秀应用程序是要费些功夫,但这方面的知识会令您在 Windows® 程序员之路上更进一步。本文的目的是通过一些必要的说明,为您开辟一条捷径。
UAC 是 Microsoft 用来降低 Windows Vista™ 用户的默认权限的一种方法。从战略角度看,Microsoft 正在转向这样一种操作系统环境:用户没有或不需要拥有那些会影响操作系统和计算机级配置的权限即可完成日常的事务处理。这种环境还可防止用户彼此影响对方的状态和设置。
进行这样一个转型有很多现实原因。UAC 可限制默认情况下应用程序的运行权限。在企业应用中,这种限制使用户以“用户”组的身份进行操作,而不是使用“管理员”身份,因而能够使那些重要的公司安全策略得以真正施行。进而又会降低用户对计算机的影响,减少总拥有成本 (TCO)。在家庭使用中,如果用户没有对“UAC 许可”或“凭据”提示进行确认,恶意软件便无法影响系统,也无法安装服务或驱动程序。虽然“社交诈骗”(social engineering) 这一类安全风险依然存在,但由于融入了获取提升的权限的方案,将来在技术上可以实现控制哪些应用程序能够在无需提示用户的情况下以计算机级权限来执行操作。
UAC 的主要目的是生成默认用户令牌,利用这一令牌,应用程序以相当于“用户”组成员的身份来运行。这一过程首先是为具有提升的权限的用户在进行交互式登录时创建受限或经过滤的令牌。如果需要更多权限来执行受限制的操作,则系统将提示用户需要安全桌面的授权。UAC 还包括用于支持应用程序兼容性以及保证提升过程安全的其他技术。
登录时有效
用户登录到计算机时,UAC 便开始工作。在交互式登录过程中,本地安全机构 (LSA) 接收用户输入的凭据并执行初始登录,对用户的令牌进行评估,以确定该令牌是否有所谓的提升的权限。如果 LSA 确定该用户具有提升的权限,它将过滤此令牌,然后以经过滤的令牌重新执行登录。如果用户是“管理员”组的成员,用户通常会同时具有经过滤的令牌和完全权限令牌。管理员的完全权限令牌有权在计算机级的位置(如 Program Files 和 HKEY_LOCAL_MACHINE (HKLM))进行写入操作,因而会影响整个计算机。但经过滤的令牌只是像“用户”组成员一样,不具有这些权限,它用于创建用户的交互式桌面会话。
始终会使用与“用户”组成员的令牌在权限上大体相当的令牌来创建桌面会话和 explorer.exe。对于任一从“开始”菜单启动的或由用户在资源管理器窗口中双击来启动的进程,如果不需要提升权限,都将只是继承这个经过滤的令牌。因此,默认情况下,每个应用程序都将以标准用户令牌运行。如果在指令清单或应用程序兼容性设置中,将某个进程标记为需要管理员权限,则 UAC 将提示用户需要提升权限。图 1 和图 2 分别显示了用于指定许可和凭据的提示对话框。如果用户进行了此类授权,则“应用程序信息”服务将以完全权限令牌来创建进程。
图 1许可提示
图 2凭据提示
创建经过滤的令牌
“用户帐户控制”可将图 3 中所列的各组定义为具有提升的权限。因此,如果 LSA 发现在用户的初始令牌中列出了其中的任何组成员身份或权限,则将在进行交互式登录时会使用某版本的 CreateRestrictedToken API 创建经过滤的令牌,完全权限令牌则由 LSA 保存。这两种令牌相互关联,使用具有新 TokenLinkedToken 信息类型的 GetTokenInformation API 可从经过滤的令牌中获取完全权限令牌。但请注意,UAC 不会影响服务、网络或批登录。
Figure3提升的权限
内置管理员 |
高级用户 |
帐户操作员 |
服务器操作员 |
打印机操作员 |
备份操作员 |
RAS Server 组 |
Windows NT 4.0 App Compat 组 |
网络配置操作员 |
域管理员 |
域控制器 |
证书发行者 |
架构管理员 |
企业管理员 |
组策略管理员 |
如果用户不属于图 3 中列出的任何一个组,但具有某些权限,则在创建经过滤的令牌时会将这些权限移除。这些权限包括:SeCreateTokenPrivilege、SeTcbPrivilege、SeTakeOwnershipPrivilege、SeBackupPrivilege、SeRestorePrivilege、SeDebugPrivilege、SeImpersonatePrivilege 和 SeRelabelPrivilege。
如果用户为“管理员”组的成员,则经过滤的令牌将具有设为“只用于否定”的“管理员”组成员资格,这会禁止 AccessCheck 使用此组来允许对资源进行访问。另外,所有影响计算机的权限也都将从该令牌中移除。(这是用来创建 explorer.exe 和非提升的进程的默认令牌。)可以使用 Windows Vista 自带的 whoami.exe 查看此令牌,只需在“命令”窗口中运行 whoami.exe /all 即可。图 4 和图 5 分别描述了该令牌中列出的组成员资格和该令牌中的权限。
Figure5经过滤的令牌中包括的权限
权限 | 描述 |
SeShutdownPrivilege | 关闭系统。 |
SeChangeNotifyPrivilege | 跳过遍历检查。 |
SeUndockPrivilege | 将计算机从插接站中删除。 |
SeIncreaseWorkingSetPrivilege | 增加进程工作集。 |
SeTimeZonePrivilege | 更改时区。 |
Figure4经过滤的令牌中列出的组成员资格
组名 | 属性 |
组名 | 属性 |
Everyone | 强制组,默认情况下启用,已启用的组 |
BUILTINAdministrators | 只用于拒绝的组 |
BUILTINUsers | 强制组,默认情况下启用,已启用的组 |
Mandatory LabelMedium Mandatory Level | 强制组,默认情况下启用,已启用的组 |
设计使用经过滤的令牌运行的应用程序
大多数应用程序运行时都不需要管理员权限。如果应用程序在执行时不维持跨会话状态且不执行修改本地安全策略一类的操作,则只需标准用户令牌便可正常运行。有时,应用程序的某些部分会需要管理员权限,您应将这些部分分离到单独的进程中运行。稍后我们将讨论这个问题。
开发标准用户应用程序过程中要执行的最重要的步骤就是测试以标准用户身份运行的应用程序。应用程序在(正式)运行时会失败,其中最重要的一个原因就是开发人员从未以标准用户身份对其进行测试。切记要执行该步骤。
下一个最重要的步骤是确定应用程序二进制文件和各个用户的配置数据的保存位置。完全是按用户安装 (per-user) 的应用程序意味着程序的二进制文件和配置都是特定于用户的,这类应用程序应在用户配置文件 %userprofile% 中维护程序的所有状态。在注册表中,应将所有状态都写入到用户的配置单元 HKEY_CURRENT_USER (HKCU) 中。在很多情况下,将二进制文件安装到 %ProgramFiles% 目录中是很有必要的,但该操作需要管理员权限,我们将在本文的后面部分对此进行讨论。
有时,在存储游戏的高分记录时,您需要维护系统级的状态,并允许用户对其进行写入。建议将此数据保存到 %allusersprofile% 目录中。在此处可以创建应用程序的专业目录,并允许用户对其进行写入。请注意,其他用户可以篡改此文件。
设计需要管理员权限的应用程序
对于编写需要管理员权限的应用程序,我的实际建议就是:不要编写!除非您十分确定应用程序需要提升的权限,否则只编写使用标准用户令牌的应用程序即可。只有极少数 API 必须由管理员进行调用。
当然,也存在某些可能需要使用管理员权限的情况,如在安装时或在保持跨会话状态时,这时您的应用程序通常是作为服务来实现的。UAC 不适用于服务,Windows 服务可使用其他安全设置,我们将在后面讨论这一问题。如果应用程序在本地计算机中实施安全模型,也会需要管理员权限。例如,如果设置是由组策略“应用程序分发和管理 (ADM)”或通过 Systems Management Server (SMS) 部署的,则在本地计算机中可能还存在对这些设置进行更新的应用程序。这将需要管理员权限。
安装时的权限
一般来说,安装应用程序都需要管理员权限,这通常是由于应用程序二进制文件要写入到 Program Files 目录中,而对于标准用户,该目录是只读的。这表示,任何要向此位置写入二进制文件的应用程序都要标记为需要管理员权限。由于 Windows Installer 已经与 UAC 集成,安装包(MSI 文件)成为用户更愿意使用的安装方法。它们可以通过两种方式来指定:按用户安装或按计算机安装,这两种方式只在必要时才提示用户需要管理员权限。
虽然在 Program Files 目录进行安装时将向用户显示提升权限的提示,但将应用程序的二进制文件放到该位置是绝对有好处的。在该目录中进行安装需要用户具有管理员权限,这样,企业的标准用户或“家长控制”用户就无法任意安装应用程序。另外,应用程序在整个计算机中只有一组二进制文件,这些文件将受保护,标准用户无法将其篡改。这样,在修复 Bug 或添加功能后更新二进制文件时,更容易确定应用程序的状态和版本。
设计应用程序时,可能会存在某些要为用户应用的应用程序设置。可能的情况是,用户会指定关于视图显示方式或要显示哪些菜单的自定义信息。提供这种便利性的一个好方法就是在 Program Files 目录中创建默认模板,然后将其复制到用户的 AppData 目录 %localappdata%,再对其进行修改。此数据可以在第一次运行应用程序时复制,并会为每位用户启用按用户状态。另一方面,如果希望使用单个用户可以修改的计算机级状态,可以将此数据放入 %allusersprofile%,正如前面所提到的存储游戏高分记录的示例那样。
就安装而言,在 UAC 环境下另一个有些棘手的方面就是更新。如果要更新 Program Files 目录中的二进制文件,则改写二进制文件的进程必须具有管理员或系统权限。使用 Windows Installer 的一个好处是能够使用 MSI 修补程序 (MSP) 文件进行修补。从 Windows Installer 3.1(Windows XP 的必要更新程序)开始,如果 MSP 文件已由原始 MSI 文件中包含的证书签名,则对系统应用该文件。有关详细信息,请参阅 MSDN® 中有关“用户帐户控制修补”(在 windowssdk.msdn.microsoft.com/ms710366.aspx 位置提供)的文档。
除了使用 MSI 文件外,应用程序开发人员还可以集成某个指定要以管理员权限运行的二进制文件(这样可以防止在企业内部以标准用户身份进行更新),也可以包括某个将执行任何所需更新的服务。强烈建议不要使用服务,因为服务中的任何安全漏洞都可能使 ISV 成为恶意软件的攻击途径。MSP 文件是最佳的选择。
为 MSI 标记权限
在 Windows Vista 之前,使用 ALLUSERS 属性来标记,MSI 文件是将应用程序的快捷方式安装到当前用户位置还是对计算机上所有用户都进行安装。这些快捷方式包括 DesktopFolder、ProgramMenuFolder、StartMenuFolder 和 StartupFolder。由于不存在类似的按用户的 Program Files 目录,应用程序二进制文件通常仍写入到 Program Files 目录。
遗憾的是,出于应用程序兼容性的原因,Windows Installer 无法仅根据 ALLUSERS 属性来确定是否应提示用户输入身份凭据。而是在 MSI 文件中分配一个附加位来确定是否要对用户进行提示。这便是“字数统计摘要”属性中的第 3 个数据位。如果此数据位的值设置为 1,则会认为该包是按用户 (per-user) 的 MSI,将不会提示用户需要“管理员”令牌。
要将某个包指定为只能由管理员安装到“公共”配置文件,请设置 ALLUSERS="1" 或 ALLUSERS="2",并将“字数统计摘要”属性的第 3 位设置为 0。要将某个包指定为可由标准用户安装的按用户 (per-user) 安装。请设置 ALLUSERS="" 或不定义该属性,并将“字数统计摘要”属性的第 3 位设置为 1。
以管理员权限运行应用程序
有时可能会需要以管理员权限来运行应用程序。您可能会编写直接与某一硬件或直接与可在 HKLM 中设置计算机级设置的应用程序进行交互的代码。如果可能,在设计应用程序时应限制对管理员权限的需要以减小代码段的访问权限,或者限制与以完全管理员权限启动的应用程序进行通信。
除了基于 MSI 进行权限提升外,还有两种方式可创建使用用户的完全管理员令牌运行的进程。在创建进程和创建 COM 对象期间,“应用程序信息服务”(AIS) 将使用 CoCreateAsAdmin 标记进行检查,以确定二进制文件是否需要管理员权限。需要特别注意的是,提升将在进程创建时发生。权限或组成员资格永远不会在运行时添加到进程令牌中,而仅会在创建时添加。
一句话提醒:只有在调用 ShellExecute 来创建进程时,才会向用户显示 UAC 提升提示。任何调用 CreateProcess 系列命令要求提升权限的操作会返回 ERROR_ELEVATION_REQUIRED 错误。
使用应用程序指令清单来标记所需权限
创建新进程后,AIS 将对二进制文件进行检查,以确定该文件是否需要提升权限。首先检查的是嵌入到应用程序资源中的应用程序指令清单。该操作优先于任何其他类型的应用程序标记,包括应用程序兼容性标记或 UAC 的“安装程序检测”,这将在后面讨论。该指令清单可定义运行级别,以此向 Windows 通知运行二进制文件所需的权限。 共有三种运行级别可供选择:asInvoker、highestAvailable 和 requireAdministrator。
如果 AIS 发现了标记为“asInvoker”运行级别的二进制文件,则不会进行任何操作,该进程将继承创建它的父进程的进程令牌。“requireAdministrator”运行级别也是非常明了的,它定义进程必须由属于“管理员”组成员的用户令牌来创建。如果尝试创建此进程的用户不具有管理员身份,则将向该用户显示“凭据”对话框,要求他输入凭据。
highestAvailable 运行级别稍微有些复杂。它指示,如果某用户具有链接的令牌,则应用程序应使用更高权限的令牌来运行。该级别通常用于包含为“用户”和“管理员”组设计的 UI 的应用程序,它可确保该应用程序获取用户的完全权限。需要特别注意的是,Backup Operators 组和 Network Operators 组的成员将具有链接的令牌,并将收到需要其凭据的提示,凭据对话框将显示这两个组的用户登录图块以及管理员组成员的用户登录图块。图 6 显示了应用程序指令清单的一个示例。
Figure6AdminAppl.exe 的 AdminApp.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="X86" name="AdminApp" type="win32"/>
<description>Description of your application</description>
<!-- Identify the application security requirements. -->
<ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
<ms_asmv2:security>
<ms_asmv2:requestedPrivileges>
<ms_asmv2:requestedExecutionLevel
level="requireAdministrator"
uiAccess="false"/>
</ms_asmv2:requestedPrivileges>
</ms_asmv2:security>
</ms_asmv2:trustInfo>
</assembly>
应用程序指令清单标记仅与 EXE 相关,而与 DLL 无关。这是因为在创建进程期间 UAC 并不检查 DLL。要将此指令清单嵌入到本机应用程序中,请将清单文件复制到源文件所在的目录,并将该清单添加到资源文件中,以指定将嵌入该指令清单。如果上面的清单文件保存为 AdminApp.exe.manifest,则在 .rc 文件中使用以下几行命令可嵌入该清单。
#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST "AdminApp.exe.manifest"
令牌 COM 对象
在创建 COM 对象时,可以使用 CoCreateAsAdmin 标记来获取以“提升的权限”运行的 COM 对象。这在创建以提示的权限运行且在应用程序 UI 中显示的对象时非常有用。
还必须对 COM 对象进行其他两项设置,这样它才能使用完全权限令牌来运行。必须指定要在 UAC 提升对话框中出现的显示名称,还必须指定一个令牌来表明该对象可以使用提升的权限来创建。以下注册表项必须添加到对象的注册项中。
HKEY_LOCAL_MACHINESoftwareClassesCLSID
{CLSID}LocalizedString = <displayname>
HKEY_LOCAL_MACHINESoftwareClassesCLSID
{CLSID}ElevationEnabled = 1
如果 COM 对象未在 HKLM 中注册且这两项信息均未指定,则创建提升的 COM 对象的尝试将失败。如果该操作成功,并且调用是来自于使用经过滤的令牌运行的进程,用户将收到需要提升的提示,并会创建一个新进程作为 COM 对象的宿主。有关创建提升的 COM 对象的详细信息,请参阅在 msdn2.microsoft.com/ms679687.aspx 位置提供的关于 CoCreateAsAdmin 标记的 MSDN 文档。
提醒用户需要提升的权限
UAC 的另一个难题是,对于那些根本不了解以降低的权限运行的应用程序和以管理员身份运行的应用程序之间的区别的用户,如何告知他们需要提升权限。理解这种区别的关键是了解出现需要提升的提示时的标准化处理过程。提升过程始终是从用户单击其图标中带有盾牌图像的对象开始。如果启动某个应用程序会要求提升权限,您应看到其图标上有一个盾牌标记。应用程序中任何需要提升权限来运行的地方都应使用盾牌图像进行标记。
许多常用控件都已更新为允许向其添加盾牌标记。如果在所显示的设置中,单击某个按钮或链接会引发创建需要提升权限的新进程,则这种方式尤为有用。添加盾牌符号的代码非常简单。例如,标准按钮控件(PUSHBUTTON 和 DEFPUSHBUTTON)已进行了增强处理,无需设置 BS_ICON 或 BS_BITMAP 样式便可将图标与显示文本一同添加。
图 7按钮上的盾牌标记
要显示盾牌图标(请参见图 7),应调用下面的宏(在 commctrl.h 中定义),该宏可向按钮发送 SETSHIELD 消息:
Button_SetElevationRequiredState(hwndButton, true);
对于 Aero™ Wizard 按钮、Syslink 和 Hyperlink 控件以及 Commandlink 和 Command 按钮等,也可使用同样的盾牌显示方式。
不同安全环境中进程间的通信
Windows Vista 还包括一项称为“Windows 完整性机制”(WIM) 的身份验证技术。WIM 定义作为对象标签使用的、以进一步进行身份验证的安全级别:低、中、高和系统。由于 UAC 的存在,在同一桌面会话中,可能同时存在具有完全管理员权限的进程和以标准用户令牌运行的进程。UAC 使用 WIM 来阻止在权限级别不同的进程之间发送 Windows 消息。
用于创建桌面的令牌将以“中”完整性级别来运行 - 请参阅经过滤的管理员令牌的示例。这样,默认情况下所有对象都以“中”完整性级别来运行。当用户收到需要提升的提示并进行许可后,所生成的进程将创建为具有完全权限令牌,且其完整性级别为“高”。过去,存在一种称为“Shatter 攻击”的攻击形式,在这种攻击中,权限较低的 UI 向权限较高的 UI 发送消息,来驱动权限较高的进程。为了免受这种攻击,大多数 Windows 消息都不再从权限较低的进程向权限较高的进程发送,这是建立在完整性级别基础上来实现的。
还是有一些方式可以实现在完整性级别不同的进程间进行通信。可以使用共享内存或远程过程调用 (RPC)。请确保创建 RPC 入口点或 IPC 机制时,指定它可由以标准用户身份运行的进程进行连接。您需要在对象的安全描述符中对此进行指定。
低权限的 Internet Explorer
对于 WIM 和 UAC,值得一提的另一点是,它可启用 LowRights Internet Explorer®,这是一种利用“低”完整性级别在用户浏览 Web 时对其进行保护的设置。以“低”级别运行时,在系统中,进程可与之交互的目录和文件十分有限。如果您的应用程序是通过 Internet Explorer 运行并且使用了 ActiveX® 控件,您应该对其进行测试,以确保控件正常运行。有关详细信息,请参阅在 msdn.microsoft.com/library/en-us/ietechcol/dnwebgen/protectedmode.asp 位置提供的白皮书。
确保 Windows Vista 中 Windows 服务的安全
在 Windows Vista 中,可以通过很多新的方式来锁定服务。开发人员可以指定服务所需的权限级别,而且,服务可具有应用于其自身的安全标识符 (SID),该标识符可用来保护资源,使得只有该服务才能对这些资源进行写入操作。还可防止服务访问网络。有关详细信息,请参阅在 microsoft.com/whdc/system/vista/Vista_Services.mspx 位置提供的有关 Windows 服务的文档。
在 Windows Vista 中,仍然可以使用 sc.exe 来定义这些新的安全参数(请参见图 8)。权限是一个字符串,包含一列使用正斜杠 (/) 分隔的具体权限。 例如,要指定备份和还原权限,可将“权限”设置为 SeBackupPrivilege/SeRestorePrivilege。
Figure8Sc.exe 命令
命令 | 语法 | 目的 |
Privs | sc <服务器> privs [服务名称] [权限] | 为服务设置所需权限。 |
Qprivs | sc <服务器> qprivs [服务名称] buffersize | 查询某服务所需的权限。 |
Sidtype | sc <服务器> sidtype [服务名称] [类型] | 将服务 SID 应用到进程令牌。 |
Qsidtype | sc <服务器> qsidtype [服务名称] | 检索服务的 SID 的设置。 |
Sc.exe 也可用来获取指定服务的 SID。下面的命令可返回以 service name 指定其名称的服务的 SID。
Sc showsid [service name]
要以编程方式更改此信息,请使用名为 ChangeServiceConfig2 的新 API。要更改指定的服务权限,请使用以下参数值调用 ChangeServiceConfig2。(在权限中所做的更改将在下次服务启动时生效。)
dwInfo 参数应为 SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,lpInfo 缓冲区应指向 SERVICE_REQUIRED_PRIVILEGES_INFO 结构。此结构包含一个列出了所需权限的复合字符串 (Multistring)。
要强制该服务使用服务 SID,请以编程方式设置标志,使用以下参数值调用 ChangeServiceConfig2。(更改将在系统下次启动时生效。)dwInfo 参数应设置为 SERVICE_CONFIG_SERVICE_SID_INFO,lpInfo 缓冲区应设置为指向 SERVICE_SID_INFO 结构。此结构包括含有 SID 类型的单个 DWORD 成员。
对于服务所有者来说,以下两个相关的公共函数也非常有用:LookupAccountName 和 LookupAccountSID。LookupAccountName 接受 SID,返回相关联的服务名称,而 LookupAccountSID 接受服务名称,返回相关联的 SID。要查询这些设置的值,请使用 QueryServiceConfig2 API。
保护服务的另一种方法是限制其对网络的访问。要为服务配置防火墙限制,请使用 INetFwServiceRestriction 接口。
最后要注意,不要轻易公开可通过权限较低的进程连接的 RPC 接口。这些接口应经过仔细的设计和测试,以便做到对于可以用户身份执行的任务,不允许该用户提升权限。
常见的应用程序兼容性问题
在开发 UAC 时,我们的团队遇到的一个最大问题是 UAC 对于整个应用程序“生态系统”的严重影响。许多应用程序并没有设计由“用户”组的成员来运行的。Microsoft 花费了大量时间来了解常见的应用程序兼容性问题,并在 Windows Vista 中使用了可解决其中的某些问题的相关技术。在本文的最后一部分,我将介绍其中的一些问题,并简要说明如何加以解决。
UAC 包含的第一种应用程序兼容性技术名为“安装程序检测”。由于大多数安装程序都会将二进制文件写入 Program Files 目录,不可避免地会需要管理员权限。“安装程序检测”旨在扫描 EXE 文件的名称和资源,以确定某应用程序是否为安装程序。例如,如果某个可执行文件的名称或描述中包含字符串“install”或“setup”,该可执行文件将标记为安装程序。因此,在不存在应用程序指令清单的情况下,如果由不具有管理员权限的令牌启动名为 setup.exe 的应用程序,则将触发 UAC 权限提升。
UAC 所包含的另一项技术用于在运行时帮助解决文件和注册表问题,该技术被称为“文件和注册表虚拟化”。许多应用程序都将其日志文件或其配置写入 Program Files 或 Windows 目录。这些目录不允许标准用户对这些位置执行写入操作,因此这些写入操作一执行即会失败。为了解决这些问题,“文件虚拟化”接受写入到文件系统中的某些受保护位置的文件,并将写入操作重定向到用户的配置文件中,如果要写入的文件为二进制文件,则不会执行虚拟化。 注册表虚拟化对注册表执行同样的操作,将向 HKLMSoftware 写入的操作重定向到注册表中每个用户的位置。这些技术大大提高了过去开发的应用程序的兼容性。
需要特别注意的是,如果二进制文件具有应用程序指令清单或二进制文件是针对 64 位计算机而进行编译的,将禁用这些技术。 其原因是,这些技术实际上只是用于支持过去开发的那些在设计时未考虑到要以标准用户身份运行的应用程序。遗憾的是,这些技术并不能解决所有问题。下面,我们看看其他一些常见的问题。
安装时出现的问题
我们遇到的最常见的一个问题就是错误地标记 MSI 文件中的 CustomActions。许多 CustomActions 并没有指定无模拟标志 msidbCustomActionTypeNoImpersonate,从而导致 CustomAction 以“本地系统”身份运行。由于现在 Windows Vista 中的默认令牌与“用户”组成员的默认令牌类似,这些 MSI 文件会因 CustomActions 尝试执行需要管理员权限的操作而失败。通常,只需向 CustomActions 添加 msidbCustomActionTypeNoImpersonate 属性,便可解决该问题。
我们在测试中遇到的另一个典型问题与作为安装的一部分而启动的应用程序有关。通常,在安装结束时都会启动应用程序。不巧的是,由于用户提供了提升的凭据来执行安装,应用程序经常在错误的用户环境中启动,且应用程序会以提升的用户令牌来创建。对于解决此问题的建议就是创建 bootstrapper EXE。这个 bootstrapper 会将二进制文件解压缩到临时位置,然后运行另一个进程来启动安装过程(请参见图 9)。当启动安装过程的进程完成安装后,会将控制权返还给启动应用程序的解压缩程序。 这样,应用程序将以标准用户令牌运行。.
图 9使用 Bootstrapper 安装和执行应用程序
最后一个经常遇到的安装问题是在第一次运行应用程序时执行需要管理员权限的操作。这包括将二进制文件复制或更新到 Program Files 中、安装驱动程序或任何其他假定用户为管理员的操作。要找到这些问题的解决办法,最好的方式就是在安装应用程序时对其进行测试,并以标准用户身份来运行。
许多应用程序都具有某些用于更新应用程序二进制文件的机制。这些更新程序经常会因为以权限较低的令牌来运行并尝试覆盖 Program Files 目录中的二进制文件而导致运行失败。如果应用程序要更新其二进制文件,请将更新功能移到单独的进程中,并将其标记为需要管理员权限。另一种方法是使用 MSP 文件基于修补程序的签名来提升权限。为此,您需要填充原始 MSI 文件中的 MsiPatchCertificate 表。
典型的运行时应用程序问题
运行时最常见的一个错误就是执行不必要的检查来查看用户是否为管理员。例如,在许多游戏中,首先要检查的便是用户是否为管理员组的成员。游戏经常会向用户报告失败,但实际上它们从未调用过任何需要管理员权限的 API!很显然,这种问题应该避免。但为了便于复原,如果编写了需要管理员权限的应用程序且已在指令清单中进行标记,请添加此检查并在用户不具有管理员权限时退出。
许多应用程序开始执行后都会立即向 HKEY_CLASSES_ROOT (HKCR) 注册类。由于应用程序不再具有管理员权限,它们便不可以对此位置执行写入操作,该操作将失败。这些类的注册应该在安装时完成或重定向到用户的类根位置:HKCUSoftwareClasses。
另一个需要注意的潜在问题是,标准用户无法在“全局”命名空间创建对象。例如,标准用户无法在该命名空间中创建“命名管道”或“共享内存”。“全局”命名空间仅供维护跨会话状态的服务或应用程序使用。应改为使用“本地”命名空间。此空间对于标准用户而言是可以执行写操作的。
另一个问题是在打开对象时使用 MAX_ALLOWED。在 Windows XP 中,通常会假定认为运行应用程序的用户具有管理员身份。因此,在此环境中,当开发人员在打开文件和注册表项后使用 MAX_ALLOWED 时,不会有任何问题,因为他们始终以完全管理员权限打开这些文件。在 Windows Vista 中,情况有所不同。现在,在开发人员编写应用程序时,需要更加小心地来指定用户打开对象所需的权限。
总结
本文简要概述了 Windows Vista 的运行环境,您将来的开发工作需要适应这个新的运行环境。与以往相比,最基本的变化是,现在应用程序默认以权限较低的令牌来运行,因而不会对计算机或其他用户造成影响。有关 Windows Vista 中 UAC 相关主题的详细信息,请参阅前面提到的 MSDN 和 TechNet 杂志中的 UAC 白皮书。不要忘记的是,UAC 将给用户和系统管理员的工作带来更多的便利!
赞助商链接