Vista/Window7上UAC相关问题/Manifest/WINSXS/PCA

来源:互联网 发布:数据查询网站 编辑:程序博客网 时间:2024/05/18 01:05

Vista/Win7/Windows Server 2008,加入了UAC功能,这就给开发带来了很多问题,总结了一下碰到的问题和解决办法。
1)权限提升之前,即使是管理员权限帐号(built-in Administrator除外)登陆,也不能向根目录,C:/windows, C:/program files等目录下写文件。
   还有也不能访问其他用户的私有文件夹,比如其他用户的Desktop文件夹等,原因是在没有提升权限之前,当前进程仍然只持有普通用户权限的access token,只有提升权限之后,才真正具有管理员权限的token.

2)有时候执行一个安装程序,UAC的权限提升询问对话框(就是屏幕变暗那个)就会弹出来,Windows怎么知道这个程序需要权限提升才能执行呢?
a) 编译的时候加入manifest:<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
   反过来,如果不希望提升,就指定为level="asInvoker"。下面会谈到,某些时候Windows也会误认为你的程序需要提升权限,本来是不需要。
   但是这样有可能在WindowXP SP2造成程序崩溃的问题。
   产生这个问题,目前已知的原因:手动编辑 manifest 文件导致的;使用 Visual Studio 2005 自动产生 manifest 文件导致的。
   解决办法:http://support.microsoft.com/kb/921337/en-us
b) 如果不包含manifest文件,或者没有指定requestedExecutionLevel,那么将会启用文件/注册表虚拟机制。
    Windows使用虚拟机制来帮助保护文件和注册表。传统的windows程序经常会在系统目录或者HKEY_LOCAL_MACHINE\SOFTWARE注册表项下写文件,但是vista之后,这些系统目录或者注册表项默认是受到保护的,普通权限的用户并没有修改权限。为了兼容这些所谓的遗留(legacy application)的windows程序,不至于在vista/win7上这些程序不能使用,vista之后才用虚拟机制来重定向这些文件/注册表项目到用户目录/注册表项。
-(%programfiles%, %ProgramData%, %SystemRoot%) 重定向到 C:\Users\User_name\AppData\Local\VirtualStore\Program Files\Application_name
-HKEY_LOCAL_MACHINE\SOFTWARE重定向到HKEY_CURRENT_USER\Software\Classes\VirtualStore
File and registry virtualization helps users who have restricted access to the registry and to the file system write to these protected areas. Virtualization creates a "per user" copy and then redirects successive data operations. For example, assume that an application is running under a Limited User Account or under accounts that require User Account Control permissions. When this application writes to a system location, such as to the %programfiles% folder, Windows Vista and Windows 7 redirect write operations and read operations to a user-specific location in the user’s profile folder (%localappdata%\VirtualStore). By default, this location is C:\Users\User_name\AppData\Local\VirtualStore\Program Files\Application_name. Registry virtualization works similarly but applies to registry keys under the HKEY_LOCAL_MACHINE\SOFTWARE subtree. The keys and data under this subtree are redirected to the HKEY_CURRENT_USER\Software\Classes\VirtualStore subkey.
    但是如果已经在manifest文件中指定了requestedExecutionLevel,那么windows是不会为这个程序启用虚拟机制的,这样如果这个程序修改受保护的文件/注册表项,就会直接失败。
    所以,开发vista/win7上的应用程序,应该注意不要在上述系统目录,注册表项中写入文件。当然,一个变通的办法是,可以更改这些受保护的文件,目录等,将modify权限赋予users组或者everyone用户。

c) 程序名字或者文件说明里面包含了setup, install等单词,Windows就会把它当作一个安装程序,认为需要提升权限了。
   很容易证明,随便找个EXE,把名字改成setup.exe,再去执行,看看会出现什么。这个时候,如果本来不需要提升,那就需要manifest了。

3)CreateProcessWithLogonW这个API,本来可以利用它来进行用户切换。
比如为了安装软件,通常需要管理员权限用户登录,
管理员可预先设定好管理权限用户密码,保存在安装包中,发给普通用户,
普通用户登陆执行安装包,通过这个API就可以切换到管理员权限,执行安装。这样就省事多了。
但是UAC下,任何调用 CreateProcess系列API来启动一个需要提升权限的程序(例如安装程序),这个API就会失败,返回ERROR_ELEVATION_REQUIRED(740)。
解决办法,写个壳程序,它不需要权限提升,CreateProcessWithLogonW切换用户并启动这个壳程序,
然后由这个壳程序调用ShellExecute来启动目标程序。
当然,调用ShellExecute提升权限的时候,还是会弹出UAC的权限提升询问对话框,否则就等于绕过UAC了。

4)RUN/RUNONCE注册表项
已知:(参考http://msdn2.microsoft.com/en-us/library/bb325654.aspx)
a) UAC下,通过Startup菜单或者RUN注册表项启动的应用程序,如果此程序需要权限提升,将会被UAC阻止(Block)。
    这个时候,右下角的任务栏上会出现个图标。
    类似上面3)的办法,先启动个不需要权限提升的壳程序,这个壳程序再启动目标程序,可以解决这个问题。

