Sample Code

Windows Driver Samples/ RegFltr Sample Driver/ C++/ sys/ post.c/

/*++
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 

--*/



BOOLEAN
PostNotificationOverrideSuccessSample(
    )
/*++

Routine Description:

    This sample shows how registry callbacks can fail a registry operation 
    in the post-notification phase. 

    Two keys are created. The creates normally should succeeded, but one 
    is intercepted by the callback and failed with STATUS_ACCESS_DENIED.
    The same is done for two values.
    
    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;
    HANDLE Key = NULL;
    HANDLE NotModifiedKey = NULL;
    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;
    
    //
    // Create two keys. 
    // Creating the "not modified" key will succeed.
    // Creating the other key will fail with file not found.
    //
    
    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 (!NT_SUCCESS(Status)) {
        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 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 create key and set value calls were failed by 
    // checking that the key and value with KEY_NAME and VALUE_NAME do not
    // 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 on key failed. Status: 0x%x", Status);
        Success = FALSE;
    }
        

    //
    // 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
    //

    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("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;
    
}

Our Services

  • What our customers say about us?

© 2011-2019 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS