用户账户控制(UAC)的新安全范例介绍

用户账户控制(User Account Control,简称UAC)是Windows操作系统中的一项安全功能,它要求用户在执行需要提升权限的操作时进行确认。例如,写入注册表、修改系统受保护文件夹下的文件等。为了支持这种行为,应用程序需要在设计时就考虑到权限提升的需求。

通常,有两种方法可以实现这种行为:

  1. 在可执行文件中嵌入清单信息,表明应用程序从一开始就需要提升权限,并且无法在没有获得管理权限的情况下运行。以下是一个示例清单:
<?xml version="1.0" encoding="utf-8"?> <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <assemblyIdentity version="1.0.0.0" name="MyApplication" /> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> </asmv1:assembly>
  1. 将代码基础分为两部分:第一部分不需要提升权限,第二部分需要,然后从一个部分调用另一个部分。文章涵盖了一个案例,即调用应用程序是一个带有多个命令行参数的控制台程序:其中一些参数可以在普通模式下运行,而另一些则只能在具有管理权限的情况下运行。

通常,需要提升权限的程序部分是一个设置表单,它在写入Windows注册表时,核心功能不需要。另一个案例是一个控制台应用程序,其中核心功能需要提升权限,而一些辅助功能(如显示帮助信息)不需要。

代码

让声明这样一个控制台应用程序的骨架。入口点方法解析输入的命令行参数,并根据解析结果包装成对象来分支流程控制:

static void Main(string[] args) { // 将命令行参数包装到一个对象中 Core.Settings = ApplicationSettingsParser.Parse(args); if (Core.Settings.UsageFlag || (args.Length == 0)) { // 无需提升权限即可运行 PrintUsage(); } else if (!Core.Settings.EngageFlag) { // 以相同的参数运行,加上标志,说明主要动作执行 var info = new ProcessStartInfo( Assembly.GetEntryAssembly().Location, String.Join(" ", Enumerable.Concat(args, new[] { "--engage" })) ) { Verb = "runas", // 指示提升权限 }; var process = new Process { EnableRaisingEvents = true, // 启用 WaitForExit() StartInfo = info }; process.Start(); process.WaitForExit(); // 调用进程线程直到被调用进程退出 } else if (Core.Settings.EngageFlag) { // 在提升的权限下执行操作 Console.WriteLine("Elevated privileges gained={0}", Core.IsElevated); } }

以下是一个仅显示示例帮助信息的方法:

private static void PrintUsage() { Console.WriteLine("Usage: ElevatedPrivilegesOnDemand.exe --do [--engage] | -?"); }

解析方法被放置在一个专用的辅助类中:

public static ApplicationSettings Parse(IEnumerable args) { var settings = new ApplicationSettings(); foreach (var arg in args) { switch (arg) { case "-?": { // 停止进一步解析并返回唯一的标志 return new ApplicationSettings() { UsageFlag = true }; } case "--do": { settings.DoFlag = true; break; } case "--engage": { settings.EngageFlag = true; break; } } } return settings; }

它返回一个POCO对象来保持设置:

class ApplicationSettings { public ApplicationSettings() { // 默认设置设置 } public bool DoFlag { get; set; } public bool EngageFlag { get; set; } public bool UsageFlag { get; set; } }

内部单例设置实例附加到一个枢轴类。它还包含一个额外的辅助方法来检查获得提升的权限:

static class Core { public static bool IsElevated { get { return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); } } public static ApplicationSettings Settings { get; set; } }

实现细节

执行流程被分为两部分:第一部分是确定是否需要提升权限,这取决于是否指定了命令行参数。如果需要,将添加一个额外的参数并传递给同一个应用程序,但以管理员权限启动并重定向控制台输出。第二部分,核心功能在获得完全权限后执行。

现在,当不需要时,没有必要始终以提升的权限模式运行整个应用程序。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485