1.first intruction will be usermode will send a message to km saying hi once km recieves the message it will send a message back saying hi then usermode can send given intructions 
2. make it so that usermode will send intructions with what it want km to do typedef struct _INSTRUCTIONS {
BOOLEAN close;
BOOLEAN read;
BOOLEAN reqBase;
PVOID bufferAddress;
UINT_PTR address;
ULONGLONG size;
PVOID output;
ULONG64 baseAddress;
char moduleName[256]; // Changed from 'const char' to a character array for string storage
} INSTRUCTIONS, PINSTRUCTIONS; that km will recieve intructions read and do it and after doing  intruction it will send results to usermode and usermode will read and display result

do not change communication method 



usermode
using System;
using NtApiDotNet;
using System.Threading;

namespace WnfSubscriber {
  class Program {
    // Define the WNF state name
    private
    const ulong MY_WNF_STATE_NAME = 0x41C64E6DA3BC1074;

    static void Main(string[] args) {
      WnfClient client = new WnfClient();

      // Subscribe to the WNF state
      client.SubscribeState(MY_WNF_STATE_NAME, OnWnfStateChange);

      Console.WriteLine("Subscribed to WNF state. Waiting for changes...");

      // Keep the application running
      while (true) {
        Thread.Sleep(1000);
      }
    }

    // Callback when WNF state changes
    static void OnWnfStateChange(WnfStateChange change) {
      Console.WriteLine("WNF State Changed!");

      // Assuming the state data is the INSTRUCTIONS structure
      // Deserialize the data accordingly
      if (change.Data.Length >= Marshal.SizeOf(typeof (INSTRUCTIONS))) {
        INSTRUCTIONS instructions = change.Data.ToStructure < INSTRUCTIONS > ();

        Console.WriteLine("Received Instructions:");
        Console.WriteLine($"Close: {instructions.close}");
        Console.WriteLine($"Read: {instructions.read}");
        Console.WriteLine($"Request Base: {instructions.reqBase}");
        Console.WriteLine($"Buffer Address: {instructions.bufferAddress}");
        Console.WriteLine($"Address: 0x{instructions.address:X}");
        Console.WriteLine($"Size: {instructions.size}");
        Console.WriteLine($"Output: {instructions.output}");
        Console.WriteLine($"Base Address: 0x{instructions.baseAddress:X}");
        Console.WriteLine($"Module Name: {instructions.moduleName}");
      } else {
        Console.WriteLine("Received data is smaller than expected.");
      }
    }

    // Define the INSTRUCTIONS structure
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct INSTRUCTIONS {
      public byte close;
      public byte read;
      public byte reqBase;
      public IntPtr bufferAddress;
      public UIntPtr address;
      public ulong size;
      public IntPtr output;
      public ulong baseAddress;

      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
      public string moduleName;
    }
  }
}

kernel mode 
#include <ntifs.h>

#include <ntddk.h>

// ==========================================================
// Definitions and Macros
// ==========================================================

#define TARGET_PROCESS_NAME L "notepad.exe" // Replace with your target process name
#define MY_WNF_STATE_NAME 0x2821B2CA3BC4075 ULL // WNF_AUDC_CAPTURE

// Windows Build Numbers
#define WINDOWS_1803 17134
#define WINDOWS_1809 17763
#define WINDOWS_1903 18362
#define WINDOWS_1909 18363
#define WINDOWS_2004 19041
#define WINDOWS_20H2 19569
#define WINDOWS_21H1 20180

// Page size definitions
#define PAGE_OFFSET_SIZE 12
#define PMASK 0x000FFFFFFFFFF000 ull // Corrected PMASK for x64

// ==========================================================
// Function Pointer Types for WNF (Undocumented APIs)
// ==========================================================

typedef ULONG64 WNF_STATE_NAME;
typedef ULONG64 WNF_CHANGE_STAMP;

typedef enum _WNF_SUBSCRIPTION_FLAG {
  WnfSubscriptionNone = 0x0,
    WnfSubscriptionPublishCurrentState = 0x1,
    // Add other flags as needed
}
WNF_SUBSCRIPTION_FLAG;

typedef struct _WNF_SUBSCRIPTION {
  // Define necessary fields based on WNF structures
}
WNF_SUBSCRIPTION, PWNF_SUBSCRIPTION;

typedef struct _WNF_TYPE_ID {
  // Define Type ID structure based on WNF specifications
}
WNF_TYPE_ID, PCWNF_TYPE_ID;

