技术开发 频道

VS与Win7共舞:UAC惹祸 如何进行安装程序检测?

  【IT168 专稿】 “快说,你是不是安装程序?”操作系统问。

  “我不是啊,长官。我虽然长得像,但是我真的不是安装程序啊!”,一个应用程序扮出一副可怜相,胆胆怯怯的回答道。

  “不是?那为什么你的程序名中含有Install?”,操作系统以怀疑的眼光盯着他,“所有程序名中含有Install的应用程序都是安装程序,都必须在执行的时候都向用户请求管理员权限!”

  “是是是,长官!”应用程序心中暗喜,操作系统主动给我机会让我请求管理员权限,我求之不得呢,用户早就厌烦UAC了,肯定直接点击“YES”了事啊,有了管理员权限,我就可以为所欲为啦,偷偷修改个首页先,哈哈哈哈~~~

  UAC又惹祸了

  随着Windows Vista引入UAC(User Access Control)机制,默认情况下,应用程序都运行在普通用户权限下。虽然微软出于良好的愿望而在Vista中引入UAC机制, 但是在Vista操作系统中,只要系统稍作改变,它就会频繁弹出对话框来寻求用户的许可,因此它成为了Vista中最受痛恨的一个功能。 虽然如此,Windows 7还是继承了这一机制并根据用户的反馈做了相应的改进。为了降低计算机系统的风险,UAC机制将执行应用程序的用户权限降低了,这就为那些在UAC机制出现之前所设计的应用程序的执行带来了兼容性的麻烦。这些旧有应用程序通常都假设以管理员权限运行,在Windows 7上,因为UAC的存在,这一假设不成立了,最终导致应用程序无法正常运行。一些应用程序确实是需要管理员权限才可以正常运行的,尤其是安装程序,他们需要向一些需要特殊权限的区域,比如“Program Files”或者是注册表的HKEY_LOCAL_MACHINE写入内容,这种情况它们会遇到访问拒绝的错误,或者是数据被UAC Virtualization重定向到其他位置而无法正确执行。

  为了解决这个问题,“聪明”的雷德蒙程序员们想出了一个办法:安装程序检测。从Windows Vista开始,当然也包括Windows 7,操作系统将采用一些启发式算法来判断应用程序是不是一个安装程序,也就是在执行的时候这个程序是否需要请求管理员权限,如果操作系统判断应用程序是一个安装程序,就会让它在执行的时候向用户请求获取管理员权限以便让应用程序正确的执行。

  操作系统是如何检测的?

  所有在Windows Vista之前开发的没有manifest(包括外部的和内部的)的32位应用程序都会进行这种启发式的安装程序检测。操作系统会假设这些应用程序是旧有的,他们都需要进行安装程序检测以确定这些应用程序是否管理员权限才能正常运行。面对这样的应用程序,操作系统的启发式安装检测通常会通过以下这些途径来判断一个32位应用程序是不是安装程序:

  • 文件名包含关键字:”install”, “setup”和”update”等等。

  • 在版本资源的以下字段内包含关键字:厂商(Vendor)、公司名(CompanyName)、产品名(ProductName)、文件说明(File Description)、初始文件名(Original Filename)、内部文件名(Internal Name)、导出名(Export Name)。

  • 在可执行文件的manifest内包含关键字。

  • 在链接到可执行文件的特定StringTable中包含关键字。

  • 在链接到可执行文件的资源文件数据包含关键属性。

  • 可执行文件包含特定的字节序列。

  如果找到了,操作系统会认为它需要管理员权限才可以正常运行。一个UAC保护盾的图标会覆盖在应用程序图标上,这就表示应用程序在启动的时候会请求管理员权限以便它可以正确执行。

  图1 安装程序检测

  开好车的就一定是好人吗?

  虽然这种启发式安装程序检测可以让一个旧有的安装程序能够直接在Windows Vista或者Windows 7上正常运行。但是,它本身也带来很多问题。一方面,一些不需要管理员权限就可以正常运行的应用程序,就因为文件名中含有了“setup”等相关的文字,比如,StockUpdater.exe,就被强行在运作的时候向用户询问请求管理员权限。这不仅给用户带来不便,同时也无形中降低了系统的安全性。甚至用户会因为担心系统受到损害而取消应用程序的运行。另一方面,一些恶意软件也会凭借这种机制的漏洞,将自己伪装成安装程序,从而轻松地,正大光明地获取管理员权限而给系统安全带来隐患。

  与此相反的是,很多自定义的安装程序,并不使用Microsoft Windows Installer(MSI)技术,并且没有按照启发式安装程序检测的规则命名,这样,操作系统就不会认为它是一个安装程序。由于启发式安装程序检测不承认它是安装程序,所以用户在运行这些应用程序的时候,它并不会自动请求管理员权限运行,这就可能导致应用程序在向一些敏感位置写入数据时遇到拒绝访问错误,安装过程半途而废。

  就像电影中的台词,“开好车的就一定是好人吗?”同样的,“姓Setup的就一定是安装程序吗?”

  想要你就说嘛

  既然微软的启发式安装程序检测这么弱智,那么我们只好自己麻烦一下,简单地为应用程序添加一个Manifest文件,告诉操作系统我们到底是不是安装程序,是否需要请求管理员权限。如果应用程序的源代码无法获得,我们只需要在应用程序的同一目录下为应用程序添加一个外部Manifest文件。这个Manifest文件的名字应该是应用程序文件名加上“.manifest”后缀。例如,StockUpdater.exe的Manifest文件的文件名应该是StockUpdater.exe.manifest。这个Manifest文件应该指明此应用程序在运行的时候是否需要申请管理员权限,或者是否在普通用户权限下就可以正常运行。

  一个典型的外部Manifest文件如下所示:

