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;
}
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;
}
Example #3
0
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;
}