Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629/* * PROJECT: ReactOS API Tests * LICENSE: GPLv2+ - See COPYING in the top level directory * PURPOSE: Test for NtLoadKey and NtUnloadKey * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) */
#include "precomp.h"
/* See xdk/cmtypes.h */#define REG_CREATED_NEW_KEY 1#define REG_OPENED_EXISTING_KEY 2
#define REG_FORCE_UNLOAD 1
#if 1
#define NDEBUG #include <debug.h>
#else
#define DPRINT(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__); #define DPRINT1(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
#endif
static NTSTATUS (NTAPI *pNtUnloadKey2)(POBJECT_ATTRIBUTES, ULONG);
static BOOLEANRetrieveCurrentModuleNTDirectory( OUT PUNICODE_STRING NtPath){ WCHAR ModulePath[MAX_PATH]; PWSTR PathSep;
/* Retrieve the current path where the test is running */ GetModuleFileNameW(NULL, ModulePath, _countof(ModulePath)); PathSep = wcsrchr(ModulePath, L'\\'); if (!PathSep) PathSep = ModulePath + wcslen(ModulePath); *PathSep = UNICODE_NULL;
/* Convert the path to NT format and work with it for now on */ return RtlDosPathNameToNtPathName_U(ModulePath, NtPath, NULL, NULL);}
static NTSTATUSCreateRegKey( OUT PHANDLE KeyHandle, IN HANDLE RootKey OPTIONAL, IN PUNICODE_STRING KeyName, IN ULONG CreateOptions, OUT PULONG Disposition OPTIONAL){ OBJECT_ATTRIBUTES ObjectAttributes;
InitializeObjectAttributes(&ObjectAttributes, KeyName, OBJ_CASE_INSENSITIVE, RootKey, NULL); return NtCreateKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, CreateOptions, Disposition);}
static NTSTATUSCreateProtoHive( OUT PHANDLE KeyHandle){ NTSTATUS Status; UNICODE_STRING KeyName;
RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV"); Status = CreateRegKey(KeyHandle, NULL, &KeyName, REG_OPTION_NON_VOLATILE, NULL); if (!NT_SUCCESS(Status)) return Status;
NtFlushKey(KeyHandle); return Status;}
static VOIDDestroyProtoHive( IN HANDLE KeyHandle){ NtDeleteKey(KeyHandle); NtClose(KeyHandle);}
static NTSTATUSOpenDirectoryByHandleOrPath( OUT PHANDLE RootPathHandle, IN HANDLE RootDirectory OPTIONAL, IN PUNICODE_STRING RootPath OPTIONAL){ NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock;
*RootPathHandle = NULL;
/* * RootDirectory and RootPath cannot be either both NULL * or both non-NULL, when being specified. */ if ((!RootDirectory && !RootPath) || ( RootDirectory && RootPath)) { return STATUS_INVALID_PARAMETER; }
if (!RootDirectory && RootPath) { /* Open the root directory path */ InitializeObjectAttributes(&ObjectAttributes, RootPath, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(RootPathHandle, // FILE_TRAVERSE is needed to be able to use the handle as RootDirectory for future InitializeObjectAttributes calls. FILE_LIST_DIRECTORY | FILE_ADD_FILE /* | FILE_ADD_SUBDIRECTORY */ | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE /* | FILE_OPEN_FOR_BACKUP_INTENT */); if (!NT_SUCCESS(Status)) { DPRINT1("NtOpenFile(%wZ) failed, Status 0x%08lx\n", RootPath, Status); return Status; }
/* Mark the handle as being opened locally */ *RootPathHandle = (HANDLE)((ULONG_PTR)*RootPathHandle | 1); } else if (RootDirectory && !RootPath) { *RootPathHandle = RootDirectory; } // No other cases possible
return STATUS_SUCCESS;}
/* * Should be called under privileges */static NTSTATUSCreateRegistryFile( IN HANDLE RootDirectory OPTIONAL, IN PUNICODE_STRING RootPath OPTIONAL, IN PCWSTR RegistryKey, IN HANDLE ProtoKeyHandle){ NTSTATUS Status; HANDLE RootPathHandle, FileHandle; UNICODE_STRING FileName; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock;
/* Open the root directory */ Status = OpenDirectoryByHandleOrPath(&RootPathHandle, RootDirectory, RootPath); if (!NT_SUCCESS(Status)) { DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status); return Status; }
/* Create the file */ RtlInitUnicodeString(&FileName, RegistryKey); InitializeObjectAttributes(&ObjectAttributes, &FileName, OBJ_CASE_INSENSITIVE, (HANDLE)((ULONG_PTR)RootPathHandle & ~1), // Remove the opened-locally flag NULL); Status = NtCreateFile(&FileHandle, FILE_GENERIC_WRITE /* | DELETE */, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL /* | FILE_FLAG_DELETE_ON_CLOSE */, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, NULL, 0); if (!NT_SUCCESS(Status)) { DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName, Status); goto Cleanup; }
/* Save the selected hive into the file */ Status = NtSaveKeyEx(ProtoKeyHandle, FileHandle, REG_LATEST_FORMAT); if (!NT_SUCCESS(Status)) { DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName, Status); }
/* Close the file, the root directory (if opened locally), and return */ NtClose(FileHandle);Cleanup: if ((ULONG_PTR)RootPathHandle & 1) NtClose((HANDLE)((ULONG_PTR)RootPathHandle & ~1)); return Status;}
/* * Should be called under privileges */static NTSTATUSMyDeleteFile( IN HANDLE RootDirectory OPTIONAL, IN PUNICODE_STRING RootPath OPTIONAL, IN PCWSTR FileName, IN BOOLEAN ForceDelete) // ForceDelete can be used to delete read-only files{ NTSTATUS Status; HANDLE RootPathHandle; UNICODE_STRING NtPath; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE FileHandle; FILE_DISPOSITION_INFORMATION FileDispInfo; BOOLEAN RetryOnce = FALSE;
/* Open the root directory */ Status = OpenDirectoryByHandleOrPath(&RootPathHandle, RootDirectory, RootPath); if (!NT_SUCCESS(Status)) { DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status); return Status; }
/* Open the directory name that was passed in */ RtlInitUnicodeString(&NtPath, FileName); InitializeObjectAttributes(&ObjectAttributes, &NtPath, OBJ_CASE_INSENSITIVE, RootPathHandle, NULL);
Retry: /* We go back there once if RetryOnce == TRUE */ Status = NtOpenFile(&FileHandle, DELETE | FILE_READ_ATTRIBUTES | (RetryOnce ? FILE_WRITE_ATTRIBUTES : 0), &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(Status)) { DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status); return Status; }
if (RetryOnce) { FILE_BASIC_INFORMATION FileInformation;
Status = NtQueryInformationFile(FileHandle, &IoStatusBlock, &FileInformation, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation); if (!NT_SUCCESS(Status)) { DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status); NtClose(FileHandle); return Status; }
FileInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL; Status = NtSetInformationFile(FileHandle, &IoStatusBlock, &FileInformation, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation); NtClose(FileHandle); if (!NT_SUCCESS(Status)) { DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status); return Status; } }
/* Ask for the file to be deleted */ FileDispInfo.DeleteFile = TRUE; Status = NtSetInformationFile(FileHandle, &IoStatusBlock, &FileDispInfo, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation); NtClose(FileHandle);
if (!NT_SUCCESS(Status)) DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName, Status);
// FIXME: Check the precise value of Status! if (!NT_SUCCESS(Status) && ForceDelete && !RetryOnce) { /* Retry once */ RetryOnce = TRUE; goto Retry; }
/* Return result to the caller */ return Status;}
/* * Should be called under privileges */static NTSTATUSConnectRegistry( IN HANDLE RootKey OPTIONAL, IN PCWSTR RegMountPoint, IN HANDLE RootDirectory OPTIONAL, IN PUNICODE_STRING RootPath OPTIONAL, IN PCWSTR RegistryKey){ NTSTATUS Status; HANDLE RootPathHandle; UNICODE_STRING KeyName, FileName; OBJECT_ATTRIBUTES KeyObjectAttributes; OBJECT_ATTRIBUTES FileObjectAttributes;
/* Open the root directory */ Status = OpenDirectoryByHandleOrPath(&RootPathHandle, RootDirectory, RootPath); if (!NT_SUCCESS(Status)) { DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status); return Status; }
RtlInitUnicodeString(&KeyName, RegMountPoint); InitializeObjectAttributes(&KeyObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, RootKey, NULL);
RtlInitUnicodeString(&FileName, RegistryKey); InitializeObjectAttributes(&FileObjectAttributes, &FileName, OBJ_CASE_INSENSITIVE, (HANDLE)((ULONG_PTR)RootPathHandle & ~1), // Remove the opened-locally flag NULL);
/* Mount the registry hive in the registry namespace */ Status = NtLoadKey(&KeyObjectAttributes, &FileObjectAttributes);
/* Close the root directory (if opened locally), and return */ if ((ULONG_PTR)RootPathHandle & 1) NtClose((HANDLE)((ULONG_PTR)RootPathHandle & ~1)); return Status;}
/* * Should be called under privileges */static NTSTATUSDisconnectRegistry( IN HANDLE RootKey OPTIONAL, IN PCWSTR RegMountPoint, IN ULONG Flags){ UNICODE_STRING KeyName; OBJECT_ATTRIBUTES ObjectAttributes;
RtlInitUnicodeString(&KeyName, RegMountPoint); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, RootKey, NULL); if (!pNtUnloadKey2) { win_skip("NtUnloadKey2 unavailable, using NtUnloadKey. Flags %lu\n", Flags); return NtUnloadKey(&ObjectAttributes); } return pNtUnloadKey2(&ObjectAttributes, Flags);}
START_TEST(NtLoadUnloadKey){ typedef struct _HIVE_LIST_ENTRY { PCWSTR HiveName; PCWSTR RegMountPoint; } HIVE_LIST_ENTRY;
static const HIVE_LIST_ENTRY RegistryHives[] = { { L"TestHive1", L"\\Registry\\Machine\\TestHive1" }, { L"TestHive2", L"\\Registry\\Machine\\TestHive2" }, };
NTSTATUS Status; UNICODE_STRING NtTestPath; UNICODE_STRING KeyName; HANDLE KeyHandle; ULONG Disposition; UINT i; BOOLEAN PrivilegeSet[2] = {FALSE, FALSE}; WCHAR PathBuffer[MAX_PATH];
pNtUnloadKey2 = (PVOID)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtUnloadKey2");
/* Retrieve our current directory */ RetrieveCurrentModuleNTDirectory(&NtTestPath);
/* Acquire restore privilege */ Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]); if (!NT_SUCCESS(Status)) { skip("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status); /* Exit prematurely here.... */ // goto Cleanup; RtlFreeUnicodeString(&NtTestPath); return; }
/* Acquire backup privilege */ Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]); if (!NT_SUCCESS(Status)) { skip("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status); RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]); /* Exit prematurely here.... */ // goto Cleanup; RtlFreeUnicodeString(&NtTestPath); return; }
/* Create the template proto-hive */ Status = CreateProtoHive(&KeyHandle); if (!NT_SUCCESS(Status)) { skip("CreateProtoHive() failed to create the proto-hive; Status 0x%08lx\n", Status); goto Cleanup; }
/* Create two registry hive files from it */ for (i = 0; i < _countof(RegistryHives); ++i) { Status = CreateRegistryFile(NULL, &NtTestPath, RegistryHives[i].HiveName, KeyHandle); if (!NT_SUCCESS(Status)) { DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status); /* Exit prematurely here.... */ break; } }
/* That is now done, remove the proto-hive */ DestroyProtoHive(KeyHandle);
/* Exit prematurely here if we failed */ if (!NT_SUCCESS(Status)) goto Cleanup;
/***********************************************************************************************/
/* Now, mount the first hive */ Status = ConnectRegistry(NULL, RegistryHives[0].RegMountPoint, NULL, &NtTestPath, RegistryHives[0].HiveName); if (!NT_SUCCESS(Status)) { DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n", &NtTestPath, RegistryHives[0].HiveName, RegistryHives[0].RegMountPoint, Status); }
/* Create or open a key inside the mounted hive */ StringCchPrintfW(PathBuffer, _countof(PathBuffer), L"%s\\%s", RegistryHives[0].RegMountPoint, L"MyKey_1"); RtlInitUnicodeString(&KeyName, PathBuffer);
KeyHandle = NULL; Status = CreateRegKey(&KeyHandle, NULL, &KeyName, REG_OPTION_NON_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName, Status); } else { DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n", &KeyName, Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open", Status); }
/* The key handle must be valid here */ Status = NtFlushKey(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/* Attempt to unmount the hive, with the handle key still opened */ Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, 0); // Same as NtUnloadKey(&ObjectAttributes); DPRINT1("Unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed"); ok_ntstatus(Status, STATUS_CANNOT_DELETE);
/* The key handle should still be valid here */ Status = NtFlushKey(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/* Force-unmount the hive, with the handle key still opened */ Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, REG_FORCE_UNLOAD); DPRINT1("Force-unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed"); ok_hex(Status, STATUS_SUCCESS);
/* The key handle should not be valid anymore */ Status = NtFlushKey(KeyHandle); if (Status != STATUS_KEY_DELETED /* Win2k3 */ && Status != STATUS_HIVE_UNLOADED /* Win7+ */) { ok_ntstatus(Status, STATUS_KEY_DELETED); }
/* The key handle should not be valid anymore */ Status = NtDeleteKey(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/* Close by principle the handle, but should this fail? */ Status = NtClose(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/***********************************************************************************************/
/* Now, mount the first hive, again */ Status = ConnectRegistry(NULL, RegistryHives[0].RegMountPoint, NULL, &NtTestPath, RegistryHives[0].HiveName); if (!NT_SUCCESS(Status)) { DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n", &NtTestPath, RegistryHives[0].HiveName, RegistryHives[0].RegMountPoint, Status); }
/* Create or open a key inside the mounted hive */ StringCchPrintfW(PathBuffer, _countof(PathBuffer), L"%s\\%s", RegistryHives[0].RegMountPoint, L"MyKey_2"); RtlInitUnicodeString(&KeyName, PathBuffer);
KeyHandle = NULL; Status = CreateRegKey(&KeyHandle, NULL, &KeyName, REG_OPTION_NON_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName, Status); } else { DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n", &KeyName, Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open", Status); }
/* The key handle must be valid here */ Status = NtFlushKey(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/* Delete the key, this should succeed */ Status = NtDeleteKey(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/* Close the handle, this should succeed */ Status = NtClose(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);
/* Attempt to unmount the hive (no forcing), this should succeed */ Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, 0); // Same as NtUnloadKey(&ObjectAttributes); DPRINT1("Unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed"); ok_ntstatus(Status, STATUS_SUCCESS);
/* Force-unmount the hive (it is already unmounted), this should fail */ Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, REG_FORCE_UNLOAD); DPRINT1("Force-unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed"); ok_hex(Status, STATUS_INVALID_PARAMETER);
#if 0 /* Close by principle the handle, but should this fail? */ Status = NtClose(KeyHandle); ok_ntstatus(Status, STATUS_SUCCESS);#endif
/***********************************************************************************************/
Cleanup:
/* Destroy the hive files */ for (i = 0; i < _countof(RegistryHives); ++i) { Status = MyDeleteFile(NULL, &NtTestPath, RegistryHives[i].HiveName, TRUE); if (!NT_SUCCESS(Status)) DPRINT1("MyDeleteFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status); }
/* Remove restore and backup privileges */ RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]); RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
RtlFreeUnicodeString(&NtTestPath);}