// Function pointer typedefs
typedef NTSTATUS(PExSubscribeWnfStateChange)(
  PWNF_SUBSCRIPTION Subscription,
  WNF_STATE_NAME StateName,
  WNF_SUBSCRIPTION_FLAG SubscriptionFlag,
  PVOID Callback,
  PVOID CallbackContext,
  PCWNF_TYPE_ID TypeId,
  PVOID DeliveryDescriptor,
  ULONG DeliveryDescriptorSize
);

typedef NTSTATUS(PExPublishWnfStateData)(
  PCWNF_STATE_NAME StateName,
  PVOID Buffer,
  ULONG Length,
  PCWNF_TYPE_ID TypeId,
  PVOID ExplicitScope,
  ULONG MatchingChangeStamp
);

typedef NTSTATUS(PExUnsubscribeWnfStateChange)(
  PWNF_SUBSCRIPTION Subscription
);

// Global function pointers
PExPublishWnfStateData ExPublishWnfStateDataFunc = NULL;
PExUnsubscribeWnfStateChange ExUnsubscribeWnfStateChangeFunc = NULL;

// ==========================================================
// Data Structures
// ==========================================================

typedef struct _INSTRUCTIONS {
  BOOLEAN close;
  BOOLEAN read;
  BOOLEAN reqBase;
  PVOID bufferAddress;
  UINT_PTR address;
  ULONGLONG size;
  PVOID output;
  ULONG64 baseAddress;
  char moduleName[256]; // Changed from 'const char' to a character array for string storage
}
INSTRUCTIONS, PINSTRUCTIONS;

// ==========================================================
// Function Declarations
// ==========================================================

NTKERNELAPI
PVOID
PsGetProcessSectionBaseAddress(
  __in PEPROCESS Process
);

PVOID GetProcessBaseAddress(HANDLE pid);
DWORD GetUserDirectoryTableBaseOffset();
ULONG_PTR GetProcessCr3(PEPROCESS pProcess);
ULONG_PTR GetKernelDirBase();
NTSTATUS ReadVirtual(uint64_t dirbase, uint64_t address, uint8_t buffer, SIZE_T size, SIZE_T read);
NTSTATUS WriteVirtual(uint64_t dirbase, uint64_t address, uint8_t buffer, SIZE_T size, SIZE_T written);
NTSTATUS ReadPhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T BytesRead);
NTSTATUS WritePhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T BytesWritten);
uint64_t TranslateLinearAddress(uint64_t directoryTableBase, uint64_t virtualAddress);
NTSTATUS ReadProcessMemory(int pid, PVOID Address, PVOID AllocatedBuffer, SIZE_T size, SIZE_T read);
NTSTATUS WriteProcessMemory(int pid, PVOID Address, PVOID AllocatedBuffer, SIZE_T size, SIZE_T * written);
VOID ProcessNotifyCallback(
  __in HANDLE ParentId,
  __in HANDLE ProcessId,
  __in BOOLEAN Create
);
VOID DriverUnload(PDRIVER_OBJECT DriverObject);

// ==========================================================
// Global Variables
// ==========================================================

PWNF_SUBSCRIPTION g_WnfSubscription = NULL;

// ==========================================================
// Function Definitions
// ==========================================================

// Retrieve the base address of a process given its PID
PVOID GetProcessBaseAddress(HANDLE pid) {
  PEPROCESS pProcess = NULL;
  if (pid == NULL) return NULL;

  NTSTATUS NtRet = PsLookupProcessByProcessId(pid, & pProcess);
  if (NtRet != STATUS_SUCCESS) return NULL;

  PVOID Base = PsGetProcessSectionBaseAddress(pProcess);
  ObDereferenceObject(pProcess);
  return Base;
}

// Get the offset for the UserDirectoryTableBase based on Windows version
DWORD GetUserDirectoryTableBaseOffset() {
  RTL_OSVERSIONINFOW ver = {
    0
  };
  RtlGetVersion( & ver);

  switch (ver.dwBuildNumber) {
  case WINDOWS_1803:
  case WINDOWS_1809:
    return 0x0278;
  case WINDOWS_1903:
  case WINDOWS_1909:
  case WINDOWS_2004:
  case WINDOWS_20H2:
  case WINDOWS_21H1:
  default:
    return 0x0388;
  }
}

