天天看点

发一注册表监控驱动代码

该代码利用微软提供的注册表回调函数CmRegisterCallback,无需任何SSDT hook

不仅能够进行注册表监控,还可以拦截。

驱动完整代码如下:

#define NTDDI_WINXPSP2                      0x05010200

#define OSVERSION_MASK      0xFFFF0000

#define SPVERSION_MASK      0x0000FF00

#define SUBVERSION_MASK     0x000000FF

//

// macros to extract various version fields from the NTDDI version

//

#define OSVER(Version)  ((Version) & OSVERSION_MASK)

#define SPVER(Version)  (((Version) & SPVERSION_MASK) >> 8)

#define SUBVER(Version) (((Version) & SUBVERSION_MASK) )

//#define NTDDI_VERSION   NTDDI_WINXPSP2

#include <ntifs.h>

#include <wdm.h>

#include <ntstrsafe.h>

#define FILE_DEVICE_UNKNOWN 0x00000022

#define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN

#define IOCTL_CAPTURE_GET_REGEVENTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER,FILE_READ_DATA | FILE_WRITE_DATA)

#define USERSPACE_CONNECTION_TIMEOUT 10

#define REGISTRY_POOL_TAG 'pRE'

typedef unsigned int UINT;

typedef char * PCHAR;

typedef PVOID POBJECT;

/* Registry event */

typedef struct  _REGISTRY_EVENT {

REG_NOTIFY_CLASS eventType;

TIME_FIELDS time;

HANDLE processId;

ULONG dataType;

ULONG dataLengthB;

ULONG registryPathLengthB;

/* Contains path and optionally data */

UCHAR registryData[];

} REGISTRY_EVENT, * PREGISTRY_EVENT;

/* Storage for registry event to be put into a linked list */

typedef struct  _REGISTRY_EVENT_PACKET {

    LIST_ENTRY     Link;

PREGISTRY_EVENT pRegistryEvent;

} REGISTRY_EVENT_PACKET, * PREGISTRY_EVENT_PACKET;

/* Context stuff */

typedef struct _CAPTURE_REGISTRY_MANAGER

{

    PDEVICE_OBJECT deviceObject;

BOOLEAN bReady;

LARGE_INTEGER  registryCallbackCookie;

LIST_ENTRY lQueuedRegistryEvents;

KTIMER connectionCheckerTimer;

KDPC connectionCheckerFunction;

KSPIN_LOCK lQueuedRegistryEventsSpinLock;

ULONG lastContactTime;

} CAPTURE_REGISTRY_MANAGER , *PCAPTURE_REGISTRY_MANAGER;

/* Methods */

NTSTATUS KDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

NTSTATUS KDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

VOID UnloadDriver(PDRIVER_OBJECT DriverObject);

NTSTATUS RegistryCallback(IN PVOID CallbackContext, IN PVOID  Argument1, IN PVOID  Argument2);

//BOOLEAN GetRegistryObjectCompleteName(PREGISTRY_EVENT pRegistryEvent, PUNICODE_STRING pPartialObjectName, PVOID pRegistryObject);

//VOID QueueRegistryEvent(PREGISTRY_EVENT pRegistryEvent);

VOID UpdateLastContactTime();

ULONG GetCurrentTime();

NTSTATUS HandleIoctlGetRegEvents(IN PDEVICE_OBJECT DeviceObject, PIRP Irp,

       PIO_STACK_LOCATION pIoStackIrp, UINT *pdwDataWritten);

VOID ConnectionChecker(

    IN struct _KDPC  *Dpc,

    IN PVOID  DeferredContext,

    IN PVOID  SystemArgument1,

    IN PVOID  SystemArgument2

    );

/* Global values */

PDEVICE_OBJECT gpDeviceObject;

/* Main entry point into the driver, is called when the driver is loaded */

NTSTATUS DriverEntry(

IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING RegistryPath

)

