Nie było to łatwe, ale znalazłem sposób na wykrycie, czy wirtualizacja UAC jest włączona. Wywołanie GetTokenInformation()
i przekazanie w TokenVirtualizationEnabled
jako klasie informacji zwróci, czy włączona jest wirtualizacja plików i rejestru. Oto funkcja C to zrobić:
// 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;
}
Nieco gorzej z P/Invoke, ale to jest tutaj, w tym P/Invoke nagłówki:
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;
}
Próbowałem tokarskich UAC wirtualizację na a wyłączyć za pomocą Menedżera zadań i sprawdzić, czy zwracany jest poprawny wynik. Włączanie i wyłączanie wirtualizacji można wykonać, dzwoniąc pod numer SetTokenInformation()
.
Firma Microsoft twierdzi, że planuje usunąć wirtualizację UAC w przyszłej wersji systemu Windows, a programy nie będą na niej polegać. Widziałem sugestię, aby ktoś zrobił oddzielny program, który nie jest UAC świadomy, aby przenieść pliki z VirtualStore do AppData, ale nie wiem, czy to dobre rozwiązanie, czy nie.
Czy ta metoda wykrywa, czy wirtualizacja jest włączona w ogóle, czy jest włączona dla określonego pliku/folderu? – Simpleton
Ogólnie. Jeśli wirtualizacja jest włączona dla procesu, wszystkie zapisy z tego procesu do folderu Program Files, folderu Windows i HKEY_LOCAL_MACHINE w rejestrze są przekierowywane (i kilka innych). Nie można włączyć niektórych z tych folderów, a nie innych. –
Ponowne odczytywanie początkowego pytania brzmi, jakbyś nie chciał wykrywać, czy wirtualizacja ma miejsce. Jeśli używasz 64-bitowego procesu, który jest znany z UAC, oznacza to, że wirtualizacja jest wyłączona. Aby uzyskać dostęp do danych umieszczonych w VirtualStore przez starszą wersję programu (z włączoną wirtualizacją), program może wyglądać w% LOCALAPPDATA% \ VirtualStore, aby sprawdzić, czy są tam jakieś jego dane. Jeśli tak, może przenieść dane w odpowiednie miejsce, w razie potrzeby scalając je. –