// Retrieve the CR3 (Page Directory Base) of a process
ULONG_PTR GetProcessCr3(PEPROCESS pProcess) {
  PUCHAR process = (PUCHAR) pProcess;
  #ifdef _WIN64
  ULONG_PTR * process_dirbase_ptr = (ULONG_PTR)(process + 0x28); // dirbase for x64
  #else
  ULONG_PTR process_dirbase_ptr = (ULONG_PTR * )(process + 0x18); // dirbase for x86
  #endif
  ULONG_PTR process_dirbase = process_dirbase_ptr;

  if (process_dirbase == 0) {
    DWORD UserDirOffset = GetUserDirectoryTableBaseOffset();
    ULONG_PTR process_userdirbase_ptr = (ULONG_PTR * )(process + UserDirOffset);
    return process_userdirbase_ptr;
  }
  return process_dirbase;
}

// Example function to get the kernel's CR3 (for demonstration; typically not used)
ULONG_PTR GetKernelDirBase() {
  // Note: Accessing the kernel's CR3 is generally unsafe and not recommended
  // This is just for illustrative purposes
  return __readcr3();
}

// Translate a virtual address to a physical address using the provided directory table base
uint64_t TranslateLinearAddress(uint64_t directoryTableBase, uint64_t virtualAddress) {
  directoryTableBase &= 0xFFFFFFFFFF000 ull; // Align directoryTableBase

  uint64_t pageOffset = virtualAddress & 0xFFF;
  uint64_t pte = (virtualAddress >> 12) & 0x1FF;
  uint64_t pt = (virtualAddress >> 21) & 0x1FF;
  uint64_t pd = (virtualAddress >> 30) & 0x1FF;
  uint64_t pdp = (virtualAddress >> 39) & 0x1FF;

  SIZE_T readsize = 0;
  uint64_t pdpe = 0;
  if (!NT_SUCCESS(ReadPhysicalAddress((PVOID)(directoryTableBase + 8 * pdp), & pdpe, sizeof(pdpe), & readsize)) || (pdpe & 1))
    return 0;

  uint64_t pde = 0;
  if (!NT_SUCCESS(ReadPhysicalAddress((PVOID)((pdpe & PMASK) + 8 * pd), & pde, sizeof(pde), & readsize)) || (pde & 1))
    return 0;

  // 1GB large page
  if (pde & (1 << 7)) // Check the PS (Page Size) bit
    return (pde & 0x000FFFFFFFFFF000 ull) + (virtualAddress & 0x3FFFFFFF ull);

  uint64_t pteAddr = 0;
  if (!NT_SUCCESS(ReadPhysicalAddress((PVOID)((pde & PMASK) + 8 * pt), & pteAddr, sizeof(pteAddr), & readsize)) || (pteAddr & 1))
    return 0;

  // 2MB large page
  if (pteAddr & (1 << 7)) // Check the PS bit
    return (pteAddr & PMASK) + (virtualAddress & 0x1FFFFF ull);

  uint64_t physPage = 0;
  if (!NT_SUCCESS(ReadPhysicalAddress((PVOID)((pteAddr & PMASK) + 8 * pte), & physPage, sizeof(physPage), & readsize)) || !(physPage & 1))
    return 0;

  physPage &= PMASK;
  return physPage + pageOffset;
}

// Read a physical address into a buffer
NTSTATUS ReadPhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T BytesRead) {
  if (TargetAddress == NULL || lpBuffer == NULL || BytesRead == NULL)
    return STATUS_INVALID_PARAMETER;

  MM_COPY_ADDRESS AddrToRead = {
    0
  };
  AddrToRead.PhysicalAddress.QuadPart = (ULONG_PTR) TargetAddress;

  NTSTATUS status = MmCopyMemory(lpBuffer, AddrToRead, Size, MM_COPY_MEMORY_PHYSICAL, BytesRead);
  return status;
}

// Write a buffer to a physical address
NTSTATUS WritePhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T * BytesWritten) {
  if (!TargetAddress || !lpBuffer || !BytesWritten)
    return STATUS_INVALID_PARAMETER;

  PHYSICAL_ADDRESS AddrToWrite = {
    0
  };
  AddrToWrite.QuadPart = (ULONG_PTR) TargetAddress;

  // Limit MmMapIoSpaceEx to PAGE_SIZE (4096 bytes)
  if (Size > PAGE_SIZE)
    return STATUS_INVALID_PARAMETER;

  PVOID pmapped_mem = MmMapIoSpaceEx(AddrToWrite, Size, PAGE_READWRITE);
  if (!pmapped_mem)
    return STATUS_UNSUCCESSFUL;

  RtlCopyMemory(pmapped_mem, lpBuffer, Size);
  BytesWritten = Size;
  MmUnmapIoSpace(pmapped_mem, Size);
  return STATUS_SUCCESS;
}