{

    NTSTATUS status;

    UNICODE_STRING uszDriverString;

    UNICODE_STRING uszDeviceString;

LARGE_INTEGER registryEventsTimeout;

    PDEVICE_OBJECT pDeviceObject;

    PCAPTURE_REGISTRY_MANAGER pRegistryManager;

// Point uszDriverString at the driver name

    RtlInitUnicodeString(&uszDriverString, L"[url=file://\\Device\\RegistryMonitor]\\Device\\RegistryMonitor[/url]");

    // Create and initialize device object

status = IoCreateDevice(

  DriverObject,

        sizeof(CAPTURE_REGISTRY_MANAGER),

        &uszDriverString,

        FILE_DEVICE_UNKNOWN,

        0,

        FALSE,

        &pDeviceObject

  );

    if(!NT_SUCCESS(status))

{

  DbgPrint("RegistryMonitor: ERROR IoCreateDevice ->  [url=file://\\Device\\RegistryMonitor]\\Device\\RegistryMonitor[/url] - %08x\n", status);

        return status;

}

/* Set global device object to newly created object */

gpDeviceObject = pDeviceObject;

/* Get the registr manager from the extension of the device */

pRegistryManager = gpDeviceObject->DeviceExtension;

pRegistryManager->bReady = FALSE;

/* Point uszDeviceString at the device name */

    RtlInitUnicodeString(&uszDeviceString, L"[url=file://\\DosDevices\\RegistryMonitor]\\DosDevices\\RegistryMonitor[/url]");

/* Create symbolic link to the user-visible name */

    status = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);

    if(!NT_SUCCESS(status))

{

        DbgPrint("RegistryMonitor: ERROR IoCreateSymbolicLink ->  [url=file://\\DosDevices\\RegistryMonitor]\\DosDevices\\RegistryMonitor[/url] - %08x\n", status);

        IoDeleteDevice(pDeviceObject);

        return status;

    }

KeInitializeSpinLock(&pRegistryManager->lQueuedRegistryEventsSpinLock);

InitializeListHead(&pRegistryManager->lQueuedRegistryEvents);

    // Load structure to point to IRP handlers

    DriverObject->DriverUnload                         = UnloadDriver;

    DriverObject->MajorFunction[IRP_MJ_CREATE]         = KDispatchCreateClose;

    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = KDispatchCreateClose;

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;

status = CmRegisterCallback(RegistryCallback, pRegistryManager, &pRegistryManager->registryCallbackCookie);

if(!NT_SUCCESS(status))

{

  DbgPrint("RegistryMonitor: ERROR CmRegisterCallback - %08x\n", status);

  return status;

}

UpdateLastContactTime();

/* Create a DPC routine so that it can be called periodically */

KeInitializeDpc(&pRegistryManager->connectionCheckerFunction,

  (PKDEFERRED_ROUTINE) ConnectionChecker, pRegistryManager);

KeInitializeTimer(&pRegistryManager->connectionCheckerTimer);

registryEventsTimeout.QuadPart = 0;

/* Set the ConnectionChecker routine to be called every so often */

KeSetTimerEx(&pRegistryManager->connectionCheckerTimer,

  registryEventsTimeout,

  (USERSPACE_CONNECTION_TIMEOUT+(USERSPACE_CONNECTION_TIMEOUT/2))*1000,

  &pRegistryManager->connectionCheckerFunction);

pRegistryManager->bReady = TRUE;

DbgPrint("RegistryMonitor: Successfully Loaded\n");

    /* Return success */

    return STATUS_SUCCESS;

}

/* Checks to see if the registry monitor has received an IOCTL from a userspace

   program in a while. If it hasn't then all old queued registry events are

   cleared. This is called periodically when the driver is loaded */

VOID ConnectionChecker(

    IN struct _KDPC  *Dpc,

    IN PVOID  DeferredContext,

    IN PVOID  SystemArgument1,

    IN PVOID  SystemArgument2

    )

{

PCAPTURE_REGISTRY_MANAGER pRegistryManager = (PCAPTURE_REGISTRY_MANAGER)DeferredContext;

if( (GetCurrentTime()-pRegistryManager->lastContactTime) > (USERSPACE_CONNECTION_TIMEOUT+(USERSPACE_CONNECTION_TIMEOUT/2)))

{

  DbgPrint("RegistryMonitor: WARNING Userspace IOCTL timeout, clearing old queued registry events\n");

  while(!IsListEmpty(&pRegistryManager->lQueuedRegistryEvents))

  {

   PLIST_ENTRY head = ExInterlockedRemoveHeadList(&pRegistryManager->lQueuedRegistryEvents, &pRegistryManager->lQueuedRegistryEventsSpinLock);

   PREGISTRY_EVENT_PACKET pRegistryEventPacket = CONTAINING_RECORD(head, REGISTRY_EVENT_PACKET, Link);

   ExFreePoolWithTag(pRegistryEventPacket->pRegistryEvent, REGISTRY_POOL_TAG);

   ExFreePoolWithTag(pRegistryEventPacket, REGISTRY_POOL_TAG);

  }

}

}

NTSTATUS KDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

    Irp->IoStatus.Status = STATUS_SUCCESS;

    Irp->IoStatus.Information=0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;

}

NTSTATUS HandleIoctlGetRegEvents(IN PDEVICE_OBJECT DeviceObject, PIRP Irp,

       PIO_STACK_LOCATION pIoStackIrp, UINT *pdwDataWritten)

{

    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PCHAR pOutputBuffer = Irp->UserBuffer;

    UINT dwOutputBufferSize = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;

PCAPTURE_REGISTRY_MANAGER pRegistryManager;

/* Get the registry manager from the device extension */

    pRegistryManager = gpDeviceObject->DeviceExtension;

*pdwDataWritten = 0;

/* Check we are allowed to write into the buffer passed from the user space program */

    if(pOutputBuffer)

    {

        try {

   ProbeForWrite(pOutputBuffer,

    pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength,

    __alignof (REGISTRY_EVENT));

   if(pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(REGISTRY_EVENT))

   {

    PLIST_ENTRY pRegistryListHead;

    PREGISTRY_EVENT_PACKET pRegistryPacket;

    UINT bufferSpace = dwOutputBufferSize;

    UINT bufferSpaceUsed = 0;

    /* Fill the buffer passed from userspace with registry events */

    while(!IsListEmpty(&pRegistryManager->lQueuedRegistryEvents) &&

     (bufferSpaceUsed < bufferSpace) &&

     ((bufferSpace-bufferSpaceUsed) >= sizeof(REGISTRY_EVENT)))

    {

     UINT registryEventSize = 0;

     pRegistryListHead = ExInterlockedRemoveHeadList(&pRegistryManager->lQueuedRegistryEvents, &pRegistryManager->lQueuedRegistryEventsSpinLock);

     pRegistryPacket = CONTAINING_RECORD(pRegistryListHead, REGISTRY_EVENT_PACKET, Link);

     registryEventSize = sizeof(REGISTRY_EVENT)+pRegistryPacket->pRegistryEvent->registryPathLengthB+pRegistryPacket->pRegistryEvent->dataLengthB;

     if((bufferSpace-bufferSpaceUsed) >= registryEventSize)

     {

      //DbgPrint("Sending-%i: %ls\n",bufferSpaceUsed, pRegistryPacket->pRegistryEvent->registryPath);

      RtlCopyMemory(pOutputBuffer+bufferSpaceUsed, pRegistryPacket->pRegistryEvent, registryEventSize);

      bufferSpaceUsed += registryEventSize;

      ExFreePoolWithTag(pRegistryPacket->pRegistryEvent, REGISTRY_POOL_TAG);

      ExFreePoolWithTag(pRegistryPacket, REGISTRY_POOL_TAG);

     } else {

      ExInterlockedInsertHeadList(&pRegistryManager->lQueuedRegistryEvents, &pRegistryPacket->Link, &pRegistryManager->lQueuedRegistryEventsSpinLock);

      break;

     }

    }

    /* Return the amount of space that is occupied with registry events */

    *pdwDataWritten = bufferSpaceUsed;

    status = STATUS_SUCCESS;

   } else {

    *pdwDataWritten = sizeof(REGISTRY_EVENT);

    status = STATUS_BUFFER_TOO_SMALL;

   }

        } except( EXCEPTION_EXECUTE_HANDLER ) {

   status = GetExceptionCode();   

   DbgPrint("RegistryMonitor: EXCEPTION IOCTL_CAPTURE_GET_REGEVENTS - %i\n", status);   

        }

    }

    return status;

}

NTSTATUS KDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

UINT dwDataWritten = 0;

    switch(irpStack->Parameters.DeviceIoControl.IoControlCode)

    {

  case IOCTL_CAPTURE_GET_REGEVENTS:

   UpdateLastContactTime();

   status = HandleIoctlGetRegEvents(DeviceObject, Irp, irpStack, &dwDataWritten);

   break;

        default:

            break;

    }

    Irp->IoStatus.Status = status;

    // Set # of bytes to copy back to user-mode...

    if(NT_SUCCESS(status))

        Irp->IoStatus.Information = dwDataWritten;

    else

        Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

return status;

}

VOID UpdateLastContactTime()

{

PCAPTURE_REGISTRY_MANAGER pRegistryManager;

/* Get the process manager from the device extension */

    pRegistryManager = gpDeviceObject->DeviceExtension;

pRegistryManager->lastContactTime = GetCurrentTime();

}

