summaryrefslogtreecommitdiff
path: root/general/registry/regfltr/sys
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /general/registry/regfltr/sys
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'general/registry/regfltr/sys')
-rw-r--r--general/registry/regfltr/sys/capture.c620
-rw-r--r--general/registry/regfltr/sys/context.c582
-rw-r--r--general/registry/regfltr/sys/driver.c468
-rw-r--r--general/registry/regfltr/sys/multialt.c776
-rw-r--r--general/registry/regfltr/sys/post.c768
-rw-r--r--general/registry/regfltr/sys/pre.c799
-rw-r--r--general/registry/regfltr/sys/regfltr.c871
-rw-r--r--general/registry/regfltr/sys/regfltr.h488
-rw-r--r--general/registry/regfltr/sys/regfltr.rc11
-rw-r--r--general/registry/regfltr/sys/regfltr.vcxproj214
-rw-r--r--general/registry/regfltr/sys/regfltr.vcxproj.Filters61
-rw-r--r--general/registry/regfltr/sys/txr.c784
-rw-r--r--general/registry/regfltr/sys/txrutil.c283
-rw-r--r--general/registry/regfltr/sys/util.c304
-rw-r--r--general/registry/regfltr/sys/version.c611
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;
+}
+