【问题标题】:How to detect file redirection to the Windows VirtualStore?如何检测到 Windows VirtualStore 的文件重定向?
【发布时间】:2012-12-22 17:44:36
【问题描述】:

自 Win Vista 发布以来,Microsoft 为作为 32 位进程运行的遗留应用程序引入了文件虚拟化。作为 Microsoft 的用户帐户控制 (UAC) 的一部分发布的任何旧版应用程序试图写入任何被认为受操作系统保护的位置都会被重定向到 VirtualStore。

此时,已采取措施确保相关应用程序现在作为可识别 UAC 的 64 位进程运行,但这对于解决将用户数据迁移到被认为安全的位置的问题几乎无济于事来自虚拟化。

在解决此问题时,我发现在处理多个用户帐户时,在位于 C:\Program Files(x86)\MyApp\Data 的旧路径中进行了一些更改,同时对位于 @987654326 的 VirtualStore 进行了更改@。问题是,我如何检测是否正在发生任何文件/文件夹虚拟化以及如何合并这两个位置?

编辑:我发现几个网站详细说明了问题以及如何复制它,但没有包含解决问题的方法。我确实找到了这个引用FILE_ATTRIBUTE_VIRTUAL,它定义了一个看起来很有希望的文件属性 - 我在某个地方找到了另一个引用,虽然我不记得在哪里,它指出这是 Windows 用来指示文件虚拟化正在发生的属性,并且标记重定向请求。

这些链接描述了这个问题:

http://www.c-sharpcorner.com/uploadfile/GemingLeader/windows-file-and-registry-virtualization/

http://www.codeproject.com/Articles/66275/Windows-Vista-File-and-Registry-Virtualization

http://download.microsoftvirtuallabs.com/download/8/a/7/8a71365b-4c80-4e60-8185-8f12f59bf1d4/UACDataRedirection.pdf