b) UAC下,写在RUNONCE注册表的项目,如果用普通用户(非管理员用户)登陆,不会被执行。
好像没什么解决办法。

5)UAC使用UIPI(User Interface Privilage Intergrity)技术
来防止低权限(Lower Integrity)的进程通过向其他已提升权限的进程发送消息的方式来进行一些可能危害系统安全的操作。
但是如果我们需要这样做怎么办呢?
Vista之后提供了一个新的API, 提升过权限的进程调用它可以打开这个限制,允许接受从低安全等级的进程发送过来的消息。
API是ChangeWindowMessageFilter,有2个参数,消息代码和操作类型(接受还是拒绝)。
参考:http://msdn2.microsoft.com/en-us/library/ms632675(VS.85).aspx

 

 PCA
Program Compatibility Assistant (PCA)/程序兼容性助手http://msdn.microsoft.com/en-us/library/bb756937.aspx



使用Visual Studio进行开发的时候,默认是把Manifest文件嵌入到exe或者dll文件中的。
随Visual Studio提供一个工具mt.exe,可以用来把Manifest文件嵌入到exe或者dll文件中,或者反之,从文件中导出Manifest。
因此可以使用mt.exe来查看manifest。
exe文件:mt.exe -inputresource:<exe-file>;#1 -out:<manifest-file>
dll文件:mt.exe -inputresource:<dll-file>;#2 -out:<manifest-file>


和Manifest相关的一个技术是Side-by-Side Assembly(SxS),这是微软在Visual Studio 2005中引入的技术,用来解决Windows平台上的DLL Hell问题。
http://msdn.microsoft.com/en-us/library/aa376307%28VS.85%29.aspx
例如,使用VS2005生成exe中嵌入以下的manifest文件:

  <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> - <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">- <dependency>- <dependentAssembly>  <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b" />   </dependentAssembly>  </dependency>- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">- <security>- <requestedPrivileges>  <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />   </requestedPrivileges>  </security>  </trustInfo>  </assembly>


winsxs下有2个相关目录:
1)存放manifest文件
%WINDIR%/WinSxS/Manifests/x86_microsoft.vc80.crt_<版本>.manifest,该清单用来标识此程序集并列出其内容(属于此程序集的 DLL)
2)程序集
%WINDIR%/WinSxS/Manifests/x86_microsoft.vc80.crt_<版本>目录下。

这样程序启动时,需要到winsxs目录下去根据manifest中的版本信息加载相应的WinCRT。

并行程序集还可以使用发行者配置文件(也称为策略文件),以便在全局上重定向应用程序和程序集,使其从使用并行程序集的一个版本变为使用该程序集的另一版本。
Windows XP中该文件在%WINDIR%/WinSxS/Policies/下面。
Windows Vista以后在%WINDIR%/winsxs/Manifests/下面。
文件名是x86_policy.8.0.microsoft.vc80.crt_<版本>。
例如,在一台没有安装上面exe的manifest中记载版本(8.0.50608.0)的CRTWindowsVista系统上,也能正常运行此exe。
原因是在已经安装的CRT的策略文件中有如下记载:

  <assemblyIdentity type="win32-policy" name="policy.8.0.Microsoft.VC80.CRT" version="8.0.50727.5592" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b" /> - <dependency>- <dependentAssembly>  <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b" />   <bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0" newVersion="8.0.50727.5592" />   <bindingRedirect oldVersion="8.0.50727.42-8.0.50727.5592" newVersion="8.0.50727.5592" />   </dependentAssembly>  </dependency>


以上策略指定,对于需要此程序集8.0.41204.256-8.0.50608.0版的任何应用程序或程序集,都应改用此程序集的8.0.50727.5592版,此版本是安装在系统上的当前版本。
所以程序会改为加载8.0.50727.5592的CRT,也就能够正常启动了。

也可以将 CRT 程序集作为私有并行程序集,安装在应用程序的本地文件夹中。如果操作系统未能找到 CRT 或作为共享程序集的任何其他程序集,它会开始将该程序集作为私有程序集来查找。它将按以下顺序搜索私有程序集:
1.在应用程序本地文件夹中查找名为 <assemblyName>.manifest 的清单文件。在此示例中,加载程序尝试在exe所在的文件夹中查找 Microsoft.VC80.CRT.manifest。如果找到该清单,加载程序将从应用程序文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。

2.尝试在exe本地文件夹中打开文件夹 <assemblyName>,如果存在此文件夹,则从中加载清单文件 <assemblyName>.manifest。如果找到该清单,加载程序将从 <assemblyName> 文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。

因此对于VS2005,可以到
<VS2005安装目录>/VC/redist/x86/Microsoft.VC80.CRT目录下,把以下4个文件拷贝出来,和exe一起发布。注意不能只是拷贝dll文件,必须也拷贝manifest文件。
Microsoft.VC80.CRT.manifest
msvcm80.dll
msvcp80.dll
msvcr80.dll

 

另外,VS2010中似乎已经取消了使用winsxs,至少默认是不用的。

可以参考http://blogs.msdn.com/b/vcblog/archive/2008/10/28/visual-studio-2010-ctp-released.aspx

New deployment model for Visual C++ Libraries (changed to not use Windows SxS configuration)

原创粉丝点击