diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /general/registry/regfltr/sys | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'general/registry/regfltr/sys')
| -rw-r--r-- | general/registry/regfltr/sys/capture.c | 620 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/context.c | 582 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/driver.c | 468 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/multialt.c | 776 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/post.c | 768 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/pre.c | 799 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/regfltr.c | 871 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/regfltr.h | 488 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/regfltr.rc | 11 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/regfltr.vcxproj | 214 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/regfltr.vcxproj.Filters | 61 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/txr.c | 784 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/txrutil.c | 283 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/util.c | 304 | ||||
| -rw-r--r-- | general/registry/regfltr/sys/version.c | 611 |
15 files changed, 7640 insertions, 0 deletions
diff --git a/general/registry/regfltr/sys/capture.c b/general/registry/regfltr/sys/capture.c new file mode 100644 index 00000000..7b350e73 --- /dev/null +++ b/general/registry/regfltr/sys/capture.c @@ -0,0 +1,620 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + capture.c + +Abstract: + + This file contains + 1. Documentation for what parameters need to be captured + 2. A simple sample showing how to capture the parameters for + SetValueKey and DeleteValueKey operations. + 3. Helper routines for capturing buffers and UNICODE_STRINGs. + +Environment: + + Kernel mode only + +--*/ + + +#include "regfltr.h" + + +/*++ + + Probing and Capturing Parameters + + This section defines how registry filtering drivers should handle input + parameters. + + + I. Probed Parameters + + All members in related registry structures (e.g. REG_CREATE_KEY_INFORMATION, + REG_SAVE_KEY_INFORMATION) and all structures and buffers pointed to by + their buffers are already probed by registry. Parameters are probed only + when ExGetPreviousMode indicates that the previous mode was user mode. If + the previous mode is kernel mode (i.e. KernelMode. is returned by + ExGetPreviousMode), all parameters are considered valid and no probing is + done in this case. + + + II. Captured parameters + + Most but not all members in related registry structures are captured. + If the previous mode is kernel mode, all parameters are considered valid + and no capturing is done in this case. If the previous mode is user mode, + all parameters are probed but not necessarily captured. Since this impacts + how registry filtering drivers should handle input parameters, we outline + below exactly which parameters are captured. It is important to note that + some members currently probed but not captured by the OS may be captured + in the future. On the other hand registry filtering drivers can rely on + the fact that parameters currently captured will be captured in all future + OS releases. + + * Note: As of Windows 8, all structures except output buffers are fully + captured. + + 1. Fully captured structures: + + Note: A fully captured structure is a captured structure where all + structures and buffers pointed to by its members are also fully + captured. + + For example: the UNICODE_STRING structure pointed to by the + CompleteName member in the fully captured REG_CREATE_KEY_INFORMATION + structure is also captured (including the Buffer member of the + UNICODE_STRING). + + List of fully captured structures (in Windows 7): + + REG_CREATE_KEY_INFORMATION (** incorrect) + REG_CREATE_KEY_INFORMATION_V1 (** incorrect) + REG_DELETE_KEY_INFORMATION + REG_FLUSH_KEY_INFORMATION + REG_KEY_HANDLE_CLOSE_INFORMATION + REG_OPEN_KEY_INFORMATION + REG_OPEN_KEY_INFORMATION_V1 + REG_QUERY_KEY_SECURITY_INFORMATION (** added) + REG_REPLACE_KEY_INFORMATION + REG_RESTORE_KEY_INFORMATION + REG_SAVE_KEY_INFORMATION + REG_SET_KEY_SECURITY_INFORMATION. + REG_UNLOAD_KEY_INFORMATION + + ** There were incorrect entries in this list that are now + corrected. REG_CREATE_KEY_INFORMATION and + REG_CREATE_KEY_INFORMATION_V1 are not fully captured and should + be on the partially captured structures. + REG_QUERY_KEY_SECURITY_INFORMATION has now been added to the list + of fully captured structures. + + 2. Partially captured structures + + Note: This section has been modified for clarity in the Windows 8 + release of the WDK. However this information only applies to Windows 7 + since in Windows 8 all input buffers and structures are captured. + + This section defines which members are captured in the partially + captured structures. + + Notes: + + A. CallContext, ObjectContext members are not consumed by registry + and are not treated as probed or captured by this document. + B. Integer types (e.g. enum, int, �) are considered captured in the + structure and are not explicitly listed below. + C. The Object member is always captured and it's not explicitly + listed below. + D. Output buffers are probed but not captured + + List of partially captured structures and the members that are NOT + captured: + + REG_CREATE_KEY_INFORMATION: Class->Buffer + REG_CREATE_KEY_INFORMATION_V1: Class->Buffer + REG_DELETE_VALUE_KEY_INFORMATION: ValueName->Buffer + REG_LOAD_KEY_INFORMATION: KeyName->Buffer + REG_QUERY_VALUE_KEY_INFORMATION: ValueName->Buffer + REG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION: BufferLength + REG_RENAME_KEY_INFORMATION: NewName->Buffer + REG_SET_VALUE_KEY_INFORMATION: Data + + + All other REG_Xxx_INFORMATION structures do not have fields that + require capturing other than those mentioned in notes B and C. + + Example: + + REG_ENUMERATE_KEY_INFORMATION: + Object: captured, see (C) above + Index: captured, see (B) above + KeyInformationClass : captured, see (B) above + KeyInformation: only probed, see (D) above + Length: captured, see (B) above + ResultLength: only probed, see (D) above + CallContext, ObjectContext: unknown, see (A) above + Reserved: currently undefined + + List of these structures: + + REG_CALLBACK_CONTEXT_CLEANUP_INFORMATION + REG_DELETE_KEY_INFORMATION + REG_ENUMERATE_KEY_INFORMATION + REG_KEY_HANDLE_CLOSE_INFORMATION + REG_QUERY_KEY_INFORMATION + REG_QUERY_KEY_SECURITY_INFORMATION + REG_REPLACE_KEY_INFORMATION + REG_RESTORE_KEY_INFORMATION + REG_SAVE_KEY_INFORMATION + REG_SET_INFORMATION_KEY_INFORMATION + REG_SET_KEY_SECURITY_INFORMATION + REG_UNLOAD_KEY_INFORMATION + + + III. Handling Registry Filtering Parameters + + Registry filtering drivers must handle input parameters correctly. If the + previous mode is user mode and the driver needs to use a parameter, it must + depending on the scenario either wrap every access with a try-except + construct or capture the parameter. If the driver wants to call a Zw + registry API or any other kernel mode Zw API, it must ensure that all the + arguments passed to the call are captured because these APIs will not + probe or capture their inputs if the call originated from kernel mode. + + There is no need for drivers to probe any of the parameters as the registry + has already probed them. If the driver uses the OS probe APIs to probe a + parameter that has already been captured by the registry, it will throw an + exception. + + Note: Special handling should be provided for NULL buffers. In some cases + such buffer might be considered valid even for kernel mode. + +--*/ + + +NTSTATUS +CallbackCapture( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine shows how to capture a buffer and a + unicode string with the name of a value. The bulk of the work is down + in the helper capture routines: CaptureBuffer and CaptureUnicodeString. + + In the pre-notification phase, we bypass the set value and delete value + operations and complete them manually by calling ZwSetValueKey and + ZwDeleteValueKey. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + PREG_DELETE_VALUE_KEY_INFORMATION PreDeleteValueInfo; + HANDLE RootKey = NULL; + PVOID LocalData = NULL; + PVOID Data = NULL; + UNICODE_STRING LocalValueName = {0}; + PUNICODE_STRING ValueName = NULL; + KPROCESSOR_MODE Mode = KernelMode; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + + case RegNtPreSetValueKey: + + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + + // + // REG_SET_VALUE_KEY_INFORMATION is a partially captured structure. + // The value name is captured but the data is not. Since we are + // passing the data to a zw* method, we need to capture it. + // + // *Note: as of win8, the data buffer is captured as well + // by the registry. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && (Mode == UserMode)) { + Status = CaptureBuffer(&LocalData, + PreSetValueInfo->Data, + PreSetValueInfo->DataSize, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Data = LocalData; + } else { + Data = PreSetValueInfo->Data; + } + + // + // Get a handle to the root key the value is being created under. + // This is in PreInfo->Object. + // + + Status = ObOpenObjectByPointer(PreSetValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Set the value. + // + + Status = ZwSetValueKey(RootKey, + PreSetValueInfo->ValueName, + 0, + PreSetValueInfo->Type, + Data, + PreSetValueInfo->DataSize); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue in CallbackModify failed. Status 0x%x", + Status); + ZwClose(RootKey); + break; + } + + // + // Finally return STATUS_CALLBACK_BYPASS to tell the registry + // not to proceed with the original registry operation and to return + // STATUS_SUCCESS to the caller. + // + + InfoPrint("\tCallback: Set value %wZ bypassed.", PreSetValueInfo->ValueName); + Status = STATUS_CALLBACK_BYPASS; + ZwClose(RootKey); + break; + + case RegNtPreDeleteValueKey: + + PreDeleteValueInfo = (PREG_DELETE_VALUE_KEY_INFORMATION) Argument2; + + // + // REG_DELETE_VALUE_KEY_INFORMATION is a partially captured + // structure. The value name's buffer is not captured. Since we are + // passing the name to a zw* method, we need to capture it. + // + // *Note: as of Win8, the data buffer is captured already + // by the registry. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && (Mode == UserMode)) { + Status = CaptureUnicodeString(&LocalValueName, + PreDeleteValueInfo->ValueName, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + ValueName = &LocalValueName; + } else { + ValueName = PreDeleteValueInfo->ValueName; + } + + // + // Get a handle to the root key the value is being created under. + // This is in PreInfo->Object. + // + + Status = ObOpenObjectByPointer(PreDeleteValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Set the value. + // + + Status = ZwDeleteValueKey(RootKey, + ValueName); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteValue failed. Status 0x%x", + Status); + ZwClose(RootKey); + break; + } + + // + // Finally return STATUS_CALLBACK_BYPASS to tell the registry + // not to proceed with the original registry operation and to return + // STATUS_SUCCESS to the caller. + // + + InfoPrint("\tCallback: Delete value %S bypassed.", ValueName->Buffer); + Status = STATUS_CALLBACK_BYPASS; + ZwClose(RootKey); + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + // + // Free buffers used for capturing user mode values. + // + + if (LocalData != NULL){ + FreeCapturedBuffer(LocalData, REGFLTR_CAPTURE_POOL_TAG); + } + + if (LocalValueName.Buffer != NULL) { + FreeCapturedUnicodeString(&LocalValueName, REGFLTR_CAPTURE_POOL_TAG); + } + + return Status; +} + + + + +NTSTATUS +CaptureBuffer( + _Outptr_result_maybenull_ PVOID *CapturedBuffer, + _In_reads_bytes_(Length) PVOID Buffer, + _In_ SIZE_T Length, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Captures a buffer using allocations with the specified pool tag. Captured + buffer should be freed using FreeCapturedBuffer. + +Arguments: + + CapturedBuffer - pointer to a variable that receives the location of the + captured buffer. + + Buffer - the buffer to capture + + Length - Length of Buffer + + PoolTag - pool tag + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PVOID TempBuffer = NULL; + + NT_ASSERT(CapturedBuffer != NULL); + + if (Length == 0) { + *CapturedBuffer = NULL; + return Status; + } + + TempBuffer = (PCALLBACK_CONTEXT) ExAllocatePoolWithTag( + PagedPool, + Length, + PoolTag); + + // + // It's a good practice to keep the contents of a try-except block to + // the bare minimum. By keeping the pool allocation call outside of the + // try-except block we don't mask possible pool corruptions. + // + + if (TempBuffer != NULL) { + try { + RtlCopyMemory(TempBuffer, Buffer, Length); + } except (ExceptionFilter(GetExceptionInformation())) { + ErrorPrint("Capturing buffer failed with exception"); + ExFreePoolWithTag(TempBuffer, PoolTag); + TempBuffer = NULL; + Status = GetExceptionCode(); + } + } else { + ErrorPrint("Capturing buffer failed wtih insufficient resources"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + *CapturedBuffer = TempBuffer; + + return Status; + } + + +VOID +FreeCapturedBuffer( + _In_ PVOID CapturedBuffer, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Frees a captured buffer. + +Arguments: + + CapturedBuffer - captured buffer + + PoolTag - pool tag + +--*/ +{ + if (CapturedBuffer != NULL) { + ExFreePoolWithTag(CapturedBuffer, PoolTag); + } +} + + +NTSTATUS +CaptureUnicodeString( + _Inout_ UNICODE_STRING *DestString, + _In_ PCUNICODE_STRING SourceString, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Captures a unicode string. The buffer is captured based on SourceString's + Length field with the addition of sizeof(WCHAR) bytes for a NULL to + signal the end of the string. + + Use FreeCapturedUnicodeString to free the captured string. + +Arguments: + + DestString - Pointer to the unicode string that will receive the + captured buffer. + + SourceString - Pointer tot he unicode string to be captured. + + PoolTag - pool tag + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + + if (SourceString->Length == 0) { + DestString->Length = 0; + DestString->Buffer = NULL; + DestString->MaximumLength = 0; + return Status; + } + + // + // Only SourceString->Length should be checked. The registry does not + // validate SourceString->MaximumLength. + // + // An additional sizeof(WCHAR) bytes are added to the buffer size since + // SourceString->Length does not include the NULL at the end of the string. + // + + DestString->Length = SourceString->Length; + DestString->MaximumLength = SourceString->Length + sizeof(WCHAR); + + DestString->Buffer = (PWSTR) ExAllocatePoolWithTag( + PagedPool, + DestString->MaximumLength, + PoolTag); + + if (DestString->Buffer != NULL) { + + RtlZeroMemory(DestString->Buffer, DestString->MaximumLength); + + // + // It's a good practice to keep the contents of a try-except block to + // the bare minimum. By keeping the pool allocation call outside of the + // try-except block we don't mask possible pool corruptions. + // + + try { + RtlCopyMemory(DestString->Buffer, + SourceString->Buffer, + SourceString->Length); + } except (ExceptionFilter(GetExceptionInformation())) { + ErrorPrint("Capturing Unicode String failed with exception"); + ExFreePoolWithTag(DestString->Buffer, PoolTag); + DestString->Buffer = NULL; + Status = GetExceptionCode(); + } + + } else { + ErrorPrint("Capturing Unicode String failed wtih insufficient resources"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (DestString->Buffer == NULL) { + DestString->Length = 0; + DestString->MaximumLength = 0; + } + + return Status; + +} + + +VOID +FreeCapturedUnicodeString( + _In_ UNICODE_STRING *String, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Frees a captured buffer. + +Arguments: + + CapturedBuffer - captured buffer + + PoolTag - pool tag + +--*/ +{ + if (String->Length != 0) { + String->Length = 0; + String->MaximumLength = 0; + FreeCapturedBuffer(String->Buffer, PoolTag); + String->Buffer = NULL; + } +} diff --git a/general/registry/regfltr/sys/context.c b/general/registry/regfltr/sys/context.c new file mode 100644 index 00000000..e599606f --- /dev/null +++ b/general/registry/regfltr/sys/context.c @@ -0,0 +1,582 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Context.c + +Abstract: + + Samples that show how to set call contexts and object contexts. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +SetObjectContextSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can associate a context on + a registry object using CmSetCallbackObjectContext. + + This context is available in the ObjectContext field of the + REG_Xxx_KEY_INFORMATION data structures. The registry object is a handle + to a key and not the registry key itself. When the handle is closed + or the callback is unregistered, the callback will receive a + RegNtCallbackObjectContextCleanup notification to give a chance to + clean up the context. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE RootKeyWithContext = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + + InfoPrint(""); + InfoPrint("=== Set Object Context Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_SET_OBJECT_CONTEXT, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Open the root key again. The callback will associate an object + // context with the RootKeyWithContext handle. + // + + RtlInitUnicodeString(&Name, ROOT_KEY_ABS_PATH); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenKey(&RootKeyWithContext, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey on root key failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Set value using both the g_RootKey handle and the RootKeyWithContext + // handle. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + + if (RootKeyWithContext != NULL) { + Status = ZwSetValueKey(RootKeyWithContext, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + // + // Check that the RegNtCallbackObjectContextCleanup notification was + // received when we unregistered the callback. + // + + if (CallbackCtx->ContextCleanupCount != 1) { + ErrorPrint("Callback was not invoked for a context cleanup notification."); + Success = FALSE; + } + + // + // Check that there were two notifications that had the object context set. + // These are the pre and post set value using the RootKeyWithContext handle. + // + + if (CallbackCtx->NotificationWithContextCount != 2) { + ErrorPrint("Callback OperationWithContext count expected 2, instead it was %d", + CallbackCtx->NotificationWithContextCount); + Success = FALSE; + } + + // + // Check that there were two notifications that did not have the object + // context set. These are the pre and post set value using the + // g_RootKey handle. + // + + if (CallbackCtx->NotificationWithNoContextCount != 2) { + ErrorPrint("Callback OperationWithNoContext count expected 2, instead it was %d", + CallbackCtx->NotificationWithNoContextCount); + Success = FALSE; + } + + Exit: + + if (Success == TRUE) { + InfoPrint("Set Object Context Sample Succeeded."); + } else { + ErrorPrint("Set Object Context Sample FAILED."); + } + + // + // Clean up + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (RootKeyWithContext != NULL) { + ZwClose(RootKeyWithContext); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; +} + + +NTSTATUS +CallbackSetObjectContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine shows how to associate a registry key object + with context information using CmSetCallbackObjectContext. The context + set is then only available to this callback. A callback that sets the + object context should be prepared for a RegNtCallbackObjectContextCleanup + where it must clean up the context. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS; + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CALLBACK_CONTEXT_CLEANUP_INFORMATION CleanupInfo; + PREG_POST_OPERATION_INFORMATION PostInfo; + PVOID ObjectContext = NULL; + + switch(NotifyClass) { + + case RegNtPostOpenKeyEx: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + + // + // If the open key was successful, set an object context + // to the key object. + // + // Note that one of the parameters of CmSetCallbackObjectContext + // is the cookie gotten from registering a callback. The object + // context will only be available to the callback with that + // particular cookie. + // + + if (NT_SUCCESS(PostInfo->Status)) { + + // + // Never call CmSetCallbackObjectContext outside of the + // callback routine. + // + + Status = CmSetCallbackObjectContext(PostInfo->Object, + &CallbackCtx->Cookie, + CallbackCtx, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmSetCallbackobjectContext failed. Status 0x%x", + Status); + } + } + break; + + case RegNtPreSetValueKey: + case RegNtPostSetValueKey: + + // + // All registry operations using the handle received from the open + // key operation will come with the ObjectContext field set to the + // context information. Other operations on the same key but + // using a different handle will not have the ObjectContext field + // set. + // + + if (NotifyClass == RegNtPreSetValueKey) { + ObjectContext = ((PREG_SET_VALUE_KEY_INFORMATION) Argument2)->ObjectContext; + } else { + ObjectContext = ((PREG_POST_OPERATION_INFORMATION) Argument2)->ObjectContext; + } + + if (ObjectContext == NULL) { + InterlockedIncrement(&CallbackCtx->NotificationWithNoContextCount); + } else if (ObjectContext == CallbackCtx) { + InterlockedIncrement(&CallbackCtx->NotificationWithContextCount); + } else { + ErrorPrint("Unexpected ObjectContext value: 0x%p", ObjectContext); + } + + break; + + case RegNtCallbackObjectContextCleanup: + + // + // This is a special notification only invoked for callbacks + // that have set context information to an object. This notification + // is either sent when the registry object is being closed or if + // the callback is being unregistered. In the first case, this + // notification comes after the RegNtPreKeyHandleClose + // notification and before the RegNtPostKeyHandleClose notification. + // + + CleanupInfo = (PREG_CALLBACK_CONTEXT_CLEANUP_INFORMATION) Argument2; + if (CleanupInfo->ObjectContext != CallbackCtx) { + ErrorPrint("ContextCleanup's ObjectContext has unexpected value: 0x%p.", + CleanupInfo->ObjectContext); + } else { + InterlockedIncrement(&CallbackCtx->ContextCleanupCount); + } + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + + +BOOLEAN +SetCallContextSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can associate a context + with a registry operation during the pre-notification phase so that it + is available in the post-notification phase. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + + InfoPrint(""); + InfoPrint("=== Set Operation Context Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_SET_CALL_CONTEXT, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback with the context + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create a key and set a value. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Check that the callback records 2 in OperationContextCount. + // The count should be incremented once in the post-notification for the + // create key and once for the set value. + // + + if (CallbackCtx->NotificationWithContextCount != 2) { + ErrorPrint("Callback OperationWithContextCount expected 2, got %d", + CallbackCtx->NotificationWithContextCount); + Success = FALSE; + } + + Exit: + + if (Success == TRUE) { + InfoPrint("Set Call Context sample succeeded."); + } else { + ErrorPrint("Set Call Context sample FAILED."); + } + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; + +} + + + +NTSTATUS +CallbackSetCallContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine shows how to attach context information to + the registry operation itself in the pre-notification phase and + have access to that context in the post-notification phase. The context + is private to this callback. + + ***Note: Any callback that receives a pre-notification will receive + a post-notifcation EXCEPT if the callback returns a non-success value + (this includes STATUS_CALLBACK_BYPASS) during the pre phase. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_POST_OPERATION_INFORMATION PostInfo; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + + switch(NotifyClass) { + + // + // Set the call context by setting it to the CallContext field of the + // REG_XXX_KEY_INFORMATION structure during the pre-notification phase. + // + + case RegNtPreSetValueKey: + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + PreSetValueInfo->CallContext = CallbackCtx; + break; + + case RegNtPreCreateKeyEx: + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + PreCreateInfo->CallContext = CallbackCtx; + break; + + // + // In the post-notification phase, check that the CallContext field + // of REG_POST_OPERATION_INFORMATION contains the context we set in + // the pre phase. + // + + case RegNtPostSetValueKey: + case RegNtPostCreateKeyEx: + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + if (PostInfo->CallContext != CallbackCtx) { + ErrorPrint("Unexpected CallContext value: 0x%p", PostInfo->CallContext); + } else { + InterlockedIncrement(&CallbackCtx->NotificationWithContextCount); + } + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + diff --git a/general/registry/regfltr/sys/driver.c b/general/registry/regfltr/sys/driver.c new file mode 100644 index 00000000..d169a1be --- /dev/null +++ b/general/registry/regfltr/sys/driver.c @@ -0,0 +1,468 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regfltr.c + +Abstract: + + Sample driver used to run the kernel mode registry callback samples. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD DeviceUnload; + +_Dispatch_type_(IRP_MJ_CREATE) DRIVER_DISPATCH DeviceCreate; +_Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH DeviceClose; +_Dispatch_type_(IRP_MJ_CLEANUP) DRIVER_DISPATCH DeviceCleanup; +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH DeviceControl; + +// +// Pointer to the device object used to register registry callbacks +// +PDEVICE_OBJECT g_DeviceObj; + +// +// Registry callback version +// +ULONG g_MajorVersion; +ULONG g_MinorVersion; + +// +// Set to TRUE if TM and RM were successfully created and the transaction +// callback was successfully enabled. +// +BOOLEAN g_RMCreated; + + +// +// OS version globals initialized in driver entry +// + +BOOLEAN g_IsWin8OrGreater = FALSE; + +VOID +DetectOSVersion() +/*++ + +Routine Description: + + This routine determines the OS version and initializes some globals used + in the sample. + +Arguments: + + None + +Return value: + + None. On failure, global variables stay at default value + +--*/ +{ + + RTL_OSVERSIONINFOEXW VersionInfo = {0}; + NTSTATUS Status; + ULONGLONG ConditionMask = 0; + + // + // Set VersionInfo to Win7's version number and then use + // RtlVerifVersionInfo to see if this is win8 or greater. + // + + VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); + VersionInfo.dwMajorVersion = 6; + VersionInfo.dwMinorVersion = 1; + + VER_SET_CONDITION(ConditionMask, VER_MAJORVERSION, VER_LESS_EQUAL); + VER_SET_CONDITION(ConditionMask, VER_MINORVERSION, VER_LESS_EQUAL); + + + + Status = RtlVerifyVersionInfo(&VersionInfo, + VER_MAJORVERSION | VER_MINORVERSION, + ConditionMask); + if (NT_SUCCESS(Status)) { + g_IsWin8OrGreater = FALSE; + InfoPrint("DetectOSVersion: This machine is running Windows 7 or an older OS."); + } else if (Status == STATUS_REVISION_MISMATCH) { + g_IsWin8OrGreater = TRUE; + InfoPrint("DetectOSVersion: This machine is running Windows 8 or a newer OS."); + } else { + ErrorPrint("RtlVerifyVersionInfo returned unexpected error status 0x%x.", + Status); + + // + // default action is to assume this is not win8 + // + g_IsWin8OrGreater = FALSE; + } + +} + + + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine is called by the operating system to initialize the driver. + It allocates a device object, initializes the supported Io callbacks, and + creates a symlink to make the device accessible to Win32. + + It gets the registry callback version and stores it in the global + variables g_MajorVersion and g_MinorVersion. It also calls + CreateKTMResourceManager to create a resource manager that is used in + the transaction samples. + +Arguments: + + DriverObject - Supplies the system control object for this test driver. + + RegistryPath - The string location of the driver's corresponding services + key in the registry. + +Return value: + + Success or appropriate failure code. + +--*/ +{ + NTSTATUS Status; + UNICODE_STRING NtDeviceName; + UNICODE_STRING DosDevicesLinkName; + UNICODE_STRING DeviceSDDLString; + + UNREFERENCED_PARAMETER(RegistryPath); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, + DPFLTR_ERROR_LEVEL, + "RegFltr: DriverEntry()\n"); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, + DPFLTR_ERROR_LEVEL, + "RegFltr: Use ed nt!Kd_IHVDRIVER_Mask 8 to enable more detailed printouts\n"); + + // + // Create our device object. + // + + RtlInitUnicodeString(&NtDeviceName, NT_DEVICE_NAME); + RtlInitUnicodeString(&DeviceSDDLString, DEVICE_SDDL); + + Status = IoCreateDeviceSecure( + DriverObject, // pointer to driver object + 0, // device extension size + &NtDeviceName, // device name + FILE_DEVICE_UNKNOWN, // device type + 0, // device characteristics + TRUE, // not exclusive + &DeviceSDDLString, // SDDL string specifying access + NULL, // device class guid + &g_DeviceObj); // returned device object pointer + + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Set dispatch routines. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceClose; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DeviceCleanup; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl; + DriverObject->DriverUnload = DeviceUnload; + + // + // Create a link in the Win32 namespace. + // + + RtlInitUnicodeString(&DosDevicesLinkName, DOS_DEVICES_LINK_NAME); + + Status = IoCreateSymbolicLink(&DosDevicesLinkName, &NtDeviceName); + + if (!NT_SUCCESS(Status)) { + IoDeleteDevice(DriverObject->DeviceObject); + return Status; + } + + // + // Get callback version. + // + + CmGetCallbackVersion(&g_MajorVersion, &g_MinorVersion); + InfoPrint("Callback version %u.%u", g_MajorVersion, g_MinorVersion); + + // + // Some variations depend on knowing if the OS is win8 or above + // + + DetectOSVersion(); + + // + // Set up KTM resource manager and pass in RMCallback as our + // callback routine. + // + + Status = CreateKTMResourceManager(RMCallback, NULL); + + if (NT_SUCCESS(Status)) { + g_RMCreated = TRUE; + } + + // + // Initialize the callback context list + // + + InitializeListHead(&g_CallbackCtxListHead); + ExInitializeFastMutex(&g_CallbackCtxListLock); + g_NumCallbackCtxListEntries = 0; + + return STATUS_SUCCESS; + +} + + + +NTSTATUS +DeviceCreate ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches file create requests. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_NOT_IMPLEMENTED + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DeviceClose ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches close requests. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DeviceCleanup ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches cleanup requests. Does nothing right now. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DeviceControl ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches ioctl requests. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + Status returned from the method called. + +--*/ +{ + PIO_STACK_LOCATION IrpStack; + ULONG Ioctl; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(DeviceObject); + + Status = STATUS_SUCCESS; + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + Ioctl = IrpStack->Parameters.DeviceIoControl.IoControlCode; + + switch (Ioctl) + { + + case IOCTL_DO_KERNELMODE_SAMPLES: + Status = DoCallbackSamples(DeviceObject, Irp); + break; + + case IOCTL_REGISTER_CALLBACK: + Status = RegisterCallback(DeviceObject, Irp); + break; + + case IOCTL_UNREGISTER_CALLBACK: + Status = UnRegisterCallback(DeviceObject, Irp); + break; + + case IOCTL_GET_CALLBACK_VERSION: + Status = GetCallbackVersion(DeviceObject, Irp); + break; + + default: + ErrorPrint("Unrecognized ioctl code 0x%x", Ioctl); + } + + // + // Complete the irp and return. + // + + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + +} + + +VOID +DeviceUnload ( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Cleans up any driver-level allocations and prepares for unload. All + this driver needs to do is to delete the device object and the + symbolic link between our device name and the Win32 visible name. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_NOT_IMPLEMENTED + +--*/ +{ + UNICODE_STRING DosDevicesLinkName; + + // + // Clean up the KTM data structures + // + + DeleteKTMResourceManager(); + + // + // Delete the link from our device name to a name in the Win32 namespace. + // + + RtlInitUnicodeString(&DosDevicesLinkName, DOS_DEVICES_LINK_NAME); + IoDeleteSymbolicLink(&DosDevicesLinkName); + + // + // Finally delete our device object + // + + IoDeleteDevice(DriverObject->DeviceObject); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, + DPFLTR_ERROR_LEVEL, + "RegFltr: DeviceUnload\n"); +} + diff --git a/general/registry/regfltr/sys/multialt.c b/general/registry/regfltr/sys/multialt.c new file mode 100644 index 00000000..b471d0ba --- /dev/null +++ b/general/registry/regfltr/sys/multialt.c @@ -0,0 +1,776 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + MultiAlt.c + +Abstract: + + Samples that feature multiple callbacks registered at different + altitudes and show what notifications they do and don't recieve. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +MultipleAltitudeBlockDuringPreSample( + ) +/*++ + +Routine Description: + + This sample features a stack of three callbacks at different altitudes and + demonstrates what happens when middle callback blocks an operation + in the pre-notification phase. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtxHigh = NULL; + PCALLBACK_CONTEXT CallbackCtxMid = NULL; + PCALLBACK_CONTEXT CallbackCtxLow = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Multiple Altitude Block During Pre Sample ===="); + + // + // Create callback contexts for the 3 callbacks. + // The high and low callbacks will only monitor how many notifications + // they receive. + // + + CallbackCtxHigh = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_HIGH_ALTITUDE); + CallbackCtxMid = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE, + CALLBACK_ALTITUDE); + CallbackCtxLow = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_LOW_ALTITUDE); + + if ((CallbackCtxHigh == NULL) || + (CallbackCtxMid == NULL) || + (CallbackCtxLow == NULL)) { + goto Exit; + } + + // + // Register the callbacks + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxHigh->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxHigh, + &CallbackCtxHigh->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxMid->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxMid, + &CallbackCtxMid->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxLow->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxLow, + &CallbackCtxLow->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Do a create key operation which will be blocked by the middle + // callback and fail with STATUS_ACCESS_DENIED + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + + // + // Unregister the callbacks + // + + Status = CmUnRegisterCallback(CallbackCtxHigh->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + Status = CmUnRegisterCallback(CallbackCtxMid->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + Status = CmUnRegisterCallback(CallbackCtxLow->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + + // + // Verify that the highest alitude callback receives a pre and a post + // notification. It receives a post notification because it returned + // STATUS_SUCCESS in the pre-notification so it is guaranteed to get a + // post notification. + // + + if ((CallbackCtxHigh->PreNotificationCount != 1) || + (CallbackCtxHigh->PostNotificationCount != 1)) { + ErrorPrint("High Callback should have seen 1 pre and 1 post notifications."); + ErrorPrint("High Callback actually saw %d pre and %d post notifications.", + CallbackCtxHigh->PreNotificationCount, + CallbackCtxHigh->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the middle callback receives only a pre notification. + // It does not get a post notification because it return a non-success + // value in the pre-notification phase. + // + + if ((CallbackCtxMid->PreNotificationCount != 1) || + (CallbackCtxMid->PostNotificationCount != 0)) { + ErrorPrint("Mid Callback should have seen 1 pre and 0 post notifications."); + ErrorPrint("Mid Callback actually saw %d pre and %d post notifications.", + CallbackCtxMid->PreNotificationCount, + CallbackCtxMid->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the lowest callback receives no notifications. + // Once the middle callback blocks, no callbacks at lower altitudes are + // notified. + // + + if ((CallbackCtxLow->PreNotificationCount != 0) || + (CallbackCtxLow->PostNotificationCount != 0)) { + ErrorPrint("Low Callback should have seen 0 pre and 0 post notifications."); + ErrorPrint("Low Callback actually saw %d pre and %d post notifications.", + CallbackCtxLow->PreNotificationCount, + CallbackCtxLow->PostNotificationCount); + Success = FALSE; + } + + Exit: + + if (Success) { + InfoPrint("Multiple Altitude Block During Pre Sample succeeded."); + } else { + ErrorPrint("Multiple Altitude Block During Pre Sample FAILED."); + } + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtxHigh != NULL) { + ExFreePoolWithTag(CallbackCtxHigh, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxMid != NULL) { + ExFreePoolWithTag(CallbackCtxMid, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxLow != NULL) { + ExFreePoolWithTag(CallbackCtxLow, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; +} + + +BOOLEAN +MultipleAltitudeInternalInvocationSample( + ) +/*++ + +Routine Description: + + This sample features a stack of 3 callbacks at different altitudes and + demonstrates what happens when the middle callback invokes a registry + operation. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtxHigh = NULL; + PCALLBACK_CONTEXT CallbackCtxMid = NULL; + PCALLBACK_CONTEXT CallbackCtxLow = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + BOOLEAN Success = FALSE; + + + InfoPrint(""); + InfoPrint("=== Multiple Altitude Internal Invocation Sample ===="); + + // + // Create callback contexts for the 3 callbacks. + // The high and low callbacks will only monitor how many notifications + // they receive. + // + + CallbackCtxHigh = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_HIGH_ALTITUDE); + CallbackCtxMid = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION, + CALLBACK_ALTITUDE); + CallbackCtxLow = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_LOW_ALTITUDE); + + if ((CallbackCtxHigh == NULL) || + (CallbackCtxMid == NULL) || + (CallbackCtxLow == NULL)) { + goto Exit; + } + + // + // Register the callbacks + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxHigh->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxHigh, + &CallbackCtxHigh->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxMid->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxMid, + &CallbackCtxMid->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxLow->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxLow, + &CallbackCtxLow->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create a key. When the middle callback receives the pre-notification + // and the post-notification for this create it will perform an open key + // and a close key operation. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callbacks + // + + Status = CmUnRegisterCallback(CallbackCtxHigh->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Status = CmUnRegisterCallback(CallbackCtxMid->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Status = CmUnRegisterCallback(CallbackCtxLow->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Verify the highest altitude callback receives one pre and one post + // notification. This callback does not get notifications for the + // registry operations called by the middle callback. + // + + if ((CallbackCtxHigh->PreNotificationCount != 1) || + (CallbackCtxHigh->PostNotificationCount != 1)) { + ErrorPrint("High Callback should have seen 1 pre and 1 post notifications."); + ErrorPrint("High Callback actually saw %d pre and %d post notifications.", + CallbackCtxHigh->PreNotificationCount, + CallbackCtxHigh->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the middle callback receives one pre and one post notification. + // This callback does not get notifications for the registry operations + // that it calls. + // + + if ((CallbackCtxMid->PreNotificationCount != 1) || + (CallbackCtxMid->PostNotificationCount != 1)) { + ErrorPrint("Mid Callback should have seen 1 pre and 1 post notifications."); + ErrorPrint("Mid Callback actually saw %d pre and %d post notifications.", + CallbackCtxMid->PreNotificationCount, + CallbackCtxMid->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the lowest callback receives 5 pre-notifications and 5 + // post-notifications. This callback receives 1 pre and 1 post from the + // original create key operation. It also receives 2 pre and 2 post for + // the open key and close key operations called by the middle callback + // during the pre phase of the create key and then 2 pre and 2 post again + // for the calls in the post phase of the create key. + // + + if ((CallbackCtxLow->PreNotificationCount != 5) || + (CallbackCtxLow->PostNotificationCount != 5)) { + ErrorPrint("Low Callback should have seen 5 pre and 5 post notifications."); + ErrorPrint("Low Callback actually saw %d pre and %d post notifications.", + CallbackCtxLow->PreNotificationCount, + CallbackCtxLow->PostNotificationCount); + Success = FALSE; + } + + Exit: + + if (Success) { + InfoPrint("Multiple Altitude Internal Invocation Sample succeeded."); + } else { + ErrorPrint("Multiple Altitude Internal Invocation Sample FAILED."); + } + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtxHigh != NULL) { + ExFreePoolWithTag(CallbackCtxHigh, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxMid != NULL) { + ExFreePoolWithTag(CallbackCtxMid, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxLow != NULL) { + ExFreePoolWithTag(CallbackCtxLow, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; +} + + +NTSTATUS +CallbackMonitor( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine just monitors how many pre and post registry + operations it receives and records it in the callback context. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS + +--*/ +{ + UNREFERENCED_PARAMETER(Argument2); + + switch(NotifyClass) { + case RegNtPreDeleteKey: + case RegNtPreSetValueKey: + case RegNtPreDeleteValueKey: + case RegNtPreSetInformationKey: + case RegNtPreRenameKey: + case RegNtPreEnumerateKey: + case RegNtPreEnumerateValueKey: + case RegNtPreQueryKey: + case RegNtPreQueryValueKey: + case RegNtPreQueryMultipleValueKey: + case RegNtPreKeyHandleClose: + case RegNtPreCreateKeyEx: + case RegNtPreOpenKeyEx: + case RegNtPreFlushKey: + case RegNtPreLoadKey: + case RegNtPreUnLoadKey: + case RegNtPreQueryKeySecurity: + case RegNtPreSetKeySecurity: + case RegNtPreRestoreKey: + case RegNtPreSaveKey: + case RegNtPreReplaceKey: + InterlockedIncrement(&CallbackCtx->PreNotificationCount); + break; + case RegNtPostDeleteKey: + case RegNtPostSetValueKey: + case RegNtPostDeleteValueKey: + case RegNtPostSetInformationKey: + case RegNtPostRenameKey: + case RegNtPostEnumerateKey: + case RegNtPostEnumerateValueKey: + case RegNtPostQueryKey: + case RegNtPostQueryValueKey: + case RegNtPostQueryMultipleValueKey: + case RegNtPostKeyHandleClose: + case RegNtPostCreateKeyEx: + case RegNtPostOpenKeyEx: + case RegNtPostFlushKey: + case RegNtPostLoadKey: + case RegNtPostUnLoadKey: + case RegNtPostQueryKeySecurity: + case RegNtPostSetKeySecurity: + case RegNtPostRestoreKey: + case RegNtPostSaveKey: + case RegNtPostReplaceKey: + InterlockedIncrement(&CallbackCtx->PostNotificationCount); + break; + } + + return STATUS_SUCCESS; + +} + + +NTSTATUS +CallbackMultipleAltitude( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine first calls CallbackMonitor to record the + number of pre and post notifications received by the callback. Then it + does one of two things depending on the callback mode specified in the + callback context. + + If callback mode is CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE: + Return STATUS_ACCESS_DENIED when we receive a pre-notification for a + create key operation. + + If callback mode is CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION: + Call ZwOpenKey and ZwCloseKey when we receive a pre or post notification + for a create key operation. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PVOID Object = NULL; + HANDLE ObjectHandle = NULL; + PCUNICODE_STRING ObjectName = NULL; + UNICODE_STRING CapturedObjectName = {0}; + OBJECT_ATTRIBUTES KeyAttributes = {0}; + PREG_POST_OPERATION_INFORMATION PostInfo; + + CallbackMonitor(CallbackCtx, NotifyClass, Argument2); + + if(CallbackCtx->CallbackMode == CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE) { + switch(NotifyClass) { + case RegNtPreSetValueKey: + case RegNtPreCreateKeyEx: + InfoPrint("\tCallback: CreateKey/SetValueKey blocked."); + Status = STATUS_ACCESS_DENIED; + break; + default: + // + // Do nothing for other notifications + // + break; + } + } + + if (CallbackCtx->CallbackMode == CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION) { + + // + // Get the Object that we will open and close. + // + + switch(NotifyClass) { + case RegNtPreCreateKeyEx: + Object = ((PREG_CREATE_KEY_INFORMATION) Argument2)->RootObject; + + // + // RootObject should never be NULL. + // + + ASSERT(Object != NULL); + + break; + + case RegNtPostCreateKeyEx: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + + // + // Make sure the operation is successful so far. + // + + if (!NT_SUCCESS(PostInfo->Status)) { + ErrorPrint("Post notification status is unexpectedly 0x%x.", + PostInfo->Status); + break; + } + + // + // If the operation si successful so far, PostInfo->Object should + // not be NULL. However, a misbehaving registry filter driver + // can make this NULL so we do not ASSERT here as we do in the + // pre-notification case. + // + + Object = PostInfo->Object; + if (Object == NULL) { + ErrorPrint("PostInfo->Object is unexpectedly null in RegNtPostCreateKeyEx."); + ErrorPrint("PostInfo->Status is 0x%x", + PostInfo->Status); + } + + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + + if (Object != NULL) { + + // + // Use CmCallbackGetKeyObjectID to get the absolute path to Object. + // + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // In Windows 8, CmCallbackGetKeyObjectIDEx was added to give + // developers a copy of the object name rather than the actual + // object name string. This is a safer programming approach and + // allows the system to safely clean up the old object name in + // operations like renaming the key. + // + // Call CmCallbackReleaseKeyObjectIDEx to release the object name + // returned by CmCallbackGetKeyObjectIDEx. + // + + Status = CmCallbackGetKeyObjectIDEx(&CallbackCtx->Cookie, + Object, + NULL, + &ObjectName, + 0); // Flag: reserved for future + + if (!NT_SUCCESS (Status)) { + ErrorPrint("CmCallbackGetKeyObjectIDEx failed. Status 0x%x", Status); + goto Exit; + } + + CapturedObjectName.Length = ObjectName->Length; + CapturedObjectName.MaximumLength = ObjectName->MaximumLength; + CapturedObjectName.Buffer = ObjectName->Buffer; + +#else + + Status = CmCallbackGetKeyObjectID(&CallbackCtx->Cookie, + Object, + NULL, + &ObjectName); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("CmCallbackGetKeyObjectID failed. Status 0x%x", Status); + goto Exit; + } + + // + // The UNICODE_STRING referenced by ObjectName from + // CmCallbackGetKeyObjectID must not be changed. If you need to + // modify the string, create a copy. + // + // Although this sample does not change the path, we show the + // code to capture the string for demonstration purposes. + // + + Status = CaptureUnicodeString(&CapturedObjectName, ObjectName, + REGFLTR_CAPTURE_POOL_TAG); + + if (!NT_SUCCESS(Status)) { + goto Exit; + } + +#endif //NTDDI_VERSION >= NTDDI_WIN8 + + + InitializeObjectAttributes(&KeyAttributes, + &CapturedObjectName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + InfoPrint("\tCallback: Internal Invocation of ZwOpenKey"); + Status = ZwOpenKey(&ObjectHandle, + KEY_ALL_ACCESS, + &KeyAttributes); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ZwOpenKey failed. Status 0x%x", Status); + } else { + InfoPrint("\tCallback: Internal Invocation of ZwCloseKey"); + ZwClose(ObjectHandle); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + CmCallbackReleaseKeyObjectIDEx(ObjectName); + +#else + + FreeCapturedUnicodeString(&CapturedObjectName, REGFLTR_CAPTURE_POOL_TAG); + +#endif //NTDDI_VERSION >= NTDDI_WIN8 + + } + } + + Exit: + + return Status; +} + + diff --git a/general/registry/regfltr/sys/post.c b/general/registry/regfltr/sys/post.c new file mode 100644 index 00000000..bff979fe --- /dev/null +++ b/general/registry/regfltr/sys/post.c @@ -0,0 +1,768 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Post.c + +Abstract: + + Samples that show what callbacks can do during the post-notification + phase. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +/*++ + + In registry callback version 1.0, there is a bug with post-notification + processing and multiple registry filter drivers that can break the samples + here. It is fixed with version 1.1. + + The bug occurs when a driver blocks or bypasses a registry operation in the + pre-notification phase. Even though the processing of the operation stops + there, registry filter drivers registered at higher altitudes will still + get a post-notification for the operation. If the higher altitude driver + tries to change the status of the operation from failure to success or + vice versa, this change will be ignored and the status returned + will be the status returned by the driver who bypassed or blocked the + operation during the pre-notification phase. + + For more information on how notification processing works with multiple + registry filter drivers registered see MultiAlt.c + + For more information on issues in version 1.0 and changes in version 1.1 + see Version.c + + + Beginning with Windows 8.1, it is no longer possible to pass the object + provided to a RegNtPostCreateKeyEx or RegNtPostOpenKeyEx callout to + ObOpenObjectByPointer. To work around this, filters should perform all + create key or open key processing in a RegNtPreCreateKeyEx or + RegNtPreOpenKeyEx callout. If for any reason the desired processing cannot + be performed in a RegNtPreCreateKeyEx or RegNtPreOpenKeyEx callout, then + use CmSetCallbackObjectContext to tag a newly-created or newly-opened key + as unprocessed and process it in the pre-operation callback for a + subsequent operation. + +--*/ + + + +BOOLEAN +PostNotificationOverrideSuccessSample( + ) +/*++ + +Routine Description: + + This sample shows how registry callbacks can fail a registry operation + in the post-notification phase. + + Two values are created. The creates normally should succeeded, but one + is intercepted by the callback and failed with STATUS_ACCESS_DENIED. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Post-Notification Override Success Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Set two values. + // Setting the "not modified" value will succeed. + // Setting the other value will fail with file not found. + // + + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Verify that the set value calls were failed by + // checking that the value with VALUE_NAME does not + // exist. + // + // Deleting the other value should return STATUS_OBJECT_NAME_NOT_FOUND + // Deleting value with the modified name should succeed. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwDeleteValueKey(g_RootKey, &Name); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwDeleteValueKey on value failed. Status: 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Post-Notification Override Success Sample succeeded."); + } else { + ErrorPrint("Post-Notification Override Success Sample FAILED."); + } + return Success; + +} + + +NTSTATUS +CallbackPostNotificationOverrideSuccess( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine intercepts create key and set value post + notifications and fails the operation with STATUS_ACCESS_DENIED. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + PREG_POST_OPERATION_INFORMATION PostInfo; + UNICODE_STRING Name; + HANDLE Key = NULL; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + case RegNtPostCreateKeyEx: + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) PostInfo->PreInformation; + + // + // REG_CREATE_KEY_INFORMATION is a partially captured + // structure however no uncaptured fields are used here. For more + // information on what parameters need to be captured, see + // capture.c. + // + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreCreateInfo->CompleteName, + TRUE)) { + break; + } + + // + // Make sure the operation is successful so far. + // + + if (!NT_SUCCESS(PostInfo->Status)) { + ErrorPrint("Operation status in post notification is unexpectedly 0x%x", + PostInfo->Status); + break; + } + + // + // Since this is the post-notification phase, the key has + // already been created. It is stored in PostInfo->Object. + // Get a handle on the key and delete it. + // + + Status = ObOpenObjectByPointer(PostInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreCreateInfo->ObjectType, + KernelMode, + &Key); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x\n", Status); + break; + } + + Status = ZwDeleteKey(Key); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteKey failed. Status 0x%x\n", Status); + break; + } + + ZwClose(Key); + + // + // Dereference the object because it will not be returned to + // the user. NULL out the references to the object in the + // post and pre information structures. + // + + ObDereferenceObject(PostInfo->Object); + PostInfo->Object = NULL; + *PreCreateInfo->ResultObject = NULL; + + InfoPrint("\tCallback: Create key %wZ overrided from success to error.", + PreCreateInfo->CompleteName); + + // + // Put the status to be returned in PostInfo->ReturnStatus and + // return STATUS_CALLBACK_BYPASS to let CM know that + // we want to change the return status. + // + // DO NOT set PostInfo->Status + // + + PostInfo->ReturnStatus = STATUS_ACCESS_DENIED; + Status = STATUS_CALLBACK_BYPASS; + break; + + case RegNtPostSetValueKey: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) PostInfo->PreInformation; + + // + // NOTE: REG_SET_VALUE_KEY_INFORMATION is a partially captured + // structure. The value name is captured but the data buffer is + // not. Since we are only using the value name we do not need to + // capture any parameters. For more information on what parameters + // need to be captured, see capture.c. + // + + // + // Only intercept the operation if the value being set has the + // name VALUE_NAME. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreSetValueInfo->ValueName, + TRUE)) { + break; + } + + // + // Make sure the operation is successful so far. + // + + if (!NT_SUCCESS(PostInfo->Status)) { + ErrorPrint("Post notification status is unexpectedly 0x%x.", + PostInfo->Status); + break; + } + + // + // To fail the operation, we have to delete the value that has + // been created. To do so, we need a handle to the root key. + // + + Status = ObOpenObjectByPointer(PreSetValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &Key); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x\n", Status); + break; + } + + Status = ZwDeleteValueKey(Key, PreSetValueInfo->ValueName); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteValueKey failed. Status 0x%x\n", Status); + break; + } + + ZwClose(Key); + + // + // Put the status to be returned in PostInfo->ReturnStatus and + // return STATUS_CALLBACK_BYPASS to let CM know that + // we want to change the return status. + // + // DO NOT set PostInfo->Status + // + + InfoPrint("\tCallback: Value %wZ overrided from success to error.", + PreSetValueInfo->ValueName); + PostInfo->ReturnStatus = STATUS_ACCESS_DENIED; + Status = STATUS_CALLBACK_BYPASS; + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; + +} + + +BOOLEAN +PostNotificationOverrideErrorSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can change a failed registry + operation into a successful operation in the post-notification phase. + + A key that does not exist is opened. The opens should fail, but it is + intercepted by the callback and the open is redirected to a key that + does exist. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + BOOLEAN Success = FALSE; + HANDLE Key = NULL; + HANDLE ModifiedKey = NULL; + + InfoPrint(""); + InfoPrint("=== Post-Notification Override Error Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Create a key with name MODIFIED_KEY_NAME + // + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&ModifiedKey, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("Creating modified key failed. Status 0x%x", Status); + goto Exit; + } + + // + // Now try to open a key by KEY_NAME which does not exist. Verify that + // this fails. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + goto Exit; + } + + // + // Register our callback with the context + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Open key again. The callback will intercept this and make it succeed. + // + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey failed unexpectedly. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (ModifiedKey != NULL) { + ZwDeleteKey(ModifiedKey); + ZwClose(ModifiedKey); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Post-Notification Override Error Sample succeeded."); + } else { + ErrorPrint("Post-Notification Override Error Sample FAILED."); + } + + return Success; +} + + +NTSTATUS +CallbackPostNotificationOverrideError( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine intercepts open key post notifications + and if they are failing with STATUS_ACCESS_DENIED, it makes + the operation successful by redirecting the open to antoher key. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_OPEN_KEY_INFORMATION PreOpenInfo; + PREG_POST_OPERATION_INFORMATION PostInfo; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + PVOID Object; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + case RegNtPostOpenKeyEx: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + PreOpenInfo = (PREG_OPEN_KEY_INFORMATION) PostInfo->PreInformation; + + // + // NOTE: REG_OPEN_KEY_INFORMATION is a fully captured structure + // so there is no need for the callback to capture any parameters. + // For more information on what parameters need to be captured, see + // capture.c. + // + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreOpenInfo->CompleteName, + TRUE)) { + break; + } + + // + // Verify that operation is currently failing as expected + // + + if (PostInfo->Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("Operation did not fail with status not found as expected. Post status: 0x%x", + PostInfo->Status); + break; + } + + // + // To make the operation successful, an object MUST be supplied as + // the opened handle. In this sample, the object will be a handle + // to another key that does exist. + // + + // + // First open the key and get its handle. + // + + Status = ObOpenObjectByPointer(PreOpenInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreOpenInfo->ObjectType, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + PreOpenInfo->SecurityDescriptor); + + Status = ZwOpenKey(&Key, + PreOpenInfo->DesiredAccess, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey failed. Status 0x%x", Status); + ZwClose(RootKey); + break; + } + + ZwClose(RootKey); + + // + // Then, get the object pointer from the new key's handle. + // + + Status = ObReferenceObjectByHandle(Key, + PreOpenInfo->DesiredAccess, + PreOpenInfo->ObjectType, + KernelMode, + &Object, + NULL); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByHandle failed. Status 0x%x", Status); + ZwClose(Key); + break; + } + + // + // Finally, set the ResultObject field in the PreInfo and + // the Object field in the PostInfo to the object just opened. + // + + *PreOpenInfo->ResultObject = Object; + PreOpenInfo->GrantedAccess = PreOpenInfo->DesiredAccess; + + if (PostInfo->Object != NULL) { + ErrorPrint("PostInfo->Object should be NULL! Instead is 0x%p", + PostInfo->Object); + } + PostInfo->Object = Object; + + ZwClose(Key); + InfoPrint("\tCallback: Opening key %wZ overrided from error to success.", + PreOpenInfo->CompleteName); + + // + // Put the status to be returned in PostInfo->ReturnStatus and + // return STATUS_CALLBACK_BYPASS to let CM know that + // we want to change the return status. + // + // DO NOT set PostInfo->Status + // + + PostInfo->ReturnStatus = STATUS_SUCCESS; + Status = STATUS_CALLBACK_BYPASS; + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; + +} + diff --git a/general/registry/regfltr/sys/pre.c b/general/registry/regfltr/sys/pre.c new file mode 100644 index 00000000..0cc895c9 --- /dev/null +++ b/general/registry/regfltr/sys/pre.c @@ -0,0 +1,799 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Pre.c + +Abstract: + + Samples that show what callbacks can do during the pre-notification + phase. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +PreNotificationBlockSample( + ) +/*++ + +Routine Description: + + This sample shows how to block a registry operation in the + pre-notification phase. + + Two keys are created. The create operations should succeed, but one + is intercepted by the callback and failed with STATUS_ACCESS_DENIED. + The same is done for two values. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + HANDLE NotModifiedKey = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Pre-Notification Block Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_PRE_NOTIFICATION_BLOCK, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create two keys. + // Creating the "not modified" key will succeed. + // Creating the other key will fail with STATUS_ACCESS_DENIED + // + + RtlInitUnicodeString(&Name, NOT_MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&NotModifiedKey, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (Status != STATUS_SUCCESS) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + + // + // Set two values. + // Setting the "not modified" value will succeed. + // Setting the other value will fail with STATUS_ACCESS_DENIED. + // + + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(Status != STATUS_SUCCESS) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (NotModifiedKey != NULL) { + ZwDeleteKey(NotModifiedKey); + ZwClose(NotModifiedKey); + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Pre-Notification Block Sample succeeded."); + } else { + ErrorPrint("Pre-Notification Block Sample FAILED."); + } + + return Success; + +} + + +NTSTATUS +CallbackPreNotificationBlock( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine shows hot to fail a registry operation + in the pre-notification phase. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + UNICODE_STRING Name; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreCreateInfo->CompleteName, + TRUE)) { + // + // By returning an error status, we block the operation. + // + + InfoPrint("\tCallback: Create key %wZ blocked.", + PreCreateInfo->CompleteName); + Status = STATUS_ACCESS_DENIED; + } + break; + + case RegNtPreSetValueKey: + + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + + // + // Only intercept the operation if the value being set has the + // name VALUE_NAME. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + if (RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreSetValueInfo->ValueName, + TRUE)) { + // + // By returning an error status, we block the operation. + // + + InfoPrint("\tCallback: Set value %wZ blocked.", + PreSetValueInfo->ValueName); + Status = STATUS_ACCESS_DENIED; + } + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + + + +BOOLEAN +PreNotificationBypassSample( + ) +/*++ + +Routine Description: + + This sample shows how to bypass a registry operation so that the CM does + not process the operation. Unlike block, an operation that is bypassed + is still considered successful so the callback must provide the caller + with what the CM would have provided. + + A key and a value are created. However both operations are bypassed by the + callback so that the key and value actually created have different names + than would is expected. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Pre-Notification Bypass Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_PRE_NOTIFICATION_BYPASS, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create a key and set a value. Both should succeed + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Check that a key with the expected name KEY_NAME cannot be found + // but a key with the "modified" name can be found. + // + + if (Key != NULL) { + ZwClose(Key); + } + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey on key returned unexpected status: 0x%x", Status); + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + Key = NULL; + } + Success = FALSE; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey on modified key path failed. Status: 0x%x", Status); + Success = FALSE; + } + + + // + // Do the same check by trying to delete a value with VALUE_NAME and + // with the "modified" name. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwDeleteValueKey(g_RootKey, &Name); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwDeleteValueKey on original value returned unexpected status: 0x%x", + Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, MODIFIED_VALUE_NAME); + Status = ZwDeleteValueKey(g_RootKey, &Name); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteValueKey on modified value failed. Status: 0x%x", + Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Pre-Notification Bypass Sample succeeded."); + } else { + ErrorPrint("Pre-Notification Bypass Sample FAILED."); + } + + return Success; +} + + + +NTSTATUS +CallbackPreNotificationBypass( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine is the most complex part of the sample. + Here we actually manipulate the registry inside the callback to modify the + outcome and the behavior of the registry operation. + + In the pre-notification phase, we bypass the call but create a key or set + a value with a different name. + + In the post-notification phase we delete the key or value that was + created by the registry and tell the registry to return the bad error + status to the caller. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + UNICODE_STRING LocalClass = {0}; + PUNICODE_STRING Class = NULL; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + PVOID Object; + PVOID LocalData = NULL; + PVOID Data = NULL; + KPROCESSOR_MODE Mode = KernelMode; + + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreCreateInfo->CompleteName, + TRUE)) { + break; + } + + // + // REG_CREATE_KEY_INFORMATION is a partially structure. The class + // field's buffer is not captured. Since it is passed to + // ZwCreateKey, it needs to be captured. + // + // *Note: in Windows 8 all fields are captured. See capture.c + // for more details. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && Mode == UserMode) { + Status = CaptureUnicodeString(&LocalClass, + PreCreateInfo->Class, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Class = &LocalClass; + + } else { + Class = PreCreateInfo->Class; + } + + + // + // Next we create a key with a modified name. + // + + Status = ObOpenObjectByPointer(PreCreateInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreCreateInfo->ObjectType, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + PreCreateInfo->SecurityDescriptor); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + Class, + PreCreateInfo->CreateOptions, + PreCreateInfo->Disposition); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + ZwClose(RootKey); + break; + } + + ZwClose(RootKey); + + // + // The we get an object pointer from the new key's handle. + // + + Status = ObReferenceObjectByHandle(Key, + PreCreateInfo->DesiredAccess, + PreCreateInfo->ObjectType, + KernelMode, + &Object, + NULL); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByHandle failed. Status 0x%x", Status); + ZwClose(Key); + break; + } + + ZwClose(Key); + + // + // Set the ResultObject field to the new key object. + // + + *PreCreateInfo->ResultObject = Object; + + // + // Return STATUS_CALLBACK_BYPASS to let CM know we want to bypass + // CM and return STATUS_SUCCESS back to the caller. + // + + InfoPrint("\tCallback: Create key %wZ bypassed.", PreCreateInfo->CompleteName); + Status = STATUS_CALLBACK_BYPASS; + break; + + case RegNtPreSetValueKey: + + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + + // + // REG_SET_VALUE_KEY_INFORMATION is a partially captured structure. + // The value name is captured but the data is not. Since we are + // passing the data to a zw* method, we need to capture it. + // + // *Note: in Windows 8 all fields are captured. See capture.c + // for more details. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && Mode == UserMode) { + Status = CaptureBuffer(&LocalData, + PreSetValueInfo->Data, + PreSetValueInfo->DataSize, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Data = LocalData; + } else { + Data = PreSetValueInfo->Data; + } + + // + // Only intercept the operation if the value being set has the + // name VALUE_NAME. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreSetValueInfo->ValueName, + TRUE)) { + break; + } + + // + // Get a handle to the root key the value is being created under. + // This is in PreInfo->Object. + // + + Status = ObOpenObjectByPointer(PreSetValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Set a value with the "modified" name. + // + + RtlInitUnicodeString(&Name, MODIFIED_VALUE_NAME); + Status = ZwSetValueKey(RootKey, + &Name, + 0, + PreSetValueInfo->Type, + Data, + PreSetValueInfo->DataSize); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", + Status); + ZwClose(RootKey); + break; + } + + // + // Finally return STATUS_CALLBACK_BYPASS to tell the registry + // not to proceed with the original registry operation and to return + // STATUS_SUCCESS to the caller. + // + + InfoPrint("\tCallback: Set value %wZ bypassed.", PreSetValueInfo->ValueName); + Status = STATUS_CALLBACK_BYPASS; + ZwClose(RootKey); + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + // + // Free buffers used for capturing user mode values. + // + + if (LocalClass.Buffer != NULL) { + FreeCapturedUnicodeString(&LocalClass, REGFLTR_CAPTURE_POOL_TAG); + } + + if (LocalData != NULL) { + FreeCapturedBuffer(LocalData, REGFLTR_CAPTURE_POOL_TAG); + } + + return Status; +} + + diff --git a/general/registry/regfltr/sys/regfltr.c b/general/registry/regfltr/sys/regfltr.c new file mode 100644 index 00000000..5cd85c6c --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.c @@ -0,0 +1,871 @@ +/*++
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ regfltr.c
+
+Abstract:
+
+ Sample driver used to run the kernel mode registry callback samples.
+
+Environment:
+
+ Kernel mode only
+
+--*/
+
+#include "regfltr.h"
+
+
+//
+// The root key used in the samples
+//
+HANDLE g_RootKey;
+
+
+
+LPCWSTR
+GetTransactionNotifyClassString (
+ _In_ ULONG TransactionNotifcation
+ );
+
+LPCWSTR
+GetNotifyClassString (
+ _In_ REG_NOTIFY_CLASS NotifyClass
+ );
+
+VOID
+DeleteTestKeys(
+ );
+
+
+
+NTSTATUS
+Callback (
+ _In_ PVOID CallbackContext,
+ _In_opt_ PVOID Argument1,
+ _In_opt_ PVOID Argument2
+)
+/*++
+
+Routine Description:
+
+ This is the registry callback we'll register to intercept all registry
+ operations.
+
+Arguments:
+
+ CallbackContext - The value that the driver passed to the Context parameter
+ of CmRegisterCallbackEx when it registers this callback routine.
+
+ Argument1 - A REG_NOTIFY_CLASS typed value that identifies the type of
+ registry operation that is being performed and whether the callback
+ is being called in the pre or post phase of processing.
+
+ Argument2 - A pointer to a structure that contains information specific
+ to the type of the registry operation. The structure type depends
+ on the REG_NOTIFY_CLASS value of Argument1. Refer to MSDN for the
+ mapping from REG_NOTIFY_CLASS to REG_XXX_KEY_INFORMATION.
+
+Return Value:
+
+ Status returned from the helper callback routine or STATUS_SUCCESS if
+ the registry operation did not originate from this process.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ REG_NOTIFY_CLASS NotifyClass;
+ PCALLBACK_CONTEXT CallbackCtx;
+
+ CallbackCtx = (PCALLBACK_CONTEXT)CallbackContext;
+ NotifyClass = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
+
+ //
+ // Ignore registry activity from other processes. If this callback
+ // wasn't registered by the current process, simply return success.
+ //
+
+ if (CallbackCtx->ProcessId != PsGetCurrentProcessId()) {
+ return STATUS_SUCCESS;
+ }
+
+ InfoPrint("\tCallback: Altitude-%S, NotifyClass-%S.",
+ CallbackCtx->AltitudeBuffer,
+ GetNotifyClassString(NotifyClass));
+
+ //
+ // Invoke a helper method depending on the value of CallbackMode in
+ // CallbackCtx.
+ //
+
+ if (Argument2 == NULL) {
+
+ //
+ // This should never happen but the sal annotation on the callback
+ // function marks Argument 2 as opt and is looser than what
+ // it actually is.
+ //
+
+ ErrorPrint("\tCallback: Argument 2 unexpectedly 0. Filter will "
+ "abort and return success.");
+ return STATUS_SUCCESS;
+ }
+
+ switch (CallbackCtx->CallbackMode) {
+ case CALLBACK_MODE_PRE_NOTIFICATION_BLOCK:
+ Status = CallbackPreNotificationBlock(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_PRE_NOTIFICATION_BYPASS:
+ Status = CallbackPreNotificationBypass(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS:
+ Status = CallbackPostNotificationOverrideSuccess(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR:
+ Status = CallbackPostNotificationOverrideError(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_TRANSACTION_ENLIST:
+ Status = CallbackTransactionEnlist(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_TRANSACTION_REPLAY:
+ Status = CallbackTransactionReplay(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_SET_OBJECT_CONTEXT:
+ Status = CallbackSetObjectContext(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_SET_CALL_CONTEXT:
+ Status = CallbackSetCallContext(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR:
+ Status = CallbackMonitor(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE:
+ case CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION:
+ Status = CallbackMultipleAltitude(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_CAPTURE:
+ Status = CallbackCapture(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_VERSION_BUGCHECK:
+ Status = CallbackBugcheck(CallbackCtx, NotifyClass, Argument2);
+ break;
+ case CALLBACK_MODE_VERSION_CREATE_OPEN_V1:
+ Status = CallbackCreateOpenV1(CallbackCtx, NotifyClass, Argument2);
+ break;
+ default:
+ ErrorPrint("Unknown Callback Mode: %d", CallbackCtx->CallbackMode);
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+
+ return Status;
+
+}
+
+
+NTSTATUS
+RMCallback(
+ _In_ PKENLISTMENT EnlistmentObject,
+ _In_ PVOID RMContext,
+ _In_ PVOID TransactionContext,
+ _In_ ULONG TransactionNotification,
+ _Inout_ PLARGE_INTEGER TMVirtualClock,
+ _In_ ULONG ArgumentLength,
+ _In_ PVOID Argument
+ )
+/*++
+
+Routine Description:
+
+ This callback recieves transaction notifications.
+
+Arguments:
+
+ EnlistmentObject - Enlistment that this notification is about
+
+ RMContext - The value specified for the RMKey parameter of the
+ TmEnableCallbacks routine
+
+ TransactionContext - Value specified for the EnlistmentKey parameter
+ of the ZwCreateEnlistment routine
+
+ TransactionNotification - Type of notification
+
+ TmVirtualClock - Pointer to virtual clock value of time when KTM prepared
+ the notification.
+
+ ArgumentLength - Length in bytes of the Argument buffer.
+
+ Argument - Buffer containing notification-spcefic arguments.
+
+Return Value:
+
+ Always STATUS_SUCCESS
+
+--*/
+{
+ PRMCALLBACK_CONTEXT Context = (PRMCALLBACK_CONTEXT) TransactionContext;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(EnlistmentObject);
+ UNREFERENCED_PARAMETER(RMContext);
+ UNREFERENCED_PARAMETER(ArgumentLength);
+ UNREFERENCED_PARAMETER(Argument);
+
+ InfoPrint("\tRMCallback: NotifyClass-%S.",
+ GetTransactionNotifyClassString(TransactionNotification));
+
+ //
+ // Transaction notifications are bit masks. Record which one(s)
+ // this callback received.
+ //
+
+ Context->Notification |= TransactionNotification;
+
+ //
+ // Call the Tm*Complete methods to inform KTM that we have completed
+ // processing. (Note: It is possible to use the Zw version of
+ // these APIs as well).
+ //
+ // Make sure that all the notifications you request are handled. The
+ // type of notification this routine gets is specified when you enlist
+ // in a transaction.
+ //
+
+ switch(TransactionNotification) {
+ case TRANSACTION_NOTIFY_COMMIT:
+ Status = TmCommitComplete(EnlistmentObject,
+ TMVirtualClock);
+ break;
+ case TRANSACTION_NOTIFY_ROLLBACK:
+ Status = TmRollbackComplete(EnlistmentObject,
+ TMVirtualClock);
+ break;
+ default:
+ ErrorPrint("Unsupported Transaction Notification: %x",
+ TransactionNotification);
+ NT_ASSERT(FALSE);
+ }
+
+ //
+ // It is safe to close the enlistment handle here.
+ // Closing it before the transaction aborts or commits will abort
+ // the transaction.
+ //
+
+ if (Context->Enlistment != NULL) {
+ ZwClose(Context->Enlistment);
+ Context->Enlistment = NULL;
+ }
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+DoCallbackSamples(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine creates the root test key and then invokes the sample.
+ It records the results of each sample in an array that it returns to
+ the usermode program.
+
+Arguments:
+
+ DeviceObject - The device object receiving the request.
+
+ Irp - The request packet.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpStack;
+ ULONG OutputBufferLength;
+ PDO_KERNELMODE_SAMPLES_OUTPUT Output;
+ UNICODE_STRING KeyPath;
+ OBJECT_ATTRIBUTES KeyAttributes;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ //
+ // Get the output buffer from the irp and check it is as large as expected.
+ //
+
+ IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (OutputBufferLength < sizeof (DO_KERNELMODE_SAMPLES_OUTPUT)) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ Output = (PDO_KERNELMODE_SAMPLES_OUTPUT) Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Clean up test keys in case the sample terminated uncleanly.
+ //
+
+ DeleteTestKeys();
+
+ //
+ // Create the root key and the modified root key
+ //
+
+ RtlInitUnicodeString(&KeyPath, ROOT_KEY_ABS_PATH);
+ InitializeObjectAttributes(&KeyAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ Status = ZwCreateKey(&g_RootKey,
+ KEY_ALL_ACCESS,
+ &KeyAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ErrorPrint("ZwCreateKey failed. Status 0x%x", Status);
+ return Status;
+ }
+
+ //
+ // Call each demo and record the results in the Output->SampleResults
+ // array
+ //
+
+ Output->SampleResults[KERNELMODE_SAMPLE_PRE_NOTIFICATION_BLOCK] =
+ PreNotificationBlockSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_PRE_NOTIFICATION_BYPASS] =
+ PreNotificationBypassSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_SUCCESS] =
+ PostNotificationOverrideSuccessSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_ERROR] =
+ PostNotificationOverrideErrorSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_TRANSACTION_ENLIST] =
+ TransactionEnlistSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_TRANSACTION_REPLAY] =
+ TransactionReplaySample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_SET_CALL_CONTEXT] =
+ SetObjectContextSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_SET_OBJECT_CONTEXT] =
+ SetCallContextSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE] =
+ MultipleAltitudeBlockDuringPreSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION] =
+ MultipleAltitudeInternalInvocationSample();
+
+ Output->SampleResults[KERNELMODE_SAMPLE_VERSION_CREATE_OPEN_V1] =
+ CreateOpenV1Sample();
+
+ Irp->IoStatus.Information = sizeof(DO_KERNELMODE_SAMPLES_OUTPUT);
+
+ Exit:
+
+ if (g_RootKey) {
+ ZwDeleteKey(g_RootKey);
+ ZwClose(g_RootKey);
+ }
+
+ InfoPrint("");
+ InfoPrint("Kernel Mode Samples End");
+ InfoPrint("");
+
+ return Status;
+}
+
+
+NTSTATUS
+RegisterCallback(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ Registers a callback with the specified callback mode and altitude
+
+Arguments:
+
+ DeviceObject - The device object receiving the request.
+
+ Irp - The request packet.
+
+Return Value:
+
+ Status from CmRegisterCallbackEx
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IrpStack;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PREGISTER_CALLBACK_INPUT RegisterCallbackInput;
+ PREGISTER_CALLBACK_OUTPUT RegisterCallbackOutput;
+ PCALLBACK_CONTEXT CallbackCtx = NULL;
+
+ //
+ // Get the input and output buffer from the irp and
+ // check they are the expected size
+ //
+
+ IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
+ OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ((InputBufferLength < sizeof(REGISTER_CALLBACK_INPUT)) ||
+ (OutputBufferLength < sizeof (REGISTER_CALLBACK_OUTPUT))) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ RegisterCallbackInput = (PREGISTER_CALLBACK_INPUT) Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Create the callback context from the specified callback mode and altitude
+ //
+
+ CallbackCtx = CreateCallbackContext(RegisterCallbackInput->CallbackMode,
+ RegisterCallbackInput->Altitude);
+
+ if (CallbackCtx == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Register the callback
+ //
+
+ Status = CmRegisterCallbackEx(Callback,
+ &CallbackCtx->Altitude,
+ DeviceObject->DriverObject,
+ (PVOID) CallbackCtx,
+ &CallbackCtx->Cookie,
+ NULL);
+ if (!NT_SUCCESS(Status)) {
+ ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status);
+ goto Exit;
+ }
+
+ if (!InsertCallbackContext(CallbackCtx)) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto Exit;
+ }
+
+ //
+ // Fill the output buffer with the Cookie received from registering the
+ // callback and the pointer to the callback context.
+ //
+
+ RegisterCallbackOutput = (PREGISTER_CALLBACK_OUTPUT)Irp->AssociatedIrp.SystemBuffer;
+ RegisterCallbackOutput->Cookie = CallbackCtx->Cookie;
+ Irp->IoStatus.Information = sizeof(REGISTER_CALLBACK_OUTPUT);
+
+ Exit:
+ if (!NT_SUCCESS(Status)) {
+ ErrorPrint("RegisterCallback failed. Status 0x%x", Status);
+ if (CallbackCtx != NULL) {
+ DeleteCallbackContext(CallbackCtx);
+ }
+ } else {
+ InfoPrint("RegisterCallback succeeded");
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+UnRegisterCallback(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ Unregisters a callback with the specified cookie and clean up the
+ callback context.
+
+Arguments:
+
+ DeviceObject - The device object receiving the request.
+
+ Irp - The request packet.
+
+Return Value:
+
+ Status from CmUnRegisterCallback
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IrpStack;
+ ULONG InputBufferLength;
+ PUNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput;
+ PCALLBACK_CONTEXT CallbackCtx;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ //
+ // Get the input buffer and check its size
+ //
+
+ IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
+
+ if (InputBufferLength < sizeof(UNREGISTER_CALLBACK_INPUT)) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ UnRegisterCallbackInput = (PUNREGISTER_CALLBACK_INPUT) Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Unregister the callback with the cookie
+ //
+
+ Status = CmUnRegisterCallback(UnRegisterCallbackInput->Cookie);
+
+ if (!NT_SUCCESS(Status)) {
+ ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status);
+ goto Exit;
+ }
+
+ //
+ // Free the callback context buffer
+ //
+ CallbackCtx = FindAndRemoveCallbackContext(UnRegisterCallbackInput->Cookie);
+ if (CallbackCtx != NULL) {
+ DeleteCallbackContext(CallbackCtx);
+ }
+
+ Exit:
+
+ if (!NT_SUCCESS(Status)) {
+ ErrorPrint("UnRegisterCallback failed. Status 0x%x", Status);
+ } else {
+ InfoPrint("UnRegisterCallback succeeded");
+ }
+ InfoPrint("");
+
+ return Status;
+
+}
+
+
+NTSTATUS
+GetCallbackVersion(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ Calls CmGetCallbackVersion
+
+Arguments:
+
+ DeviceObject - The device object receiving the request.
+
+ Irp - The request packet.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION IrpStack;
+ ULONG OutputBufferLength;
+ PGET_CALLBACK_VERSION_OUTPUT GetCallbackVersionOutput;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ //
+ // Get the output buffer and verify its size
+ //
+
+ IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (OutputBufferLength < sizeof(GET_CALLBACK_VERSION_OUTPUT)) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ GetCallbackVersionOutput = (PGET_CALLBACK_VERSION_OUTPUT) Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Call CmGetCallbackVersion and store the results in the output buffer
+ //
+
+ CmGetCallbackVersion(&GetCallbackVersionOutput->MajorVersion,
+ &GetCallbackVersionOutput->MinorVersion);
+
+ Irp->IoStatus.Information = sizeof(GET_CALLBACK_VERSION_OUTPUT);
+
+ Exit:
+
+ if (!NT_SUCCESS(Status)) {
+ ErrorPrint("GetCallbackVersion failed. Status 0x%x", Status);
+ } else {
+ InfoPrint("GetCallbackVersion succeeded");
+ }
+
+ return Status;
+}
+
+
+LPCWSTR
+GetNotifyClassString (
+ _In_ REG_NOTIFY_CLASS NotifyClass
+ )
+/*++
+
+Routine Description:
+
+ Converts from NotifyClass to a string
+
+Arguments:
+
+ NotifyClass - value that identifies the type of registry operation that
+ is being performed
+
+Return Value:
+
+ Returns a string of the name of NotifyClass.
+
+--*/
+{
+ switch (NotifyClass) {
+ case RegNtPreDeleteKey: return L"RegNtPreDeleteKey";
+ case RegNtPreSetValueKey: return L"RegNtPreSetValueKey";
+ case RegNtPreDeleteValueKey: return L"RegNtPreDeleteValueKey";
+ case RegNtPreSetInformationKey: return L"RegNtPreSetInformationKey";
+ case RegNtPreRenameKey: return L"RegNtPreRenameKey";
+ case RegNtPreEnumerateKey: return L"RegNtPreEnumerateKey";
+ case RegNtPreEnumerateValueKey: return L"RegNtPreEnumerateValueKey";
+ case RegNtPreQueryKey: return L"RegNtPreQueryKey";
+ case RegNtPreQueryValueKey: return L"RegNtPreQueryValueKey";
+ case RegNtPreQueryMultipleValueKey: return L"RegNtPreQueryMultipleValueKey";
+ case RegNtPreKeyHandleClose: return L"RegNtPreKeyHandleClose";
+ case RegNtPreCreateKeyEx: return L"RegNtPreCreateKeyEx";
+ case RegNtPreOpenKeyEx: return L"RegNtPreOpenKeyEx";
+ case RegNtPreFlushKey: return L"RegNtPreFlushKey";
+ case RegNtPreLoadKey: return L"RegNtPreLoadKey";
+ case RegNtPreUnLoadKey: return L"RegNtPreUnLoadKey";
+ case RegNtPreQueryKeySecurity: return L"RegNtPreQueryKeySecurity";
+ case RegNtPreSetKeySecurity: return L"RegNtPreSetKeySecurity";
+ case RegNtPreRestoreKey: return L"RegNtPreRestoreKey";
+ case RegNtPreSaveKey: return L"RegNtPreSaveKey";
+ case RegNtPreReplaceKey: return L"RegNtPreReplaceKey";
+
+ case RegNtPostDeleteKey: return L"RegNtPostDeleteKey";
+ case RegNtPostSetValueKey: return L"RegNtPostSetValueKey";
+ case RegNtPostDeleteValueKey: return L"RegNtPostDeleteValueKey";
+ case RegNtPostSetInformationKey: return L"RegNtPostSetInformationKey";
+ case RegNtPostRenameKey: return L"RegNtPostRenameKey";
+ case RegNtPostEnumerateKey: return L"RegNtPostEnumerateKey";
+ case RegNtPostEnumerateValueKey: return L"RegNtPostEnumerateValueKey";
+ case RegNtPostQueryKey: return L"RegNtPostQueryKey";
+ case RegNtPostQueryValueKey: return L"RegNtPostQueryValueKey";
+ case RegNtPostQueryMultipleValueKey: return L"RegNtPostQueryMultipleValueKey";
+ case RegNtPostKeyHandleClose: return L"RegNtPostKeyHandleClose";
+ case RegNtPostCreateKeyEx: return L"RegNtPostCreateKeyEx";
+ case RegNtPostOpenKeyEx: return L"RegNtPostOpenKeyEx";
+ case RegNtPostFlushKey: return L"RegNtPostFlushKey";
+ case RegNtPostLoadKey: return L"RegNtPostLoadKey";
+ case RegNtPostUnLoadKey: return L"RegNtPostUnLoadKey";
+ case RegNtPostQueryKeySecurity: return L"RegNtPostQueryKeySecurity";
+ case RegNtPostSetKeySecurity: return L"RegNtPostSetKeySecurity";
+ case RegNtPostRestoreKey: return L"RegNtPostRestoreKey";
+ case RegNtPostSaveKey: return L"RegNtPostSaveKey";
+ case RegNtPostReplaceKey: return L"RegNtPostReplaceKey";
+
+ case RegNtCallbackObjectContextCleanup: return L"RegNtCallbackObjectContextCleanup";
+
+ default:
+ return L"Unsupported REG_NOTIFY_CLASS";
+ }
+}
+
+
+LPCWSTR
+GetTransactionNotifyClassString (
+ _In_ ULONG TransactionNotifcation
+ )
+/*++
+
+Routine Description:
+
+ Converts from TransactionNotification to a string
+
+Arguments:
+
+ TransactionNotification - value that identifies the type of
+ transaction notification
+
+Return Value:
+
+ Returns a string of the name of TransactionNotification
+
+--*/
+{
+ switch (TransactionNotifcation) {
+ case TRANSACTION_NOTIFY_COMMIT: return L"TRANSACTION_NOTIFY_COMMIT";
+ case TRANSACTION_NOTIFY_ROLLBACK: return L"TRANSACTION_NOTIFY_ROLLBACK";
+
+ default:
+ return L"Unsupported Transaction Notification";
+ }
+}
+
+
+
+VOID
+DeleteTestKeys(
+ )
+/*++
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING KeyPath;
+ OBJECT_ATTRIBUTES KeyAttributes;
+ HANDLE RootKey = NULL;
+ HANDLE ChildKey = NULL;
+
+ //
+ // Check if the root key can be opened. If it can be opened, a previous
+ // run must have not completed cleanly. Delete the key and recreate the
+ // root key.
+ //
+
+ RtlInitUnicodeString(&KeyPath, ROOT_KEY_ABS_PATH);
+ InitializeObjectAttributes(&KeyAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ Status = ZwOpenKey(&RootKey,
+ KEY_ALL_ACCESS,
+ &KeyAttributes);
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ return;
+ } else if (!NT_SUCCESS(Status)) {
+ ErrorPrint("Opening root key fails with unexpected status %x.", Status);
+ }
+
+ RtlInitUnicodeString(&KeyPath, KEY_NAME);
+ InitializeObjectAttributes(&KeyAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ RootKey,
+ NULL);
+
+ Status = ZwOpenKey(&ChildKey,
+ KEY_ALL_ACCESS,
+ &KeyAttributes);
+
+ if (NT_SUCCESS(Status)) {
+ ZwDeleteKey(ChildKey);
+ ZwClose(ChildKey);
+ ChildKey = NULL;
+ } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+ ErrorPrint("Opening %S key fails with unexpected status %x.",
+ KEY_NAME,
+ Status);
+ }
+
+ RtlInitUnicodeString(&KeyPath, NOT_MODIFIED_KEY_NAME);
+ InitializeObjectAttributes(&KeyAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ RootKey,
+ NULL);
+
+ Status = ZwOpenKey(&ChildKey,
+ KEY_ALL_ACCESS,
+ &KeyAttributes);
+
+ if (NT_SUCCESS(Status)) {
+ ZwDeleteKey(ChildKey);
+ ZwClose(ChildKey);
+ ChildKey = NULL;
+ } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+ ErrorPrint("Opening %S key fails with unexpected status %x.",
+ NOT_MODIFIED_KEY_NAME,
+ Status);
+ }
+
+ RtlInitUnicodeString(&KeyPath, MODIFIED_KEY_NAME);
+ InitializeObjectAttributes(&KeyAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ RootKey,
+ NULL);
+
+ Status = ZwOpenKey(&ChildKey,
+ KEY_ALL_ACCESS,
+ &KeyAttributes);
+
+ if (NT_SUCCESS(Status)) {
+ ZwDeleteKey(ChildKey);
+ ZwClose(ChildKey);
+ ChildKey = NULL;
+ } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+ ErrorPrint("Opening %S key fails with unexpected status %x.",
+ MODIFIED_KEY_NAME,
+ Status);
+ }
+
+ ZwDeleteKey(RootKey);
+ ZwClose(RootKey);
+
+ return;
+
+}
diff --git a/general/registry/regfltr/sys/regfltr.h b/general/registry/regfltr/sys/regfltr.h new file mode 100644 index 00000000..d8787ff2 --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.h @@ -0,0 +1,488 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regfltr.h + +Abstract: + + Header file for the sample driver + +Environment: + + Kernel mode only + + +--*/ + +#pragma once + +#include <ntifs.h> +#include <ntstrsafe.h> +#include <wdmsec.h> + +#include "common.h" + + +// +// Pool tags +// + +#define REGFLTR_CONTEXT_POOL_TAG '0tfR' +#define REGFLTR_CAPTURE_POOL_TAG '1tfR' + + +// +// Logging macros +// + +#define InfoPrint(str, ...) \ + DbgPrintEx(DPFLTR_IHVDRIVER_ID, \ + DPFLTR_INFO_LEVEL, \ + "%S: "##str"\n", \ + DRIVER_NAME, \ + __VA_ARGS__) + +#define ErrorPrint(str, ...) \ + DbgPrintEx(DPFLTR_IHVDRIVER_ID, \ + DPFLTR_ERROR_LEVEL, \ + "%S: %d: "##str"\n", \ + DRIVER_NAME, \ + __LINE__, \ + __VA_ARGS__) + + +// +// The root key used in the samples +// +extern HANDLE g_RootKey; + + +// +// Pointer to the device object used to register registry callbacks +// +extern PDEVICE_OBJECT g_DeviceObj; + + +// +// Registry callback version +// +extern ULONG g_MajorVersion; +extern ULONG g_MinorVersion; + + +// +// Set to TRUE if TM and RM were successfully created and the transaction +// callback was successfully enabled. +// +extern BOOLEAN g_RMCreated; + + +// +// Flag that indicates if the system is win8 or higher. This is set on +// driver entry by calling RtlVerifyVersionInfo. +// +extern BOOLEAN g_IsWin8OrGreater; + + +// +// The following are variables used to manage callback contexts handed +// out to user mode. +// + +#define MAX_CALLBACK_CTX_ENTRIES 10 + +// +// The fast mutex guarding the callback context list +// +extern FAST_MUTEX g_CallbackCtxListLock; + +// +// The list head +// +extern LIST_ENTRY g_CallbackCtxListHead; + +// +// Count of entries in list +// +extern USHORT g_NumCallbackCtxListEntries; + +// +// Context data structure for the transaction callback RMCallback +// + +typedef struct _RMCALLBACK_CONTEXT { + + // + // A bit mask of all transaction notifications types that the RM Callback is + // notified of. + // + ULONG Notification; + + // + // The handle to an enlistment + // + HANDLE Enlistment; + +} RMCALLBACK_CONTEXT, *PRMCALLBACK_CONTEXT; + + +// +// The context data structure for the registry callback. It will be passed +// to the callback function every time it is called. +// + +typedef struct _CALLBACK_CONTEXT { + + // + // List of callback contexts currently active + // + LIST_ENTRY CallbackCtxList; + + // + // Specifies which callback helper method to use + // + CALLBACK_MODE CallbackMode; + + // + // Records the current ProcessId to filter out registry operation from + // other processes. + // + HANDLE ProcessId; + + // + // Records the altitude that the callback was registered at + // + UNICODE_STRING Altitude; + WCHAR AltitudeBuffer[MAX_ALTITUDE_BUFFER_LENGTH]; + + // + // Records the cookie returned by the registry when the callback was + // registered + // + LARGE_INTEGER Cookie; + + // + // A pointer to the context for the transaction callback. + // Used to enlist on a transaction. Only used in the transaction samples. + // + PRMCALLBACK_CONTEXT RMCallbackCtx; + + // + // These fields record information for verifying the behavior of the + // certain samples. They are not used in all samples + // + + // + // Number of times the RegNtCallbackObjectContextCleanup + // notification was received + // + LONG ContextCleanupCount; + + // + // Number of times the callback saw a notification with the call or + // object context set correctly. + // + LONG NotificationWithContextCount; + + // + // Number of times callback saw a notirication without call or without + // object context set correctly + // + LONG NotificationWithNoContextCount; + + // + // Number of pre-notifications received + // + LONG PreNotificationCount; + + // + // Number of post-notifications received + // + LONG PostNotificationCount; + +} CALLBACK_CONTEXT, *PCALLBACK_CONTEXT; + + +// +// The registry and transaction callback routines +// + +EX_CALLBACK_FUNCTION Callback; + +NTSTATUS +RMCallback( + _In_ PKENLISTMENT EnlistmentObject, + _In_ PVOID RMContext, + _In_ PVOID TransactionContext, + _In_ ULONG TransactionNotification, + _Inout_ PLARGE_INTEGER TMVirtualClock, + _In_ ULONG ArgumentLength, + _In_ PVOID Argument + ); + +// +// The samples and their corresponding callback helper methods +// + +NTSTATUS +CallbackPreNotificationBlock( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PreNotificationBlockSample(); + +NTSTATUS +CallbackPreNotificationBlock( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PreNotificationBypassSample(); + +NTSTATUS +CallbackPreNotificationBypass( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PostNotificationOverrideSuccessSample(); + +NTSTATUS +CallbackPostNotificationOverrideSuccess( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PostNotificationOverrideErrorSample(); + +NTSTATUS +CallbackPostNotificationOverrideError( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +TransactionEnlistSample(); + +NTSTATUS +CallbackTransactionEnlist( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +TransactionReplaySample(); + +NTSTATUS +CallbackTransactionReplay( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +SetObjectContextSample(); + +NTSTATUS +CallbackSetObjectContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +SetCallContextSample(); + +NTSTATUS +CallbackSetCallContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +MultipleAltitudeBlockDuringPreSample(); + +BOOLEAN +MultipleAltitudeInternalInvocationSample(); + +NTSTATUS +CallbackMonitor( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +NTSTATUS +CallbackMultipleAltitude( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +NTSTATUS +CallbackCapture( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +VOID +BugCheckSample(); + +NTSTATUS +CallbackBugcheck( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +CreateOpenV1Sample(); + +NTSTATUS +CallbackCreateOpenV1( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +// +// Driver dispatch functions +// + +NTSTATUS +DoCallbackSamples( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +NTSTATUS +RegisterCallback( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +NTSTATUS +UnRegisterCallback( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +NTSTATUS +GetCallbackVersion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +// +// Transaction related routines +// + +NTSTATUS +CreateKTMResourceManager( + _In_ PTM_RM_NOTIFICATION CallbackRoutine, + _In_opt_ PVOID RMKey + ); + +NTSTATUS +EnlistInTransaction( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PVOID Transaction, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ); + +VOID +DeleteKTMResourceManager( + ); + + +// +// Capture methods +// + +NTSTATUS +CaptureBuffer( + _Outptr_result_maybenull_ PVOID *CapturedBuffer, + _In_reads_bytes_(Length)PVOID Buffer, + _In_ SIZE_T Length, + _In_ ULONG PoolTag + ); + +VOID +FreeCapturedBuffer( + _In_ PVOID Buffer, + _In_ ULONG PoolTag + ); + +NTSTATUS +CaptureUnicodeString( + _Inout_ UNICODE_STRING * DestString, + _In_ PCUNICODE_STRING SourceString, + _In_ ULONG PoolTag + ); + +VOID +FreeCapturedUnicodeString( + _In_ UNICODE_STRING * String, + _In_ ULONG PoolTag + ); + + +// +// Utility methods +// + +PVOID +CreateCallbackContext( + _In_ CALLBACK_MODE CallbackMode, + _In_ PCWSTR AltitudeString + ); + +BOOLEAN +InsertCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ); + +PCALLBACK_CONTEXT +FindCallbackContext( + _In_ LARGE_INTEGER Cookie + ); + +PCALLBACK_CONTEXT +FindAndRemoveCallbackContext( + _In_ LARGE_INTEGER Cookie + ); + +VOID +DeleteCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ); + + +ULONG +ExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointers + ); + + diff --git a/general/registry/regfltr/sys/regfltr.rc b/general/registry/regfltr/sys/regfltr.rc new file mode 100644 index 00000000..67bc9a4b --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.rc @@ -0,0 +1,11 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Registry Filter System Driver" +#define VER_INTERNALNAME_STR "regfltr.sys" +#define VER_ORIGINALFILENAME_STR "RegFltr.sys" + +#include "common.ver"
\ No newline at end of file diff --git a/general/registry/regfltr/sys/regfltr.vcxproj b/general/registry/regfltr/sys/regfltr.vcxproj new file mode 100644 index 00000000..6bddb5e4 --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.vcxproj @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{12666DFF-2CD6-4000-AFE6-0796D9B6D330}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{3973A2A5-EFFD-4997-985D-51731D2A58CA}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>regfltr</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>regfltr</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>regfltr</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>regfltr</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + <Optimization>Disabled</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + <Optimization>Disabled</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + <Optimization>Disabled</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + <Optimization>Disabled</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="Capture.c" /> + <ClCompile Include="Context.c" /> + <ClCompile Include="driver.c" /> + <ClCompile Include="MultiAlt.c" /> + <ClCompile Include="Post.c" /> + <ClCompile Include="Pre.c" /> + <ClCompile Include="regfltr.c" /> + <ClCompile Include="TxR.c" /> + <ClCompile Include="TxRUtil.c" /> + <ClCompile Include="Util.c" /> + <ClCompile Include="Version.c" /> + <ResourceCompile Include="regfltr.rc" /> + </ItemGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/general/registry/regfltr/sys/regfltr.vcxproj.Filters b/general/registry/regfltr/sys/regfltr.vcxproj.Filters new file mode 100644 index 00000000..22722767 --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.vcxproj.Filters @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{2B50099F-1EBF-480D-9136-448FBDF31F1F}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{1001336B-4543-45F1-A020-C5E84A980E24}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{9EEC1AF7-63F4-44AB-81FA-A0DAC270B821}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{AE93F2E1-863E-4BAE-BC5D-09810656976E}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="Capture.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Context.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="driver.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MultiAlt.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Post.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Pre.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="regfltr.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TxR.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TxRUtil.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Util.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Version.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="regfltr.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/general/registry/regfltr/sys/txr.c b/general/registry/regfltr/sys/txr.c new file mode 100644 index 00000000..6fd3c8a6 --- /dev/null +++ b/general/registry/regfltr/sys/txr.c @@ -0,0 +1,784 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TxR.c + +Abstract: + + Samples that show how to deal with transactional registry operations. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +TransactionEnlistSample( + ) +/*++ + +Routine Description: + + This sample shows how to enlist to a transaction that a registry operation + is part of inorder to get notifications when it commits or aborts. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ + +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + PRMCALLBACK_CONTEXT RMCallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + OBJECT_ATTRIBUTES TxAttributes; + HANDLE Key = NULL; + HANDLE Transaction = NULL; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Transaction Enlist Sample ===="); + + if (!g_RMCreated) { + ErrorPrint("Sample can't run because KTM data structures were not successfully created."); + goto Exit; + } + + // + // Create the registry callback context and the transaction callback context. + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_TRANSACTION_ENLIST, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + RMCallbackCtx = (PRMCALLBACK_CONTEXT) ExAllocatePoolWithTag ( + PagedPool, + sizeof(RMCALLBACK_CONTEXT), + REGFLTR_CONTEXT_POOL_TAG); + if (RMCallbackCtx == NULL) { + goto Exit; + } + RtlZeroMemory(RMCallbackCtx, sizeof(RMCALLBACK_CONTEXT)); + CallbackCtx->RMCallbackCtx = RMCallbackCtx; + + // + // Create a transaction + // + + InitializeObjectAttributes(&TxAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateTransaction(&Transaction, + TRANSACTION_ALL_ACCESS, + &TxAttributes, + NULL, + NULL, + 0, + 0, + 0, + NULL, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateTransaction failed. Status 0x%x", Status); + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Success = TRUE; + + // + // Create a key + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKeyTransacted(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + Transaction, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKeyTransacted failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Commit the transaction + // + + Status = ZwCommitTransaction(Transaction, TRUE); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCommitTransaction failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Check that the transaction callback context records a commit notification + // + + if (RMCallbackCtx->Notification != TRANSACTION_NOTIFY_COMMIT) { + ErrorPrint("RMContext notification mask is 0x%x instead of 0x%x.", + RMCallbackCtx->Notification, + TRANSACTION_NOTIFY_COMMIT); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Transaction != NULL) { + ZwClose(Transaction); + } + + // + // Need to reopen the key to delete it because the previous + // handle was part of a transaction that is now gone. + // + + if (Key != NULL) { + ZwClose(Key); + Key = NULL; + } + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + ZwOpenKey(&Key, KEY_ALL_ACCESS, &KeyAttributes); + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (RMCallbackCtx != NULL) { + ExFreePoolWithTag(RMCallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Transaction Enlist Demo succeeded."); + } else { + ErrorPrint("Transaction Enlist Demo FAILED."); + } + + return Success; +} + + +NTSTATUS +CallbackTransactionEnlist( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine shows hot to enlist on a transaction. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PVOID Transaction = NULL; + + switch(NotifyClass) { + + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Get the transaction object + // + + Transaction = PreCreateInfo->Transaction; + if (Transaction == NULL) { + + // + // Even if the transaction is not provided in the + // REG_Xxx_INFORMATION, we need to call CmGetBoundTransaction + // on the RootObject to check that it isn't associated with + // a transaction. + // + + Transaction = CmGetBoundTransaction(&CallbackCtx->Cookie, + PreCreateInfo->RootObject); + + if (Transaction == NULL) { + ErrorPrint("CreateKey is unexpectedly not transacted."); + break; + } + } + + // + // Use the volatile RM created in CreateKTMResourceManager() + // to enlist in the transaction. We want notifications for + // when the transaction commits or rolls back. + // + // Note: Make sure the callback routine handles all the + // notifications requested here. Look at RMCallback() to see + // how to handle notifications. + // + + Status = EnlistInTransaction(&CallbackCtx->RMCallbackCtx->Enlistment, + ENLISTMENT_SUBORDINATE_RIGHTS, + Transaction, + TRANSACTION_NOTIFY_COMMIT | + TRANSACTION_NOTIFY_ROLLBACK, + CallbackCtx->RMCallbackCtx); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("EnlistInTransaction failed. Status 0x%x.", Status); + } + + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + + + +BOOLEAN +TransactionReplaySample( + ) +/*++ + +Routine Description: + + This sample shows how to copy a transactional create key operation. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ + +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + OBJECT_ATTRIBUTES TxAttributes; + HANDLE Key = NULL; + HANDLE Transaction = NULL; + HANDLE TransactedRoot = NULL; + BOOLEAN bSuccess = FALSE; + + InfoPrint(""); + InfoPrint("=== Transaction Replay Sample ===="); + + if (!g_RMCreated) { + ErrorPrint("Sample can't run because KTM data structures were not successfully created."); + goto Exit; + } + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_TRANSACTION_REPLAY, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Create a transaction + // + + InitializeObjectAttributes(&TxAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateTransaction(&Transaction, + TRANSACTION_ALL_ACCESS, + &TxAttributes, + NULL, + NULL, + 0, + 0, + 0, + NULL, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateTransaction failed. Status 0x%x", Status); + goto Exit; + } + + // + // Open a transacted handle to the root key + // + + RtlInitUnicodeString(&Name, ROOT_KEY_ABS_PATH); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenKeyTransacted(&TransactedRoot, + KEY_ALL_ACCESS, + &KeyAttributes, + Transaction); + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKeyTransacted failed. Status 0x%x",Status); + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + bSuccess = TRUE; + + // + // Create a key using the transacted root key handle. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + TransactedRoot, + NULL); + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + bSuccess = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + bSuccess = FALSE; + } + + + // + // Verify that the key created exists and that a key with the + // "modified" name is also exists. + // + + if (Key != NULL) { + ZwClose(Key); + Key = NULL; + } + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + bSuccess = FALSE; + } else { + ZwClose(Key); + Key = NULL; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + TransactedRoot, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + bSuccess = FALSE; + } else { + ZwClose(Key); + Key = NULL; + } + + // + // Roll back transaction + // + + Status = ZwRollbackTransaction(Transaction, TRUE); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwRollbackTransaction failed. Status 0x%x", Status); + bSuccess = FALSE; + goto Exit; + } + + // + // Check that both keys no longer exist. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x. Expected 0x%x", + Status, + STATUS_OBJECT_NAME_NOT_FOUND); + bSuccess = FALSE; + } + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + Key = NULL; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x. Expected 0x%x", + Status, + STATUS_OBJECT_NAME_NOT_FOUND); + bSuccess = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (TransactedRoot!= NULL) { + ZwDeleteKey(TransactedRoot); + ZwClose(TransactedRoot); + } + + if (Transaction != NULL) { + ZwClose(Transaction); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (bSuccess) { + InfoPrint("Transaction Replay Sample succeeded."); + } else { + ErrorPrint("Transaction Replay Sample FAILED."); + } + + return bSuccess; + +} + + + +NTSTATUS +CallbackTransactionReplay( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine shows how to get the transaction associated + with a registry operation and shows how to do another operation in the + same transaction. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + HANDLE TransactionHandle = NULL; + PVOID Transaction = NULL; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + UNICODE_STRING LocalClass = {0}; + PUNICODE_STRING Class = NULL; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + KPROCESSOR_MODE Mode = KernelMode; + + switch(NotifyClass) { + + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Get the transaction object + // + + Transaction = PreCreateInfo->Transaction; + if (Transaction == NULL) { + + // + // Even if the transaction is not provided in the + // REG_Xxx_INFORMATION, we need to call CmGetBoundTransaction + // on the RootObject to check that it isn't associated with + // a transaction. + // + + Transaction = CmGetBoundTransaction(&CallbackCtx->Cookie, + PreCreateInfo->RootObject); + if (Transaction == NULL) { + ErrorPrint("CreateKey is unexpectedly not transacted."); + break; + } + } + + // + // Get a handle to the transaction object + // + + Status = ObOpenObjectByPointer(Transaction, + OBJ_KERNEL_HANDLE, + NULL, + TRANSACTION_ALL_ACCESS, + *TmTransactionObjectType, + KernelMode, + &TransactionHandle); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Next replay the create key using the transacted version of the + // API and the transaction handle. + // + + Status = ObOpenObjectByPointer(PreCreateInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, // Getting handle with all access + PreCreateInfo->ObjectType, + KernelMode, + &RootKey); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByPointer failed. Status 0x%x", Status); + break; + } + + + // + // REG_CREATE_KEY_INFORMATION is a partially structure. The class + // field's buffer is not captured. Since it is passed to + // ZwCreateKeyTransacted, it needs to be captured. + // + // *Note: in Windows 8 all fields are captured. See capture.c + // for more details. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && Mode == UserMode) { + Status = CaptureUnicodeString(&LocalClass, + PreCreateInfo->Class, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Class = &LocalClass; + + } else { + Class = PreCreateInfo->Class; + } + + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + PreCreateInfo->SecurityDescriptor); + + Status = ZwCreateKeyTransacted(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + Class, + PreCreateInfo->CreateOptions, + TransactionHandle, + PreCreateInfo->Disposition); + + ZwClose(RootKey); + ZwClose(TransactionHandle); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKeyTransacted failed. Status 0x%x.", Status); + break; + } + + ZwClose(Key); + InfoPrint("\tCallback: Create key %wZ replayed in same transaction context.", + PreCreateInfo->CompleteName); + Status = STATUS_SUCCESS; + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + // + // Free buffers used for capturing user mode values. + // + + if (LocalClass.Buffer != NULL) { + FreeCapturedUnicodeString(&LocalClass, REGFLTR_CAPTURE_POOL_TAG); + } + + return Status; +} + diff --git a/general/registry/regfltr/sys/txrutil.c b/general/registry/regfltr/sys/txrutil.c new file mode 100644 index 00000000..337e5cc4 --- /dev/null +++ b/general/registry/regfltr/sys/txrutil.c @@ -0,0 +1,283 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + txrutil.c + +Abstract: + + Utility functions for working with transaction registry operations + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +static HANDLE ResourceManager = NULL; +static HANDLE TransactionManager = NULL; + + +NTSTATUS +CreateKTMResourceManager( + _In_ PTM_RM_NOTIFICATION CallbackRoutine, + _In_opt_ PVOID RMKey + ) +/*++ + +Routine Description: + + This method will create a volatile Transaction Manager (TM) and a volatile + Resource Manager (RM) and enable callback notification through them. + The RM created here is used to enlist onto a transaction so that the + RMCallback routine will be called when the transaction commits or aborts. + +Arguments: + + CallbackRoutine - Pointer to a ResourceManagerNotification Routine + + RMKey - A caller-defined context value that uniquely identifies the + resource manager. The callback routine receives this value as + input. + Note: When you are enlisting to a transaction, you can pass in a + context that is specific to that particular enlistment. + +Return Value: + + NTSTATUS + +--*/ +{ + OBJECT_ATTRIBUTES ObjAttributes; + PKRESOURCEMANAGER RMObject; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE TMHandle = NULL; + HANDLE RMHandle = NULL; + GUID RMGuid; + + InfoPrint("Creating KTM Resource Manager"); + + // + // Create the volatile TM + // + + InitializeObjectAttributes(&ObjAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateTransactionManager(&TMHandle, + TRANSACTIONMANAGER_ALL_ACCESS, + &ObjAttributes, + NULL, + TRANSACTION_MANAGER_VOLATILE, + 0); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateTransactionManager failed. Status 0x%x", Status); + goto Exit; + } + + // + // Create the volatile RM + // + + Status = ExUuidCreate(&RMGuid); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ExUuidCreate failed. Status 0x%x", Status); + goto Exit; + } + + InitializeObjectAttributes(&ObjAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateResourceManager(&RMHandle, + RESOURCEMANAGER_ALL_ACCESS, + TMHandle, + &RMGuid, + &ObjAttributes, + + RESOURCE_MANAGER_VOLATILE, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateResourceManager failed. Status 0x%x", Status); + goto Exit; + } + + // + // Grab the RM object from the handle + // + + Status = ObReferenceObjectByHandle(RMHandle, + 0, + NULL, + KernelMode, + (PVOID *) &RMObject, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ObReferenceObjectbyHandle failed. Status 0x%x", Status); + goto Exit; + } + + // + // Enable callbacks and pass in our notification routine + // + + Status = TmEnableCallbacks(RMObject, + CallbackRoutine, + RMKey); + + ObDereferenceObject(RMObject); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("TmEnableCallbacks failed. Status 0x%x", Status); + goto Exit; + } + + Exit: + + if (!NT_SUCCESS(Status)) { + if (RMHandle != NULL) { + ZwClose(RMHandle); + } + if (TMHandle!= NULL) { + ZwClose(TMHandle); + } + } else { + ResourceManager = RMHandle; + TransactionManager = TMHandle; + } + + return Status; +} + + + +NTSTATUS +EnlistInTransaction( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PVOID Transaction, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ) +/*++ + +Routine Description: + + This method is a wrapper around ZwCreateEnlistment. It outputs a handle + to the enlistment object which represent's a resource manager's + enlistment to a transaction. Enlisting to a transaction allows the + resource manager to receive notifications about a transaction's events. + +Arguments: + + EnlistmentHandle - Pointer to variable that receives the handle to the + new enlistment object. + + DesiredAccess - Specifies the requested access to the enlistment object. + + Transaction - Transaction object + + NotificationMask - A bitwise OR of TRANSACTION_NOTIFY_Xxx values defined + in Ktmtypes.h. It specifies the types of transaction + notifications that KTM will send to the caller. + + EnlistmentKey - A caller-defined context value that uniquely identifies the + enlistment. The callback routine registered when callbacks + were enabled in the resource manager receives this value. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status; + HANDLE TransactionHandle = NULL; + OBJECT_ATTRIBUTES ObjAttributes; + + // + // Get a handle to the transaction object + // + + Status = ObOpenObjectByPointer(Transaction, + OBJ_KERNEL_HANDLE, + NULL, + TRANSACTION_ALL_ACCESS, + *TmTransactionObjectType, + KernelMode, + &TransactionHandle); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ObOpenObjectByPointer failed. Status 0x%x.", Status); + return Status; + } + + // + // Use the transaction handle and the volatile RM created in + // CreateKTMResourceManager() to enlist to the transaction. + // + + InitializeObjectAttributes(&ObjAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateEnlistment(EnlistmentHandle, + DesiredAccess, + ResourceManager, + TransactionHandle, + &ObjAttributes, + 0, + NotificationMask, + EnlistmentKey); + + ZwClose(TransactionHandle); + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateEnlistment failed. Status 0x%x", Status); + } + + return Status; + +} + + +VOID +DeleteKTMResourceManager( + ) +/*++ + +Routine Description: + + Clean up any resources associated wtih the resource manager. + +--*/ +{ + if (ResourceManager != NULL) { + ZwClose(ResourceManager); + ResourceManager = NULL; + } + if (TransactionManager != NULL) { + ZwClose(TransactionManager); + TransactionManager = NULL; + } +} diff --git a/general/registry/regfltr/sys/util.c b/general/registry/regfltr/sys/util.c new file mode 100644 index 00000000..068416c8 --- /dev/null +++ b/general/registry/regfltr/sys/util.c @@ -0,0 +1,304 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + util.c + +Abstract: + + Utility routines for the sample driver. + +Environment: + + Kernel mode only + +--*/ + + +#include "regfltr.h" + + +FAST_MUTEX g_CallbackCtxListLock; +LIST_ENTRY g_CallbackCtxListHead; +USHORT g_NumCallbackCtxListEntries; + + +ULONG +ExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointers + ) +/*++ + +Routine Description: + + ExceptionFilter breaks into the debugger if an exception happens + inside the callback. + +Arguments: + + ExceptionPointers - unused + +Return Value: + + Always returns EXCEPTION_CONTINUE_SEARCH + +--*/ +{ + + ErrorPrint("Exception %lx, ExceptionPointers = %p", + ExceptionPointers->ExceptionRecord->ExceptionCode, + ExceptionPointers); + + DbgBreakPoint(); + + return EXCEPTION_EXECUTE_HANDLER; + +} + + +PVOID +CreateCallbackContext( + _In_ CALLBACK_MODE CallbackMode, + _In_ PCWSTR AltitudeString + ) +/*++ + +Routine Description: + + Utility method to create a callback context. Callback context + should be freed using DeleteCallbackContext. + +Arguments: + + CallbackMode - the callback mode value + + AltitudeString - a string with the altitude the callback will be + registered at + +Return Value: + + Pointer to the allocated and initialized callback context + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + BOOLEAN Success = FALSE; + + CallbackCtx = (PCALLBACK_CONTEXT) ExAllocatePoolWithTag ( + PagedPool, + sizeof(CALLBACK_CONTEXT), + REGFLTR_CONTEXT_POOL_TAG); + + if (CallbackCtx == NULL) { + ErrorPrint("CreateCallbackContext failed due to insufficient resources."); + goto Exit; + } + + RtlZeroMemory(CallbackCtx, sizeof(CALLBACK_CONTEXT)); + + CallbackCtx->CallbackMode = CallbackMode; + CallbackCtx->ProcessId = PsGetCurrentProcessId(); + + Status = RtlStringCbPrintfW(CallbackCtx->AltitudeBuffer, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + L"%s", + AltitudeString); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("RtlStringCbPrintfW in CreateCallbackContext failed. Status 0x%x", Status); + goto Exit; + } + + RtlInitUnicodeString (&CallbackCtx->Altitude, CallbackCtx->AltitudeBuffer); + + Success = TRUE; + + Exit: + + if (Success == FALSE) { + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + CallbackCtx = NULL; + } + } + + return CallbackCtx; + +} + + +BOOLEAN +InsertCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ) +/*++ + +Routine Description: + + Utility method to insert the callback context into a list. + +Arguments: + + CallbackCtx - the callback context to insert + +Return Value: + + TRUE if successful, FALSE otherwise + +--*/ +{ + + BOOLEAN Success = FALSE; + + ExAcquireFastMutex(&g_CallbackCtxListLock); + + if (g_NumCallbackCtxListEntries < MAX_CALLBACK_CTX_ENTRIES) { + g_NumCallbackCtxListEntries++; + InsertHeadList(&g_CallbackCtxListHead, &CallbackCtx->CallbackCtxList); + Success = TRUE; + } else { + ErrorPrint("Insert Callback Ctx failed: Max CallbackCtx entries reached."); + } + + ExReleaseFastMutex(&g_CallbackCtxListLock); + + return Success; + +} + + +PCALLBACK_CONTEXT +FindCallbackContext( + _In_ LARGE_INTEGER Cookie + ) +/*++ + +Routine Description: + + Utility method to find a callback context using the cookie value. + +Arguments: + + Cookie - the cookie value associated with the callback context. The + cookie is returned when CmRegisterCallbackEx is called. + +Return Value: + + Pointer to the found callback context + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + PLIST_ENTRY Entry; + + ExAcquireFastMutex(&g_CallbackCtxListLock); + + Entry = g_CallbackCtxListHead.Flink; + while (Entry != &g_CallbackCtxListHead) { + + CallbackCtx = CONTAINING_RECORD(Entry, + CALLBACK_CONTEXT, + CallbackCtxList); + if (CallbackCtx->Cookie.QuadPart == Cookie.QuadPart) { + break; + } + + Entry = Entry->Flink; + } + + ExReleaseFastMutex(&g_CallbackCtxListLock); + + if (CallbackCtx == NULL) { + ErrorPrint("FindCallbackContext failed: No context with specified cookied was found."); + } + + return CallbackCtx; + +} + +PCALLBACK_CONTEXT +FindAndRemoveCallbackContext( + _In_ LARGE_INTEGER Cookie + ) +/*++ + +Routine Description: + + Utility method to find a callback context using the cookie value and then + remove it. + +Arguments: + + Cookie - the cookie value associated with the callback context. The + cookie is returned when CmRegisterCallbackEx is called. + +Return Value: + + Pointer to the found callback context + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + PLIST_ENTRY Entry; + + ExAcquireFastMutex(&g_CallbackCtxListLock); + + Entry = g_CallbackCtxListHead.Flink; + while (Entry != &g_CallbackCtxListHead) { + + CallbackCtx = CONTAINING_RECORD(Entry, + CALLBACK_CONTEXT, + CallbackCtxList); + if (CallbackCtx->Cookie.QuadPart == Cookie.QuadPart) { + RemoveEntryList(&CallbackCtx->CallbackCtxList); + g_NumCallbackCtxListEntries--; + break; + } + } + + ExReleaseFastMutex(&g_CallbackCtxListLock); + + if (CallbackCtx == NULL) { + ErrorPrint("FindAndRemoveCallbackContext failed: No context with specified cookied was found."); + } + + return CallbackCtx; +} + + +VOID +DeleteCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ) +/*++ + +Routine Description: + + Utility method to delete a callback context. + +Arguments: + + CallbackCtx - the callback context to insert + +Return Value: + + None + +--*/ +{ + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + +} diff --git a/general/registry/regfltr/sys/version.c b/general/registry/regfltr/sys/version.c new file mode 100644 index 00000000..11edc9a3 --- /dev/null +++ b/general/registry/regfltr/sys/version.c @@ -0,0 +1,611 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Version.c + +Abstract: + + Information and samples that describe: + 1. Changes in registry callback version 1.1 + 2. How to use the version 1 REG_OPEN_KEY_INFORMATION and + REG_CREATE_KEY_INFORMATION structures + 3. Work arounds for issues in callback version 1.0. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +/*++ + + Callback Version 1.1 is available in Windows 7 and Windows Server 2008 R2. + It is NOT available on Vista or Windows Server 2008 as of Service Pack 2. + + + Issues in callback version 1.0 that have been fixed in version 1.1: + + 1. In the post-notification phase for a create or open key operation, + the PostInfo->Object field might not be NULL even if the operation was + unsuccessful as indicated by PostInfo->ReturnStatus. + + This problem happens when there are multiple registry filter drivers + registered and one of the drivers blocks the operation in the + pre-notification phase by returning a nonsuccess status. Filter drivers + that are at higher altitudes will receive a post-notification where + PostInfo->ReturnStatus is the nonsuccess status value but + PostInfo->Object will not be NULL. PostInfo->Object in this case will + be equal to PostInfo->PreInfo->RootObject. + + 2. In version 1.0, an uncatched exception in a registry callback + routine will be swallowed by the system. In version 1.1 this has been + changed so an uncatched exception will cause the machine to bugcheck. + We provide a sample in this file (BugCheckSample) but obviously it is not + run. + + NOTE: While bugchecking the system is not a good thing to do, we do not + recommend putting your entire callback routine in one big try-except block + and swallow legitimate exceptions like possible pool corruptions. Please + keep what you wrap with a try-except block to the bare minimum. + +--*/ + +/*++ + + Version 1 of the create and open key REG_Xxx_INFORMATION structure is + available in Windows 7 and Windows Server 2008 R2. It is NOT available on + Vista or Windows Server 2008 as of Service Pack 2. + + NOTE: While Version 1 of the create and open key data structures will + likely be available on systems that have callback version 1.1, this + relationship is not guaranteed. You must check the create and open key + data structure to see its version rather than depending on the + callback version. See CreateOpenV1Sample on how to check the version. + + + Issues addressed by version 1 of the create and open key data structures: + + 1. Without the Attributes field provided in the V1 create and open + REG_Xxx_INFORMATION structure, there is no way to exactly replicate + certain create and open operations. See CreateOpenV1Sample for a + demonstration. Unfortunately there is no work around for this issue. + + 2. The PreInfo->CompleteName and PreInfo->RootObject fields in a + create or open key operation do not behave as expected when the key to be + opened or created is represented as an absolute path. + REG_CREATE_KEY_INFORMATION_V1 and REG_OPEN_KEY_INFORMATION_V1 contain a + field PreInfoV1->RemainingName which addresses this issue. + + Example: + + Open operation on this key: \REGISTRY\MACHINE\Software\_RegFltrRoot + + One way of relatively opening the key is to open "Software\_RegFltrRoot" + relative to \REGISTRY\MACHINE. + + RegOpenKeyEx(HKEY_LOCAL_MACHINE, + "Software\\_RegFltrRoot", + 0, + KEY_ALL_ACCESS, + &Key); + + In this case the value of the fields in REG_OPEN_KEY_INFORMATION_V1 + would be: + + RootObject - Handle to the key \REGISTRY\MACHINE + CompleteName - "Software\\_RegFltrRoot" + RemainingName - "Software\\_RegFltrRoot" + + + If the open uses an absolute path, + + RtlInitUnicodeString(&Name, L"\\REGISTRY\\MACHINE\\Software\\_RegFltrRoot") + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + ZwOpenKey(&Key, KEY_ALL_ACCESS, &KeyAttributes); + + the value of the fields in REG_OPEN_KEY_INFORMATION_V1 would be: + + RootObject - Handle to the key \REGISTRY + CompleteName - "\\REGISTRY\\MACHINE\\Software\\_RegFltrRoot" + RemainingName - "MACHINE\\Software\\_RegFltrRoot" + + Note that RootObject is not NULL even though CompleteName holds the + absolute path to the key. + + + The work around for this on systems without REG_OPEN_KEY_INFORMATION_V1 is + to check if the first character of CompleteName is a '\'. If that is the + case you can be sure that CompleteName is holding an absolute path to the + key. + +--*/ + + + +VOID +BugCheckSample( + ) +/*++ + +Routine Description: + + In version 1.1, if a registry filter driver's callback routine throws + an exception the registry will bugcheck the machine: + + REGISTRY_FILTER_DRIVER_EXCEPTION (0x135) + This bugcheck is caused by an unhandled exception in a registry + filtering driver. + + PARAMETERS + 1 - ExceptionCode + 2 - Address of the context record for the exception that caused + the bugcheck + 3 - The driver's callback routine address + 4 - Internal + + DESCRIPTION + This bugcheck indicates that a registry filtering driver didn't handle + exception inside its notification routine. One can identify the driver + by the 3rd parameter. + + In version 1.0, an exception in the callback routine is simply swallowed and + ignored. + + This sample uses a simple callback routine that will access NULL to throw + an exception. The sample is not normally run and is only here for + demonstration purposes. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + + InfoPrint(""); + InfoPrint("=== Bugcheck Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_VERSION_BUGCHECK, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + // + // Do an open key just to invoke the callback. + // In version 1.1 this will bugcheck and nothing else will run. + // In version 1.0 the open will simply fail as expected. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x", Status); + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + return; + +} + + +NTSTATUS +CallbackBugcheck( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine throws an exception by dereferencing a + null pointer. + + NOTE: While bugchecking the system is not a good thing to do, we do not + recommend putting your entire callback routine in one big try-except block + and swallow legitimate exceptions like possible pool corruptions. Please + keep what you wrap with a try-except block to the bare minimum. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS; + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PULONG NullPointer = NULL; + + UNREFERENCED_PARAMETER(CallbackCtx); + UNREFERENCED_PARAMETER(NotifyClass); + UNREFERENCED_PARAMETER(Argument2); + + + InfoPrint("\tCallback is about to throw an exception."); + if (g_MajorVersion == 1 && g_MinorVersion == 0) { + InfoPrint("\tException will be swallowed by registry"); + } else { + ErrorPrint("Exception will cause machine to bugcheck"); + } + + + #pragma prefast(suppress: 6011, "Sample is purposefully dereferencing a null pointer."); + *NullPointer = 0; + + return Status; +} + + +BOOLEAN +CreateOpenV1Sample( + ) +/*++ + +Routine Description: + + This sample shows how the information in the Attributes field of + the REG_OPEN_KEY_INFORMATION_V1 data structure can change the outcome of + a registry operation. Without the Attributes field, it is impossible for + the callback routine to accuratel replay certain registry operations. + + A special key is used in this sample which has security set on it to + protect it from being deleted: + + \REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Enum + + In the sample we try to open this key with DELETE access. Normally this + will work if we do it in kernel mode since the system bypasses all + access checks on handles created in kernel mode. However here we set the + OBJ_FORCE_ACCESS_CHECK flag which tells the system to perform all access + checks on the handle. + + In the callback routine associated with this sample, we will replay the + open operation with and without the flag to show how the presence of + the attributes information can change the outcome of the operation. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + WCHAR NameBuffer[] = L"\\registry\\machine\\system\\currentcontrolset\\enum"; + HANDLE Key = NULL; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Create/Open V1 Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_VERSION_CREATE_OPEN_V1, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Try to open the special key with delete access but have the + // OBJ_FORCE_ACCESS_CHECK flag in object attributes. This operation + // should fail with access denied. + // + + RtlInitUnicodeString(&Name, NameBuffer); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | + OBJ_FORCE_ACCESS_CHECK, + NULL, + NULL); + + Status = ZwOpenKey(&Key, + DELETE, + &KeyAttributes); + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Create/Open V1 sample succeeded."); + } else { + ErrorPrint("Create/Open V1 sample failed."); + } + + return Success; + +} + + +NTSTATUS +CallbackCreateOpenV1( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine will show how to check whether the system + supports version 1 of the REG_OPEN_KEY_INFORMATION structure. + + If version 1 is supported, the callback routine will replay the open + operation during the pre-notification phase with and without the + Attributes field found in REG_OPEN_KEY_INFORMATION_V1 to show how the + outcome is different. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS; + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PREG_OPEN_KEY_INFORMATION_V1 PreOpenInfo; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + + + UNREFERENCED_PARAMETER(CallbackCtx); + + // + // Check for the pre-notification phase of a create operation + // + + if (NotifyClass != RegNtPreOpenKeyEx) { + goto Exit; + } + + PreOpenInfo = (PREG_OPEN_KEY_INFORMATION_V1) Argument2; + + // + // Check if version 1 is available on this system. If not, + // simply return success. + // + + InfoPrint("\tREG_OPEN_KEY_INFORMATION structure's version is 0x%p", + (PVOID)PreOpenInfo->Version); + + if ((ULONG_PTR) PreOpenInfo->Version != 1) { + InfoPrint("Create/Open v1 sample is only for version 1"); + goto Exit; + } + + // + // Open a handle to the root object + // + + Status = ObOpenObjectByPointer(PreOpenInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreOpenInfo->ObjectType, + KernelMode, + &RootKey); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + goto Exit; + } + + // + // Do the open with the same attributes as in the original call. This + // includes the OBJ_FORCE_ACCESS_CHECK flag which will cause the open + // operation to fail. + // + // Note: The openkey operation might have originated from user mode so the + // OBJ_KERNEL_HANDLE flag needs to be explicitly added to the attributes + // to prevent user mode handle spoofing attacks. + // + + + InitializeObjectAttributes(&KeyAttributes, + PreOpenInfo->RemainingName, + PreOpenInfo->Attributes | OBJ_KERNEL_HANDLE, + RootKey, + PreOpenInfo->SecurityDescriptor); + + Status = ZwOpenKey(&Key, + PreOpenInfo->DesiredAccess, + &KeyAttributes); + + if (NT_SUCCESS(Status)) { + ZwClose(Key); + Key = NULL; + } + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwOpenKey with attributes returned unexpected status 0x%x", Status); + Status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + // + // Do the open without the attributes in the original call. The open + // operation will succeed now because the access checks will not be + // performed. + // + + InitializeObjectAttributes(&KeyAttributes, + PreOpenInfo->RemainingName, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + RootKey, + PreOpenInfo->SecurityDescriptor); + + Status = ZwOpenKey(&Key, + PreOpenInfo->DesiredAccess, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey without attributes returned unexpected status 0x%x", Status); + goto Exit; + } + + Status = STATUS_SUCCESS; + + + Exit: + + if (Key != NULL) { + ZwClose(Key); + } + + if (RootKey != NULL) { + ZwClose(RootKey); + } + + return Status; +} + |