ULONG GetCurrentTime()

{

LARGE_INTEGER currentSystemTime;

LARGE_INTEGER currentLocalTime;

ULONG time;

KeQuerySystemTime(&currentSystemTime);

ExSystemTimeToLocalTime(&currentSystemTime,&currentLocalTime);

RtlTimeToSecondsSince1970(&currentLocalTime, &time);

return time;

}

BOOLEAN GetRegistryObjectCompleteName(PUNICODE_STRING pRegistryPath, PUNICODE_STRING pPartialRegistryPath, PVOID pRegistryObject)

{

PCAPTURE_REGISTRY_MANAGER pRegistryManager;

BOOLEAN foundCompleteName = FALSE;

BOOLEAN partial = FALSE;

/* Get the process manager from the device extension */

    pRegistryManager = gpDeviceObject->DeviceExtension;

/* Check to see if everything is valid */

/* We sometimes see a partial registry object name which is actually complete

    however if fails one of these checks for some reason. Not sure whether to report

    this registry event */

if((!MmIsAddressValid(pRegistryObject)) ||

  (pRegistryObject == NULL))

{

  return FALSE;

}

/* Check to see if the partial name is really the complete name */

if(pPartialRegistryPath != NULL)

{

  if( (((pPartialRegistryPath->Buffer[0] == '\\') || (pPartialRegistryPath->Buffer[0] == '%')) ||

   ((pPartialRegistryPath->Buffer[0] == 'T') && (pPartialRegistryPath->Buffer[1] == 'R') && (pPartialRegistryPath->Buffer[2] == 'Y') && (pPartialRegistryPath->Buffer[3] == '\\'))) )

  {

   RtlUnicodeStringCopy(pRegistryPath, pPartialRegistryPath);

   partial = TRUE;

   foundCompleteName = TRUE;

  }

}

if(!foundCompleteName)

{

  /* Query the object manager in the kernel for the complete name */

  NTSTATUS status;

  ULONG returnedLength;

  PUNICODE_STRING pObjectName = NULL;

  status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)pObjectName, 0, &returnedLength );

  if(status == STATUS_INFO_LENGTH_MISMATCH)

  {

   pObjectName = ExAllocatePoolWithTag(NonPagedPool, returnedLength, REGISTRY_POOL_TAG);

   status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)pObjectName, returnedLength, &returnedLength );

   if(NT_SUCCESS(status))

   {

    RtlUnicodeStringCopy(pRegistryPath, pObjectName);

    foundCompleteName = TRUE;

   }

   ExFreePoolWithTag(pObjectName, REGISTRY_POOL_TAG);

  }

}

//ASSERT(foundCompleteName == TRUE);

return foundCompleteName;

}

BOOLEAN QueueRegistryEvent(PREGISTRY_EVENT pRegistryEvent)

{

PCAPTURE_REGISTRY_MANAGER pRegistryManager;

    /* Get the registry manager from the device extension */

    pRegistryManager = gpDeviceObject->DeviceExtension;

/* Check the last contact time of the user space program before queuing */

if( (GetCurrentTime()-pRegistryManager->lastContactTime) <= USERSPACE_CONNECTION_TIMEOUT)

{

  PREGISTRY_EVENT_PACKET pRegistryEventPacket = ExAllocatePoolWithTag(NonPagedPool, sizeof(REGISTRY_EVENT_PACKET), REGISTRY_POOL_TAG);

  pRegistryEventPacket->pRegistryEvent = pRegistryEvent;

  /* Queue registry event */

  ExInterlockedInsertTailList(&pRegistryManager->lQueuedRegistryEvents, &pRegistryEventPacket->Link, &pRegistryManager->lQueuedRegistryEventsSpinLock);

  return TRUE;

}

return FALSE;

}

NTSTATUS RegistryCallback(IN PVOID CallbackContext,

        IN PVOID  Argument1,

        IN PVOID  Argument2)

{

//REGISTRY_EVENT registryEvent;

BOOLEAN registryEventIsValid = FALSE;

BOOLEAN exception = FALSE;

LARGE_INTEGER CurrentSystemTime;

LARGE_INTEGER CurrentLocalTime;

TIME_FIELDS TimeFields;

int type;

UNICODE_STRING registryPath;

UCHAR* registryData = NULL;

ULONG registryDataLength = 0;

ULONG registryDataType = 0;

/* Allocate a large 64kb string ... maximum path name allowed in windows */

registryPath.Length = 0;

registryPath.MaximumLength = NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR);

registryPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, registryPath.MaximumLength, REGISTRY_POOL_TAG);

if(registryPath.Buffer == NULL)

{

  return STATUS_SUCCESS;

}

/* Put the time this event occured into the registry event */

KeQuerySystemTime(&CurrentSystemTime);

ExSystemTimeToLocalTime(&CurrentSystemTime,&CurrentLocalTime);

//registryEvent.processId = PsGetCurrentProcessId();

//registryEvent.eventType = (REG_NOTIFY_CLASS)Argument1;

type = (REG_NOTIFY_CLASS)Argument1;

try

{

  /* Large switch statement for all registry events ... fairly easy to understand */

  switch(type)

  {

  case RegNtPostCreateKey:

  {

   PREG_POST_CREATE_KEY_INFORMATION createKey = (PREG_POST_CREATE_KEY_INFORMATION)Argument2;

   if(NT_SUCCESS(createKey->Status))

   {

    PVOID* registryObject = createKey->Object;

    registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, createKey->CompleteName, *registryObject);

   }

   break;

  }

  case RegNtPostOpenKey:

  {

   PREG_POST_OPEN_KEY_INFORMATION openKey = (PREG_POST_OPEN_KEY_INFORMATION)Argument2;

   if(NT_SUCCESS(openKey->Status))

   {

    PVOID* registryObject = openKey->Object;

    registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, openKey->CompleteName, *registryObject);

   }

   break;

  }

  case RegNtPreDeleteKey:

  {

   PREG_DELETE_KEY_INFORMATION deleteKey = (PREG_DELETE_KEY_INFORMATION)Argument2;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, deleteKey->Object);

   break;

  }

  case RegNtDeleteValueKey:

  {

   PREG_DELETE_VALUE_KEY_INFORMATION deleteValueKey = (PREG_DELETE_VALUE_KEY_INFORMATION)Argument2;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, deleteValueKey->Object);

   if((registryEventIsValid) && (deleteValueKey->ValueName->Length > 0))

   {

    RtlUnicodeStringCatString(&registryPath,L"\\");

    RtlUnicodeStringCat(&registryPath, deleteValueKey->ValueName);

   }

   break;

  }

  case RegNtPreSetValueKey:

  {

   PREG_SET_VALUE_KEY_INFORMATION setValueKey = (PREG_SET_VALUE_KEY_INFORMATION)Argument2;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, setValueKey->Object);

   if((registryEventIsValid) && (setValueKey->ValueName->Length > 0))

   {

    registryDataType = setValueKey->Type;

    registryDataLength = setValueKey->DataSize;

    registryData = ExAllocatePoolWithTag(NonPagedPool, registryDataLength, REGISTRY_POOL_TAG);

    if(registryData != NULL)

    {

     RtlCopyBytes(registryData,setValueKey->Data,setValueKey->DataSize);

    } else {

     DbgPrint("RegistryMonitor: ERROR can't allocate memory for setvalue data\n");

    }

    RtlUnicodeStringCatString(&registryPath,L"\\");

    RtlUnicodeStringCat(&registryPath, setValueKey->ValueName);

   }

   break;

  }

  case RegNtEnumerateKey:

  {

   PREG_ENUMERATE_KEY_INFORMATION enumerateKey = (PREG_ENUMERATE_KEY_INFORMATION)Argument2;

   registryDataType = enumerateKey->KeyInformationClass;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, enumerateKey->Object);

   break;

  }

  case RegNtEnumerateValueKey:

  {

   PREG_ENUMERATE_VALUE_KEY_INFORMATION enumerateValueKey = (PREG_ENUMERATE_VALUE_KEY_INFORMATION)Argument2;

   registryDataType = enumerateValueKey->KeyValueInformationClass;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, enumerateValueKey->Object);

   break;

  }

  case RegNtQueryKey:

  {

   PREG_QUERY_KEY_INFORMATION queryKey = (PREG_QUERY_KEY_INFORMATION)Argument2;

   registryDataType = queryKey->KeyInformationClass;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, queryKey->Object);

   break;

  }

  case RegNtQueryValueKey:

  {

   PREG_QUERY_VALUE_KEY_INFORMATION queryValueKey = (PREG_QUERY_VALUE_KEY_INFORMATION)Argument2;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, queryValueKey->Object);

   if((registryEventIsValid) && (queryValueKey->ValueName->Length > 0))

   {

    registryDataType = queryValueKey->KeyValueInformationClass;

    RtlUnicodeStringCatString(&registryPath,L"\\");

    RtlUnicodeStringCat(&registryPath, queryValueKey->ValueName);

   }

   break;

  }

  case RegNtKeyHandleClose:

  {

   PREG_KEY_HANDLE_CLOSE_INFORMATION closeKey = (PREG_KEY_HANDLE_CLOSE_INFORMATION)Argument2;

   registryEventIsValid = GetRegistryObjectCompleteName(&registryPath, NULL, closeKey->Object);

   break;

  }

  default:

   break;

  }

} except( EXCEPTION_EXECUTE_HANDLER ) {

  /* Do nothing if an exception occured ... event won't be queued */

  registryEventIsValid = FALSE;

  exception = TRUE;

}