// Read virtual memory from a process
NTSTATUS ReadVirtual(uint64_t dirbase, uint64_t address, uint8_t buffer, SIZE_T size, SIZE_T * read) {
  if (!buffer || !read)
    return STATUS_INVALID_PARAMETER;

  uint64_t paddress;
  NTSTATUS status = STATUS_SUCCESS;
  SIZE_T CurOffset = 0;
  SIZE_T TotalSize = size;

  while (TotalSize > 0) {
    paddress = TranslateLinearAddress(dirbase, address + CurOffset);
    if (!paddress)
      return STATUS_UNSUCCESSFUL;

    ULONG64 ReadSize = min(PAGE_SIZE - (paddress & 0xFFF), TotalSize);
    SIZE_T BytesReadLocal = 0;

    status = ReadPhysicalAddress((PVOID) paddress, buffer + CurOffset, ReadSize, & BytesReadLocal);
    if (!NT_SUCCESS(status) || BytesReadLocal == 0)
      break;

    TotalSize -= BytesReadLocal;
    CurOffset += BytesReadLocal;
  }

  read = CurOffset;
  return status;
}

// Write virtual memory to a process
NTSTATUS WriteVirtual(uint64_t dirbase, uint64_t address, uint8_t buffer, SIZE_T size, SIZE_T * written) {
  if (!buffer || !written)
    return STATUS_INVALID_PARAMETER;

  uint64_t paddress;
  NTSTATUS status = STATUS_SUCCESS;
  SIZE_T CurOffset = 0;
  SIZE_T TotalSize = size;

  while (TotalSize > 0) {
    paddress = TranslateLinearAddress(dirbase, address + CurOffset);
    if (!paddress)
      return STATUS_UNSUCCESSFUL;

    ULONG64 WriteSize = min(PAGE_SIZE - (paddress & 0xFFF), TotalSize);
    SIZE_T BytesWrittenLocal = 0;

    status = WritePhysicalAddress((PVOID) paddress, buffer + CurOffset, WriteSize, & BytesWrittenLocal);
    if (!NT_SUCCESS(status) || BytesWrittenLocal == 0)
      break;

    TotalSize -= BytesWrittenLocal;
    CurOffset += BytesWrittenLocal;
  }

  written = CurOffset;
  return status;
}

// Read the memory of a process by PID
NTSTATUS ReadProcessMemory(int pid, PVOID Address, PVOID AllocatedBuffer, SIZE_T size, SIZE_T read) {
  if (pid == 0 || Address == NULL || AllocatedBuffer == NULL || read == NULL)
    return STATUS_INVALID_PARAMETER;

  PEPROCESS pProcess = NULL;
  NTSTATUS NtRet = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR) pid, & pProcess);
  if (NtRet != STATUS_SUCCESS)
    return NtRet;

  ULONG_PTR process_dirbase = GetProcessCr3(pProcess);
  ObDereferenceObject(pProcess);

  NtRet = ReadVirtual(process_dirbase, (ULONG_PTR) Address, (uint8_t * ) AllocatedBuffer, size, read);
  return NtRet;
}

// Write the memory of a process by PID
NTSTATUS WriteProcessMemory(int pid, PVOID Address, PVOID AllocatedBuffer, SIZE_T size, SIZE_T * written) {
  if (pid == 0 || Address == NULL || AllocatedBuffer == NULL || written == NULL)
    return STATUS_INVALID_PARAMETER;

  PEPROCESS pProcess = NULL;
  NTSTATUS NtRet = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR) pid, & pProcess);
  if (NtRet != STATUS_SUCCESS)
    return NtRet;

  ULONG_PTR process_dirbase = GetProcessCr3(pProcess);
  ObDereferenceObject(pProcess);

  NtRet = WriteVirtual(process_dirbase, (ULONG_PTR) Address, (uint8_t * ) AllocatedBuffer, size, written);
  return NtRet;
}