【问题讨论】:

    标签: c# virtualization uac virtualstore


    【解决方案1】:

    不容易,但我找到了如何检测是否启用了 UAC 虚拟化。调用GetTokenInformation() 并传入TokenVirtualizationEnabled 作为信息类将返回是否启用了文件和注册表虚拟化。这是一个 C 函数:

    // Gets whether the current process has UAC virtualization enabled.
    // Returns TRUE on success and FALSE on failure.
    BOOL GetVirtualizationEnabled(BOOL *enabled) {
        HANDLE token;
        DWORD tmpEnabled;
        DWORD returnLen;
        BOOL retVal = TRUE;
    
        if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
            return FALSE;
    
        if(!GetTokenInformation(token, TokenVirtualizationEnabled,
                &tmpEnabled, sizeof(tmpEnabled), &returnLen)) {
            retVal = FALSE;
            goto err;
        }
    
        *enabled = tmpEnabled;
    
    err:
        CloseHandle(token);
    
        return retVal;
    }
    

    使用 P/Invoke 有点困难,但在这里,包括 P/Invoke 标头:

    enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin,
        TokenElevationType,
        TokenLinkedToken,
        TokenElevation,
        TokenHasRestrictions,
        TokenAccessInformation,
        TokenVirtualizationAllowed,
        TokenVirtualizationEnabled,
        TokenIntegrityLevel,
        TokenUIAccess,
        TokenMandatoryPolicy,
        TokenLogonSid,
        MaxTokenInfoClass
    }
    
    public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
    public const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
    public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
    public const UInt32 TOKEN_DUPLICATE = 0x0002;
    public const UInt32 TOKEN_IMPERSONATE = 0x0004;
    public const UInt32 TOKEN_QUERY = 0x0008;
    public const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
    public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
    public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
    public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
    public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
    public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
    public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
        TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
        TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
        TOKEN_ADJUST_SESSIONID);
    
    [DllImport("advapi32.dll", SetLastError=true)]
    static extern bool GetTokenInformation(
        IntPtr TokenHandle,
        TOKEN_INFORMATION_CLASS TokenInformationClass,
        IntPtr TokenInformation,
        int TokenInformationLength,
        out uint ReturnLength);
    
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
        ref uint TokenInformation, uint TokenInformationLength);
    
    [DllImport("advapi32.dll", SetLastError=true)]
    static extern bool OpenProcessToken(IntPtr ProcessHandle,
        uint DesiredAccess, out IntPtr TokenHandle);
    
    [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool CloseHandle(IntPtr hObject);
    
    static bool TryGetVirtualizationEnabled(out bool enabled) {
        IntPtr processHandle = Process.GetCurrentProcess().Handle;
        IntPtr token;
        uint returnLen;
        object tmpEnabled = new uint();
    
        enabled = false;
        GCHandle handle = GCHandle.Alloc(tmpEnabled, GCHandleType.Pinned);
    
        try {
            if(!OpenProcessToken(processHandle, TOKEN_QUERY, out token))
                return false;
    
            try {
                if(!GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenVirtualizationEnabled,
                                        handle.AddrOfPinnedObject(), Marshal.SizeOf(typeof(uint)), out returnLen))
                    return false;
    
                enabled = (uint)tmpEnabled != 0;
            } finally {
                CloseHandle(token);
            }
        } finally {
            handle.Free();
        }
    
        return true;
    }
    

    我尝试使用任务管理器打开和关闭 UAC 虚拟化,并验证返回了正确的结果。可以通过调用SetTokenInformation() 来启用和禁用虚拟化。

    微软表示,他们计划在未来的 Windows 版本中移除 UAC 虚拟化,并让程序不再依赖现有的 UAC 虚拟化。我看到有人建议制作一个不支持 UAC 的单独程序来将文件从 VirtualStore 移动到 AppData,但我不知道这是否是一个好的解决方案。

    【讨论】:

    • 此方法是否检测虚拟化是在一般情况下启用还是为特定文件/文件夹启用?
    • 一般来说。如果在某个进程上启用了虚拟化,则从该进程到 Program Files 文件夹、Windows 文件夹和注册表中的 HKEY_LOCAL_MACHINE 的所有写入都将被重定向(以及其他几个)。不能为其中一些文件夹启用它,而其他文件夹则不能。
    • 重读您最初的问题,听起来您不想检测虚拟化是否正在发生。如果您正在运行可识别 UAC 的 64 位进程,则您知道虚拟化已禁用。要访问旧版本的程序(启用了虚拟化)放入 VirtualStore 的数据,该程序可以查看 %LOCALAPPDATA%\VirtualStore 以查看其中是否有任何数据。如果有,它可以将数据移动到正确的位置,并在需要时进行合并。
    • 这只是部分正确...在对 UAC 虚拟化问题进行故障排除时,我发现,即使应用程序进程在所需的清单(UAC 感知)上运行良好,但对通过 SMB 的 Windows 文件共享经常被重定向到 VirtualStore,因为 SMB 几乎总是被视为有限的系统进程。
    • 我曾希望能够确定重定向是否正在发生,以便通知用户危险 - 鼓励他们将数据移动到更理想的位置。虚拟化的问题在于它基于每个用户,这意味着虚拟化数据可以分布在多个用户帐户中;这本身就是一个问题。我真正需要的是一种方法来确定是否发生了这种重定向,并在理想情况下合并两个数据集。
    【解决方案2】:

    FWIW,这是Delphi中检测代码的一个版本:

    unit checkvirtual;
    
    interface
    uses windows;
    
    function GetVirtualizationEnabled(var enabled:Boolean):Boolean;
    
    implementation
    
    // Gets whether the current process has UAC virtualization enabled.
    // Returns TRUE on success and FALSE on failure.
    function GetVirtualizationEnabled(var enabled:Boolean):Boolean;
    var
      token:THandle;
      tmpEnabled:DWORD;
      returnLen:DWORD;
    begin
      result:=false;
      enabled:=false;
      if not(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token)) then exit;
      try
        if not(GetTokenInformation(token, TokenVirtualizationEnabled,
                  @tmpEnabled, sizeof(tmpEnabled), returnLen)) then exit;
    
        enabled:=tmpEnabled<>0;
        result:=true;
      finally
        CloseHandle(token);
      end;
    end;
    
    end.
    

    【讨论】:

      【解决方案3】:

      如果您从本地应用程序数据路径“运行”,听起来您正在尝试标记,在这种情况下:

      var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);  
      var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
      Console.WriteLine(
          "Current path is:{0}, AppData Path is {1}, Current is subpath of appdata path:{2}",
          currentPath, 
          appDataPath, 
          currentPath.StartsWith(appDataPath)
      );
      

      【讨论】:

      • 感谢您的回复!我正在寻找一种解决方案,不仅可以感知 UAC,还可以主动检测文件重定向或虚拟化是否正在发生。如果可能的话,我希望能够检测到进程当前是否正在运行的天气——因为这是一个基于网络的应用程序。在这一点上,我一直无法找到任何方法来确定文件重定向到 VirtualStore 是否已经发生或曾经发生过;没有通过每个用户配置文件搜索 VirtualStore 数据/文件,我想不出其他方法来识别 VirtualStore 数据。
      • @Simpleton 啊,我明白了 - 抱歉,我对 VirtualStore 的详细信息不够了解,无法给出特定的答案;应用程序启动后,此简介只会让您知道。
      猜你喜欢
      • 2013-08-06
      • 2012-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多