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 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; }
VOID 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 ERROR_ACCESS_DENIED. The same is done for two values. See ..\sys\Post.c for the callback routine used in this sample. --*/ { LONG Res; HRESULT hr; BOOL Result; HKEY Key = NULL; HKEY NotModifiedKey = NULL; BOOL Success = FALSE; DWORD BytesReturned; DWORD ValueData = 0xDEADBEEF; REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; InfoPrint(""); InfoPrint("=== Post-Notification Override Success Sample ===="); // // Register a callback with the specified callback mode and altitude. // RtlZeroMemory(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); hr = StringCbPrintf(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), CALLBACK_ALTITUDE); if (!SUCCEEDED(hr)) { ErrorPrint("Copying altitude string failed. Error %d", hr); goto Exit; } RegisterCallbackInput.CallbackMode = CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS; Result = DeviceIoControl(g_Driver, IOCTL_REGISTER_CALLBACK, &RegisterCallbackInput, sizeof(REGISTER_CALLBACK_INPUT), &RegisterCallbackOutput, sizeof(REGISTER_CALLBACK_OUTPUT), &BytesReturned, NULL); if (Result != TRUE) { ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); goto Exit; } Success = TRUE; // // Create two keys. // Creating the "not modified" key will succeed. // Creating the other key will fail with file not found. // // // NOTE: In the kernel debugger output, you will see 3 sets of // notifications for create key even though we only call create key twice. // You will also see the message that create key is overrided twice. // // Kd output: // // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPreCreateKeyEx. // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPostCreateKeyEx. // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPreCreateKeyEx. // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPostCreateKeyEx. // RegFltr: Callback: Create key _RegFltrKey overrided from success to error. // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPreCreateKeyEx. // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPostCreateKeyEx. // RegFltr: Callback: Create key _RegFltrKey overrided from success to error. // // The reason this happens is that RegCreateKeyEx is more than just a // wrapper around NtCreateKey. If the call to NtCreateKey fails, // RegCreateKeyEx will retry the call in slightly different ways // depending on the error returned. // Res = RegCreateKeyEx(g_RootKey, NOT_MODIFIED_KEY_NAME, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &NotModifiedKey, NULL); if (Res != ERROR_SUCCESS) { ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); Success = FALSE; } Res = RegCreateKeyEx(g_RootKey, KEY_NAME, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &Key, NULL); if (Res != ERROR_ACCESS_DENIED) { ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); Success = FALSE; } // // Set two values. // Setting the "not modified" value will succeed. // Setting the other value will fail with file not found. // Res = RegSetValueEx(g_RootKey, NOT_MODIFIED_VALUE_NAME, 0, REG_DWORD, (BYTE *) &ValueData, sizeof(ValueData)); if(Res != ERROR_SUCCESS) { ErrorPrint("RegSetValueEx return unexpected status %d", Res); Success = FALSE; } Res = RegSetValueEx(g_RootKey, VALUE_NAME, 0, REG_DWORD, (BYTE *) &ValueData, sizeof(ValueData)); if(Res != ERROR_ACCESS_DENIED) { ErrorPrint("RegSetValueEx return unexpected status %d", Res); Success = FALSE; } // // Unregister the callback // UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; Result = DeviceIoControl(g_Driver, IOCTL_UNREGISTER_CALLBACK, &UnRegisterCallbackInput, sizeof(UNREGISTER_CALLBACK_INPUT), NULL, 0, &BytesReturned, NULL); if (Result != TRUE) { ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); 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. // Res = RegOpenKeyEx(g_RootKey, KEY_NAME, 0, KEY_ALL_ACCESS, &Key); if (Res != ERROR_FILE_NOT_FOUND) { ErrorPrint("RegOpenKeyEx returned unexpected error: %d", Res); if (Key != NULL) { RegCloseKey(Key); Key = NULL; } Success = FALSE; } Res = RegDeleteValue(g_RootKey, VALUE_NAME); if (Res != ERROR_FILE_NOT_FOUND) { ErrorPrint("RegDeleteValue on value returned unexpected status: %d", Res); Success = FALSE; } Exit: if (Key != NULL) { RegCloseKey(Key); } RegDeleteKey(g_RootKey, KEY_NAME); if (NotModifiedKey != NULL) { RegCloseKey(NotModifiedKey); } RegDeleteKey(g_RootKey, NOT_MODIFIED_KEY_NAME); RegDeleteValue(g_RootKey, VALUE_NAME); RegDeleteValue(g_RootKey, NOT_MODIFIED_VALUE_NAME); if (Success) { InfoPrint("Post-Notification Override Success Sample succeeded."); } else { ErrorPrint("Post-Notification Override Success Sample FAILED."); } }
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; }
VOID share_display_share(TCHAR * netname) { USHORT err; /* API return status */ TCHAR FAR * pBuffer; USHORT2ULONG num_read; /* num entries read by API */ USHORT maxLen; /* max msg length */ USHORT len; /* message length formater */ struct share_info_2 FAR * share_entry; struct connection_info_1 FAR * conn_entry; USHORT2ULONG i; USHORT more_data = FALSE; TCHAR txt_UNKNOWN[APE2_GEN_MAX_MSG_LEN]; LUI_GetMsg(txt_UNKNOWN, APE2_GEN_MAX_MSG_LEN, APE2_GEN_UNKNOWN); // // On NT, the redir doesn't have to be running to use the server // start_autostart(txt_SERVICE_FILE_SRV); if (err = MNetShareGetInfo(NULL, netname, 2, (LPBYTE*)&share_entry)) ErrorExit(err); GetMessageList(NUM_SHARE_MSGS, ShareMsgList, &maxLen); len = maxLen + (USHORT) 5; share_entry->shi2_type &= ~STYPE_SPECIAL; if (share_entry->shi2_type == STYPE_PRINTQ) get_print_devices(share_entry->shi2_netname); else _tcscpy(Buffer, share_entry->shi2_path); WriteToCon(fmtPSZ, 0, len, PaddedString(len,ShareMsgList[SHARE_MSG_NAME].msg_text,NULL), share_entry->shi2_netname); WriteToCon(fmtNPSZ, 0, len, PaddedString(len,ShareMsgList[SHARE_MSG_PATH].msg_text,NULL), Buffer); WriteToCon(fmtPSZ, 0, len, PaddedString(len,ShareMsgList[SHARE_MSG_REMARK].msg_text,NULL), share_entry->shi2_remark); if (share_entry->shi2_max_uses == SHI_USES_UNLIMITED) WriteToCon(fmtNPSZ, 0, len, PaddedString(len,ShareMsgList[SHARE_MSG_MAX_USERS].msg_text,NULL), ShareMsgList[SHARE_MSG_ULIMIT].msg_text); else WriteToCon(fmtULONG, 0, len, PaddedString(len,ShareMsgList[SHARE_MSG_MAX_USERS].msg_text,NULL), share_entry->shi2_max_uses); NetApiBufferFree((TCHAR FAR *) share_entry); if( (err = MNetConnectionEnum( NULL, netname, 1, (LPBYTE*)&pBuffer, &num_read)) == ERROR_MORE_DATA) more_data = TRUE; else if (err) ErrorExit( err ); WriteToCon(TEXT("%-*.*ws"),0,len, PaddedString(len,ShareMsgList[SHARE_MSG_USERS].msg_text,NULL)); for (i = 0, conn_entry = (struct connection_info_1 FAR *) pBuffer; i < num_read; i++, conn_entry++) { if ((i != 0) && ((i % 3) == 0)) WriteToCon(TEXT("%-*.*ws"),len,len, NULL_STRING); WriteToCon(TEXT("%Fws"), PaddedString(21,(conn_entry->coni1_username == NULL) ? (TCHAR FAR *)txt_UNKNOWN : conn_entry->coni1_username, NULL)); if (((i + 1) % 3) == 0) PrintNL(); } if ((i == 0) || ((i % 3) != 0)) PrintNL(); if (num_read) { NetApiBufferFree(pBuffer); } if( more_data ) InfoPrint(APE_MoreData); else InfoSuccess(); }
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 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 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; }
VOID NEAR device_header_display(VOID) { InfoPrint(APE2_DEV_MSG_HDR); }
VOID CaptureSample( ) /*++ Routine Description: This sample shows how to capture input parameters when the registery operation comes from user mode. The main part of this sample and a detailed explanation of why and how to capture user mode parameters can be found in ..\sys\capture.c. The user mode part of this sample simply calls RegSetValueEx and DeleteValue since the REG_XXX_INFORMATION structure for these two operations are only partially captured. See ..\sys\Capture.c for the callback routine used in this sample. Return Value: None --*/ { LONG Res; HRESULT hr; DWORD ValueData = 0xDEADBEEF; BOOL Result; BOOL Success = FALSE; DWORD BytesReturned; REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; InfoPrint(""); InfoPrint("=== Capture Sample ===="); // // Register callback // RtlZeroMemory(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); hr = StringCbPrintf(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), CALLBACK_ALTITUDE); if (!SUCCEEDED(hr)) { ErrorPrint("Copying altitude string failed. Error %d", hr); goto Exit; } RegisterCallbackInput.CallbackMode = CALLBACK_MODE_CAPTURE; Result = DeviceIoControl(g_Driver, IOCTL_REGISTER_CALLBACK, &RegisterCallbackInput, sizeof(REGISTER_CALLBACK_INPUT), &RegisterCallbackOutput, sizeof(REGISTER_CALLBACK_OUTPUT), &BytesReturned, NULL); if (Result != TRUE) { ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); goto Exit; } Success = TRUE; // // Create a value and delete it. Both should be successful. // Res = RegSetValueEx(g_RootKey, VALUE_NAME, 0, REG_DWORD, (BYTE *) &ValueData, sizeof(ValueData)); if(Res != ERROR_SUCCESS) { ErrorPrint("RegSetValueEx return unexpected status %d", Res); Success = FALSE; } Res = RegDeleteValue(g_RootKey, VALUE_NAME); if (Res != ERROR_SUCCESS) { ErrorPrint("RegDeleteValue on original value returned unexpected status: %d", Res); Success = FALSE; } // // Unregister the callback // UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; Result = DeviceIoControl(g_Driver, IOCTL_UNREGISTER_CALLBACK, &UnRegisterCallbackInput, sizeof(UNREGISTER_CALLBACK_INPUT), NULL, 0, &BytesReturned, NULL); if (Result != TRUE) { ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); Success = FALSE; } Exit: if (Success) { InfoPrint("Capture Sample succeeded."); } else { ErrorPrint("Capture Sample failed."); } }
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; }
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; }
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; }
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 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 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); // TODO(H): 看下这个函数 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. // // TODO(H): 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 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; }
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; }
VOID version_display(VOID) { CHAR FAR * pBuffer; USHORT ProductMsg; USHORT RetVal; /* Determine product identity. */ #ifdef DOS3 /* If compiled for DOS, product can only be DOS Enhanced Workstation. */ { ProductMsg = APE2_VER_ProductDOSWorkstation; } #endif #ifdef OS2 /* Are we an OS/2 Server or OS/2 Workstation? We attempt to answer */ /* this question by looking for the server initialization */ /* executable (currently netsvini.exe). If we find it, we assume */ /* we are an OS/2 server, otherwise we are an OS/2 workstation. We */ /* determine the existence of the file by first constructing a path */ /* to it, and then doing a query for it's attributes - if the call */ /* succeeds, the file exists. This is not an infallible approach, */ /* but is probably the simplest and best approach out of a several */ /* imperfect alternatives currently available. */ #if defined(NTENV) // BUGBUG - for now, assume any NT system is a server. For later, this // information should be in the configuration database ProductMsg = APE2_VER_ProductOS2Server; #else /* !NTENV */ { CHAR Path[PATHLEN]; /* Get path to netsvini.exe (the server initialization file). */ if (RetVal = NetIMakeLMFileName("SERVICES\\" FNAME_SRV_FULL_INIT, Path, sizeof(Path))) { ErrorExit(RetVal); } /* Determine whether netsvini.exe exists. */ if (_access(Path, 0)) ProductMsg = APE2_VER_ProductOS2Workstation; else ProductMsg = APE2_VER_ProductOS2Server; } #endif /* !NTENV */ #endif /* Get version info from lanman.ini. */ RetVal = MNetConfigGet(NULL, NULL, txt_COMP_VERSION, txt_PARM_V_LAN_MANAGER, &pBuffer); /* If version info not available, "Unknown" is printed. */ if (RetVal) { if (RetVal == NERR_NetNotStarted) ErrorExit(RetVal); LUI_GetMsg(pBuffer, BIG_BUFFER_SIZE, APE2_GEN_UNKNOWN); } /* Display the LAN Manager version information. */ InfoPrintInsTxt(APE2_VER_Release, pBuffer); /* Display product identity. */ InfoPrint(ProductMsg); #ifndef NTENV /* Determine and display build time. The net_ctime() function */ /* assumes a local time, whereas the value in bldtime is */ /* produced by time(), and is GMT-based. If it is desired to */ /* make this timestamp more accurate, makever.c should be */ /* modified to use time_now() instead of time(). */ net_ctime((LONG FAR *) &bldtime, pBuffer, NET_CTIME_FMT2_LEN, 2); InfoPrintInsTxt(APE2_VER_BuildTime, pBuffer); #endif NetApiBufferFree(pBuffer); return; }
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; }
/*** * share_display_all() * Display info about one share or all shares * * Args: * netname - the share to display of NULL for all * * Returns: * nothing - success * exit(2) - command failed */ VOID share_display_all(VOID) { USHORT err; /* API return status */ TCHAR FAR * pBuffer; USHORT2ULONG num_read; /* num entries read by API */ USHORT maxLen; /* max msg length */ USHORT2ULONG i; struct share_info_2 FAR * share_entry; // // On NT, the redir doesn't have to be running to use the server // #if !defined(NTENV) start_autostart(txt_SERVICE_REDIR); #endif start_autostart(txt_SERVICE_FILE_SRV); if (err = MNetShareEnum( NULL, 2, (LPBYTE*)&pBuffer, &num_read)) ErrorExit(err); if (num_read == 0) EmptyExit(); NetISort(pBuffer, num_read, sizeof(struct share_info_2), CmpShrInfo2); GetMessageList(NUM_SHARE_MSGS, ShareMsgList, &maxLen); PrintNL(); InfoPrint(APE2_SHARE_MSG_HDR); PrintLine(); for (i = 0, share_entry = (struct share_info_2 FAR *) pBuffer; i < num_read; i++, share_entry++) { if (SizeOfHalfWidthString(share_entry->shi2_netname) <= 12) WriteToCon(TEXT("%Fws "),PaddedString(12,share_entry->shi2_netname,NULL)); else { WriteToCon(TEXT("%Fws"), share_entry->shi2_netname); PrintNL(); WriteToCon(TEXT("%-12.12Fws "), TEXT("")); } share_entry->shi2_type &= ~STYPE_SPECIAL; if (share_entry->shi2_type == STYPE_PRINTQ) { get_print_devices(share_entry->shi2_netname); WriteToCon(TEXT("%ws "),PaddedString(-22, Buffer, NULL)); WriteToCon(TEXT("%ws "),PaddedString( 8, ShareMsgList[SHARE_MSG_SPOOLED].msg_text, NULL)); } else { WriteToCon(TEXT("%Fws "),PaddedString(-31,share_entry->shi2_path,NULL)); } WriteToCon(TEXT("%Fws"),PaddedString(-34,share_entry->shi2_remark,NULL)); PrintNL(); } InfoSuccess(); NetApiBufferFree(pBuffer); }
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; }
/*** * share_del() * Delete a share * * Args: * name - share to delete * * Returns: * nothing - success * exit(1) - command completed with errors * exit(2) - command failed */ VOID share_del(TCHAR * name) { USHORT err; /* API return status */ USHORT2ULONG err2; /* API return status */ TCHAR FAR * pEnumBuffer; USHORT last_err; USHORT2ULONG err_cnt = 0; USHORT2ULONG num_read; /* num entries read by API */ USHORT2ULONG i; ULONG LongType; USHORT2ULONG type; int found; TCHAR share[NNLEN+1]; struct share_info_2 FAR * share_entry; #ifndef NTENV PRQINFO FAR * print_entry; USHORT2ULONG available; /* num entries available */ TCHAR FAR * pGetInfoBuffer; #endif /* * MAINTENANCE NOTE: While doing maintenance for bug fix 1800, it was * noticed that this function uses BigBuf, and so does the function * delete_share() which is called by this function. In the current * implementation this is not a problem, because of the api calling * pattern. However, the slightest change could break this function, or * delete_share(), so beware! Bug fix 1800 was directly ported from * the MSKK code. The api calls in this function and in share_del() are * incredibly redundant, but I left it as is rather than risk breaking * it. - RobDu */ err = delete_share(name); /* check for Open files, and delete share */ switch (err) { case NERR_Success: return; case NERR_NetNameNotFound: /* * the name was not found, so we try deleting the sticky entry * in registry. */ err = MNetShareDelSticky(NULL, name, 0) ; if (err == NERR_Success) { InfoPrintInsTxt(APE_DelStickySuccess, name); return ; } else if (err == NERR_NetNameNotFound) break; else ErrorExit(err); default: ErrorExit(err); } /*** * Only get here if "share" that user asked us to delete was * NOT a share name. Could be a disk path, or a com or lpt * device */ if (err2 = I_MNetPathType(NULL, name, &LongType, 0L)) ErrorExit((USHORT) err2); if (LongType == ITYPE_PATH_ABSD) type = STYPE_DISKTREE; else { if ((LongType & ITYPE_DEVICE) == 0) ErrorExit( NERR_NetNameNotFound); else { if (err = MNetShareCheck(NULL, name, &type)) ErrorExit(err); } } found = FALSE; switch (type) { case STYPE_DISKTREE: if (err = MNetShareEnum(NULL, 2, (LPBYTE*)&pEnumBuffer, &num_read)) ErrorExit(err); for (i = 0, share_entry = (struct share_info_2 FAR *) pEnumBuffer; i < num_read; i++, share_entry++) { if (! stricmpf(share_entry->shi2_path, name)) { found = TRUE; _tcscpy(share, share_entry->shi2_netname); ShrinkBuffer(); if (err = delete_share(share)) { last_err = err; err_cnt++; InfoPrintInsTxt(APE_ShareErrDeleting, share); } } } NetApiBufferFree(pEnumBuffer); break; // // NT does not support Net Share of a printer/comm port via the net cmd // #ifndef NTENV case STYPE_PRINTQ: if (err = ApiEnumerator(DosPrintQEnum, NULL, 1, &num_read, &available)) ErrorExit(err); for (i = 0, print_entry = (struct PRINTQ FAR *) BigBuf; i < num_read; i++, print_entry++) { if (IsMember(name, print_entry->pszDestinations)) { _tcscpy(share, print_entry->szName); if (MNetShareGetInfo(NULL, share, 0, &pGetInfoBuffer)) continue; NetApiBufferFree(pGetInfoBuffer); ShrinkBuffer(); found = TRUE; if (err = delete_share(share)) { last_err = err; err_cnt++; InfoPrintInsTxt(APE_ShareErrDeleting, share); } } } NetApiBufferFree(pEnumBuffer); break; case STYPE_DEVICE: if (err = MNetCharDevQEnum(NULL, NULL, 1, &pEnumBuffer, &num_read)) ErrorExit(err); for (i = 0, char_entry = (struct chardevQ_info_1 FAR *) pEnumBuffer; i < num_read; i++, char_entry++) { if (IsMember(name, char_entry->cq1_devs)) { found = TRUE; _tcscpy(share, char_entry->cq1_dev); ShrinkBuffer(); if (err = delete_share(share)) { last_err = err; err_cnt++; InfoPrintInsTxt(APE_ShareErrDeleting, share); } } } NetApiBufferFree(pEnumBuffer); break; #endif /* not NTENV */ default: ErrorExit(ERROR_INVALID_PARAMETER) ; } /* switch */ /*** * Bye, bye */ if ((err_cnt) && (err_cnt == num_read)) ErrorExit(last_err); else if (err_cnt) { InfoPrint(APE_CmdComplWErrors); NetcmdExit(1); } else if (! found) ErrorExit(APE_ShareNotFound); InfoPrintInsTxt(APE_DelSuccess, name); }
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; }
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; }
VOID 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. See ..\sys\Post.c for the callback routine used in this sample. Return Value: None --*/ { LONG Res; HRESULT hr; BOOL Success = FALSE; BOOL Result; HKEY Key = NULL; HKEY ModifiedKey = NULL; DWORD BytesReturned; REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; InfoPrint(""); InfoPrint("=== Post-Notification Override Error Sample ===="); // // Create a key with name MODIFIED_KEY_NAME // Res = RegCreateKeyEx(g_RootKey, MODIFIED_KEY_NAME, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &ModifiedKey, NULL); if (Res != ERROR_SUCCESS) { ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); goto Exit; } // // Now try to open a key by KEY_NAME which does not exist. Verify that // this fails. // Res = RegOpenKeyEx(g_RootKey, KEY_NAME, 0, KEY_ALL_ACCESS, &Key); if (Res != ERROR_FILE_NOT_FOUND) { ErrorPrint("RegOpenKeyEx returned unexpected error %d", Res); goto Exit; } // // Register a callback with the specified callback mode and altitude. // RtlZeroMemory(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); hr = StringCbPrintf(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), CALLBACK_ALTITUDE); if (!SUCCEEDED(hr)) { ErrorPrint("Copying altitude string failed. Error %d", hr); goto Exit; } RegisterCallbackInput.CallbackMode = CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR; Result = DeviceIoControl(g_Driver, IOCTL_REGISTER_CALLBACK, &RegisterCallbackInput, sizeof(REGISTER_CALLBACK_INPUT), &RegisterCallbackOutput, sizeof(REGISTER_CALLBACK_OUTPUT), &BytesReturned, NULL); if (Result != TRUE) { ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); goto Exit; } Success = TRUE; // // Open key again. The callback will intercept this and make it succeed. // Res = RegOpenKeyEx(g_RootKey, KEY_NAME, 0, KEY_ALL_ACCESS, &Key); if (Res != ERROR_SUCCESS) { ErrorPrint("RegOpenKeyEx returned unexpected error %d", Res); Success = FALSE; } // // Unregister the callback // UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; Result = DeviceIoControl(g_Driver, IOCTL_UNREGISTER_CALLBACK, &UnRegisterCallbackInput, sizeof(UNREGISTER_CALLBACK_INPUT), NULL, 0, &BytesReturned, NULL); if (Result != TRUE) { ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); Success = FALSE; } Exit: if (Key != NULL) { RegCloseKey(Key); } if (ModifiedKey != NULL) { RegCloseKey(ModifiedKey); } RegDeleteKey(g_RootKey, KEY_NAME); RegDeleteKey(g_RootKey, MODIFIED_KEY_NAME); if (Success) { InfoPrint("Post-Notification Override Error Sample succeeded."); } else { ErrorPrint("Post-Notification Override Error Sample FAILED."); } }