How to detect file redirection to the Windows VirtualStore?

限于喜欢 提交于 2019-11-27 21:34:16

Wasn't easy, but I found how to detect whether UAC virtualization is enabled. Calling GetTokenInformation() and passing in TokenVirtualizationEnabled as the information class will return whether file and registry virtualization is enabled. Here is a C function to do it:

// 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;
}

A bit harder with P/Invoke, but here it is, including the P/Invoke headers:

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;
}

I tried turning UAC virtualization on and off with Task Manager and verified that the correct result is returned. Enabling and disabling virtualization can be done by calling SetTokenInformation().

Microsoft says they plan on removing UAC virtualization in a future Windows version and for programs to not rely on it existing. I saw a suggestion by someone to make a separate program that is not UAC aware to move files from the VirtualStore to AppData, but I don't know if that's a good solution or not.

FWIW, here is a version of the detection code in 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.

Sounds like you're trying to flag if you are "running" from the local app data path, in which case:

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)
);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!