不容易,但我找到了如何检测是否启用了 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,但我不知道这是否是一个好的解决方案。