// Callback function for WNF state changes
NTSTATUS notifCallback(
  PWNF_SUBSCRIPTION Subscription,
  WNF_STATE_NAME StateName,
  ULONG SubscribedEventSet,
  WNF_CHANGE_STAMP ChangeStamp,
  PCWNF_TYPE_ID TypeId,
  PVOID CallbackContext
) {
  UNREFERENCED_PARAMETER(TypeId);
  UNREFERENCED_PARAMETER(CallbackContext);
  UNREFERENCED_PARAMETER(SubscribedEventSet);

  NTSTATUS status = STATUS_SUCCESS;
  ULONG bufferSize = 0x0;
  PVOID pStateData = NULL;
  WNF_CHANGE_STAMP changeStampLocal = 0;

  // Query required buffer size
  status = ExQueryWnfStateDataFunc(Subscription, & changeStampLocal, NULL, & bufferSize);
  if (status != STATUS_BUFFER_TOO_SMALL)
    goto Exit;

  // Allocate buffer
  pStateData = ExAllocatePoolWithTag(PagedPool, bufferSize, 'LULZ');
  if (pStateData == NULL) {
    status = STATUS_UNSUCCESSFUL;
    goto Exit;
  }

  // Query the state data
  status = ExQueryWnfStateDataFunc(Subscription, & changeStampLocal, pStateData, & bufferSize);
  if (NT_SUCCESS(status)) {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "## Data processed: %S\n", (PWSTR) pStateData);
    // Implement further processing of pStateData as needed
  }

  Exit:
    if (pStateData != NULL)
      ExFreePoolWithTag(pStateData, 'LULZ');
  return status;
}

// Function to subscribe to WNF state changes
NTSTATUS CallExSubscribeWnfStateChange() {
  PWNF_SUBSCRIPTION wnfSubscription = NULL;
  WNF_STATE_NAME stateName = MY_WNF_STATE_NAME; // Use the corrected WNF state name
  NTSTATUS status;

  status = ExSubscribeWnfStateChange( & wnfSubscription, stateName, WnfSubscriptionPublishCurrentState,
    NULL, ¬ifCallback, NULL, NULL, 0);
  if (NT_SUCCESS(status)) {
    g_WnfSubscription = wnfSubscription;
    DbgPrint("Subscription address: %p\n", (PVOID) wnfSubscription);
  }

  return status;
}

// ==========================================================
// Process Notification Callback
// ==========================================================

VOID ProcessNotifyCallback(
  __in HANDLE ParentId,
  __in HANDLE ProcessId,
  __in BOOLEAN Create
) {
  UNREFERENCED_PARAMETER(ParentId);

  if (Create) {
    PEPROCESS pProcess = NULL;
    NTSTATUS status = PsLookupProcessByProcessId(ProcessId, & pProcess);
    if (NT_SUCCESS(status)) {
      // Get process image file name
      WCHAR imageName[300] = {
        0
      };
      UNICODE_STRING uProcessImageName = {
        0
      };

      // Use SeLocateProcessImageName
      status = SeLocateProcessImageName(pProcess, & uProcessImageName);
      if (NT_SUCCESS(status)) {
        // Extract file name from full path
        PWSTR processName = wcsrchr(uProcessImageName.Buffer, L ''); // Corrected to search for backslash
        if (processName) {
          processName++; // Move past the backslash

          // Check if this is the target process
          if (_wcsicmp(processName, TARGET_PROCESS_NAME) == 0) {
            // Perform memory operations here

            // Get base address
            PVOID baseAddress = GetProcessBaseAddress(ProcessId);
            if (baseAddress) {
              DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Base address of %ws: %p\n", processName, baseAddress);

              // Example: Read some memory from the process
              UCHAR buffer[256] = {
                0
              };
              SIZE_T bytesRead = 0;
              NTSTATUS ntStatus = ReadProcessMemory((int)(ULONG_PTR) ProcessId, baseAddress, buffer, sizeof(buffer), & bytesRead);

              if (NT_SUCCESS(ntStatus)) {
                // Do something with the read data
                DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Successfully read %zu bytes from %ws\n", bytesRead, processName);
              } else {
                DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to read process memory: 0x%X\n", ntStatus);
              }

              // Example: Write to process memory (be cautious with actual addresses)
              /
              UCHAR writeData[256] = {
                / ... */
              };
              SIZE_T bytesWritten = 0;
              ntStatus = WriteProcessMemory((int)(ULONG_PTR) ProcessId, baseAddress, writeData, sizeof(writeData), & bytesWritten);

              if (NT_SUCCESS(ntStatus)) {
                DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Successfully wrote %zu bytes to %ws\n", bytesWritten, processName);
              } else {
                DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to write process memory: 0x%X\n", ntStatus);
              }
              /
            } else {
              DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to get base address of %ws\n", processName);
            }
          }
        } else {
          DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to parse process name\n");
        }

        // Free the UNICODE_STRING allocated by SeLocateProcessImageName
        ExFreePoolWithTag(uProcessImageName.Buffer, 'imgN');
      } else {
        DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] SeLocateProcessImageName failed with status: 0x%X\n", status);
      }

      ObDereferenceObject(pProcess);
    } else {
      DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] PsLookupProcessByProcessId failed with status: 0x%X\n", status);
    }
  }
}