<?xmlversion="1.0"encoding="utf-8"?>
<asmv1:assemblymanifestVersion="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">
    
<assemblyIdentityversion="1.0.0.0"name="MyApplication.app"/>
    
<trustInfoxmlns="urn:schemas-microsoft-com:asm.v2">
        
<security>
            
<requestedPrivilegesxmlns="urn:schemas-microsoft-com:asm.v3">
                
<!-- UAC Manifest 选项
          
<requestedExecutionLevel  level="asInvoker" uiAccess="false" />
          
<requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
          
<requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
        
-->
        
        
<requestedExecutionLevellevel="asInvoker"uiAccess="false" />
              
</requestedPrivileges>
          
      
</security>
      
</trustInfo>
    
</asmv1:assembly>

 

  其中,requestedExecutionLevel属性就表示我们的应用程序正确执行所需要的权限。它有这样几个可选值:

  • asInvoker – 它表示应用程序需要跟创建者相同的权限运行。也就是跟Windows Explorer相同的权限运行,通常就是普通用户权限。这个应用程序不是安装程序并且不会被启发式安装程序检测错误地标记。

  • requireAdministrator – 它表示这个应用程序需要管理员权限才能正常运行。(它可能是一个安装程序。)

  • highestAvailable – 它表示这个应用程序应该以尽可能高的权限运行。如果当前用户是一个管理员用户,那么它就等同于requireAdministrator 。如果当前用户是普通用户,那么它会在运行的时候请求管理员权限。

  当我们为应用程序添加外部Manifest文件后,操作系统会根据Manifest文件中的定义为应用程序制定相应的UAC规则。例如,我们可以通过外部Manifest文件,为一个自定义的安装程序在执行时请求管理员权限。

  图2  自定义安装程序

  嵌入式Manifest

  如果我们可以获得应用程序的源代码,我们可以在Visual Studio 2010中为应用程序添加嵌入式的Manifest来完成相同的工作。在项目属性中,我们设置“Linker->Manifest File->UAC Execution Level”,就可以指定应用程序执行所需要的权限。

  图3 设置UAC执行权限等级

  同时,我们可以将Manifest文件作为资源嵌入到应用程序中,这样我们就只需要一个单独应用程序就可以完成所有工作了,而无需额外的一个Manifest文件。同样的,在项目属性“Manifest Tool->Input and Output->Embed Manifest”中设置就可以将Manifest文件嵌入到应用程序中。

  图4 嵌入式Manifest

  在默认情况下,Visual Studio 2010所创建的新项目,已经含有了嵌入式的Manifest。

  有了Manifest文件,应用程序就像有了身份证一样,操作系统不会再冤枉好人了。

  系列文章索引:

  VS2010与Win7共舞:UAC与数据重定向

  VS2010与Win7共舞:ETW自定义程序日志

  VS与Win7共舞:性能计数器进行性能分析

  VS2010与Windows7共舞:对库进行编程

  VS与Windows 7共舞:库(Library)

  VS2010与Win7共舞:响应Ribbon控件消息

  VS与Win7共舞:用XML文件定义Ribbon界面

  VS 2010与Windows7共舞:又见Ribbon

  VS2010与Win7共舞 :任务栏状态提示

  VS2010与Win7共舞 :任务栏缩略图

  VS2010与Windows 7共舞:Jumplist

0
相关文章