如何检测到Windows VirtualStore的文件重定向?

Sim*_*ton 10 c# virtualization uac virtualstore

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

此时,已采取措施确保有问题的应用程序现在作为支持UAC的64位进程运行,但是,这对解决将用户数据迁移到被认为是安全的虚拟化位置的问题几乎没有作用.

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

编辑:我发现几个网站详细说明了问题以及如何复制它,但没有包含修复方法.我确实找到了这个参考文件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

Jor*_*ner 14

这并不容易,但我发现了如何检测是否启用了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;
}
Run Code Online (Sandbox Code Playgroud)

使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

微软称他们计划在未来的Windows版本中删除UAC虚拟化,并且计划不依赖于现有版本.我看到有人建议制作一个不是UAC意识的单独程序将文件从VirtualStore移动到AppData,但我不知道这是不是一个好的解决方案.

  • 重读您的初始问题,听起来您不想检测虚拟化是否正在发生.如果您正在运行支持UAC的64位进程,那么您就知道已禁用虚拟化.要通过旧版本的程序(已启用虚拟化)访问VirtualStore中的数据,程序可以查看%LOCALAPPDATA%\ VirtualStore以查看其中是否存在任何数据.如果有,它可以将数据移动到正确的位置,如果需要合并它. (2认同)