// ==========================================================
// Driver Unload Routine
// ==========================================================

VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
  UNICODE_STRING symbolicLinkName;
  RtlInitUnicodeString( & symbolicLinkName, L "??\MyKernelDriverLink");

  // Remove the process notify routine
  NTSTATUS status = PsSetCreateProcessNotifyRoutine(ProcessNotifyCallback, TRUE);
  if (!NT_SUCCESS(status)) {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to remove process notify routine: 0x%X\n", status);
  } else {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Process notify routine removed successfully\n");
  }

  // Unsubscribe WNF state change if subscribed
  if (g_WnfSubscription != NULL && ExUnsubscribeWnfStateChangeFunc != NULL) {
    ExUnsubscribeWnfStateChangeFunc(g_WnfSubscription);
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] WNF subscription unsubscribed successfully\n");
  }

  // Delete the symbolic link and device object (if created)
  // Assuming device creation was implemented as per user requirements. If not, this section can be omitted.

  DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Driver unloaded\n");
}

// ==========================================================
// WNF Functions and DriverEntry Implementation
// ==========================================================

// Function to initialize WNF communication
NTSTATUS InitializeWNF() {
  // Dynamically resolve the ExPublishWnfStateData function
  UNICODE_STRING publishFunctionName;
  RtlInitUnicodeString( & publishFunctionName, L "ExPublishWnfStateData");
  ExPublishWnfStateDataFunc = (PExPublishWnfStateData) MmGetSystemRoutineAddress( & publishFunctionName);

  // Dynamically resolve the ExUnsubscribeWnfStateChange function
  UNICODE_STRING unsubscribeFunctionName;
  RtlInitUnicodeString( & unsubscribeFunctionName, L "ExUnsubscribeWnfStateChange");
  ExUnsubscribeWnfStateChangeFunc = (PExUnsubscribeWnfStateChange) MmGetSystemRoutineAddress( & unsubscribeFunctionName);

  if (ExPublishWnfStateDataFunc == NULL || ExUnsubscribeWnfStateChangeFunc == NULL) {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to resolve WNF functions\n");
    return STATUS_UNSUCCESSFUL;
  }

  // Prepare your data (if needed)
  /
  INSTRUCTIONS instructions = {
    0
  };
  instructions.reqBase = TRUE;
  instructions.baseAddress = 0x12345678; // Example base address
  instructions.output = (PVOID) instructions.baseAddress;

  // Publish the data to the WNF state
  NTSTATUS status = ExPublishWnfStateDataFunc( &
    MY_WNF_STATE_NAME, &
    instructions,
    sizeof(INSTRUCTIONS),
    NULL,
    NULL,
    0
  );

  if (!NT_SUCCESS(status)) {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] ExPublishWnfStateData failed: 0x%08X\n", status);
    return status;
  }

  DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Published WNF state data successfully\n");
  /

  // Subscribe to the WNF state change
  status = CallExSubscribeWnfStateChange();
  if (!NT_SUCCESS(status)) {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] ExSubscribeWnfStateChange failed: 0x%08X\n", status);
    return status;
  }

  DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Subscribed to WNF state changes successfully\n");
  return STATUS_SUCCESS;
}

// Main DriverEntry function
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
  UNREFERENCED_PARAMETER(RegistryPath);

  NTSTATUS status;

  // Register the process creation notify routine
  status = PsSetCreateProcessNotifyRoutine(ProcessNotifyCallback, FALSE);
  if (!NT_SUCCESS(status)) {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] PsSetCreateProcessNotifyRoutine failed: 0x%X\n", status);
    return status;
  } else {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Process notify routine set successfully\n");
  }

  // Initialize WNF communication
  status = InitializeWNF();
  if (!NT_SUCCESS(status)) {
    // If WNF initialization fails, unload the driver
    PsSetCreateProcessNotifyRoutine(ProcessNotifyCallback, TRUE);
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] WNF initialization failed. Driver will unload.\n");
    return status;
  }

  // Set the unload routine
  DriverObject -> DriverUnload = DriverUnload;

  DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Driver loaded successfully\n");
  return STATUS_SUCCESS;
}