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

标签 c# virtualization uac virtualstore

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

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

在解决此问题时,我发现在处理多个用户帐户时,已在位于 C:\Program Files(x86)\MyApp\Data 的旧路径中进行了一些更改,而同时,对位于 %localappdata%\VirtualStore\Programs\MyApp\Data 的 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

最佳答案

这并不容易,但我找到了如何检测 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 header :

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() 来启用和禁用虚拟化。 .

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

关于c# - 如何检测到 Windows VirtualStore 的文件重定向?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14005081/

相关文章:

c++ - Windows 7 中的 COleTemplateServer::RegisterAll() 会发生什么

c# - 序列化为 XML 时重命名类

c# - 使用泛型转换不同的用户输入类型

c# - 基于坐标c#突出显示pdf中的元素

c - 在 VMX 中启用 EPT 会因访客状态而导致登录失败

assembly - 为什么此 mov gs 指令会导致运行 Windows 7 操作系统的 VMWare Workstation 客户机出现故障?

file - Win32 - 获取将文件保存到C盘的权限

installation - 在具有 UAC 的系统中修复期间 WiX 安装条件失败

c# - 如何为 DirectoryEntry.Exists 提供凭据?

database - dockerize(容器化)数据库有意义吗?