if(registryEventIsValid)

{

  PREGISTRY_EVENT pRegistryEvent;

  UINT eventSize = sizeof(REGISTRY_EVENT)+registryPath.Length+(sizeof(WCHAR))+registryDataLength;

  pRegistryEvent = ExAllocatePoolWithTag(NonPagedPool, eventSize, REGISTRY_POOL_TAG);

  if(pRegistryEvent != NULL)

  {

   pRegistryEvent->registryPathLengthB = registryPath.Length+sizeof(WCHAR);

   pRegistryEvent->dataType = registryDataType;

   pRegistryEvent->dataLengthB = registryDataLength;

   //RtlStringCbCopyUnicodeString(pRegistryEvent->registryPath, pRegistryEvent->registryPathLength, &registryPath);

   RtlCopyBytes(pRegistryEvent->registryData, registryPath.Buffer, registryPath.Length);

   pRegistryEvent->registryData[registryPath.Length] = '\0';

   pRegistryEvent->registryData[registryPath.Length+1] = '\0';

   RtlCopyBytes(pRegistryEvent->registryData+pRegistryEvent->registryPathLengthB, registryData, registryDataLength);

   if(registryData != NULL)

   {

    ExFreePoolWithTag(registryData, REGISTRY_POOL_TAG);

   }

   pRegistryEvent->processId = PsGetCurrentProcessId();

   RtlTimeToTimeFields(&CurrentLocalTime,&pRegistryEvent->time);

   pRegistryEvent->eventType = (REG_NOTIFY_CLASS)Argument1;

   if(!QueueRegistryEvent(pRegistryEvent))

   {

    ExFreePoolWithTag(pRegistryEvent, REGISTRY_POOL_TAG);

   }

  }

}

if(registryPath.Buffer != NULL)

{

  ExFreePoolWithTag(registryPath.Buffer, REGISTRY_POOL_TAG);

}

/* Always return a success ... we aren't doing any filtering, just monitoring */

return STATUS_SUCCESS;

}

void UnloadDriver(IN PDRIVER_OBJECT DriverObject)

{

    UNICODE_STRING  uszDeviceString;

NTSTATUS        ntStatus;

PCAPTURE_REGISTRY_MANAGER pRegistryManager;

    /* Get the registry manager from the device extension */

    pRegistryManager = gpDeviceObject->DeviceExtension;

if(pRegistryManager->bReady == TRUE)

{

  KeCancelTimer(&pRegistryManager->connectionCheckerTimer);

  CmUnRegisterCallback(pRegistryManager->registryCallbackCookie);

  pRegistryManager->bReady = FALSE;

}

while(!IsListEmpty(&pRegistryManager->lQueuedRegistryEvents))

{

  PLIST_ENTRY head = ExInterlockedRemoveHeadList(&pRegistryManager->lQueuedRegistryEvents, &pRegistryManager->lQueuedRegistryEventsSpinLock);

  PREGISTRY_EVENT_PACKET pRegistryEventPacket = CONTAINING_RECORD(head, REGISTRY_EVENT_PACKET, Link);

  ExFreePoolWithTag(pRegistryEventPacket->pRegistryEvent, REGISTRY_POOL_TAG);

  ExFreePoolWithTag(pRegistryEventPacket, REGISTRY_POOL_TAG);

}

RtlInitUnicodeString(&uszDeviceString, L"[url=file://\\DosDevices\\RegistryMonitor]\\DosDevices\\RegistryMonitor[/url]");

    IoDeleteSymbolicLink(&uszDeviceString);

    if(DriverObject->DeviceObject != NULL)

{

  IoDeleteDevice(DriverObject->DeviceObject);

}

}