take the use of threads out of my driver remember my rules no io, irp, IOCTL, irp mjor functions, system threads, permanent hooks, driver objects, section objects or device objects ollowed.


#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 0x41C64E6DA3BC1074ULL // WNF_AUDC_CAPTURE
#define BASE64_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
// 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 0x000FFFFFFFFFF000ull // 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
// ==========================================================
#pragma pack(push, 1)

typedef struct _INSTRUCTIONS {
BOOLEAN close;
BOOLEAN read;
BOOLEAN write;
BOOLEAN reqBase;
UINT_PTR processId;
UINT_PTR address;
ULONGLONG size;
PVOID bufferAddress;
PVOID output;
ULONG64 baseAddress;
char moduleName[256];
char data[1024]; // Added to match user-mode
} INSTRUCTIONS, PINSTRUCTIONS;
#pragma pack(pop)

// ==========================================================
// 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;
}
}
UCHAR Base64DecodeChar(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
return 0xFF; // Invalid character
}

NTSTATUS DecodeBase64(const char* base64Input, UCHAR* outputBuffer, SIZE_T bufferSize, SIZE_T* decodedSize) {
if (!base64Input || !outputBuffer || !decodedSize)
return STATUS_INVALID_PARAMETER;

SIZE_T inputLen = strlen(base64Input);
if (inputLen % 4 != 0)
return STATUS_INVALID_PARAMETER;

SIZE_T outputLen = (inputLen / 4) * 3;
if (outputLen > bufferSize)
return STATUS_BUFFER_TOO_SMALL;

decodedSize = 0;

for (SIZE_T i = 0; i < inputLen; i += 4) {
UCHAR sextet_a = Base64DecodeChar(base64Input[i]);
UCHAR sextet_b = Base64DecodeChar(base64Input[i + 1]);
UCHAR sextet_c = Base64DecodeChar(base64Input[i + 2]);
UCHAR sextet_d = Base64DecodeChar(base64Input[i + 3]);

if (sextet_a == 0xFF || sextet_b == 0xFF)
return STATUS_INVALID_PARAMETER;

UCHAR triple[3] = {0};

triple[0] = (sextet_a << 2) | (sextet_b >> 4);
if (base64Input[i + 2] != '=') {
if (sextet_c == 0xFF)
return STATUS_INVALID_PARAMETER;
triple[1] = ((sextet_b & 0xF) << 4) | (sextet_c >> 2);
}
if (base64Input[i + 3] != '=') {
if (sextet_d == 0xFF)
return STATUS_INVALID_PARAMETER;
triple[2] = ((sextet_c & 0x3) << 6) | sextet_d;
}

// Copy the decoded bytes to the output buffer
outputBuffer[decodedSize] = triple[0];
(decodedSize)++;

if (base64Input[i + 2] != '=') {
outputBuffer[decodedSize] = triple[1];
(decodedSize)++;
}

if (base64Input[i + 3] != '=') {
outputBuffer[decodedSize] = triple[2];
(decodedSize)++;
}
}

return STATUS_SUCCESS;
}

NTSTATUS EncodeBase64(const UCHAR input, SIZE_T inputSize, char outputBuffer, SIZE_T bufferSize, SIZE_T encodedSize) {
if (!input || !outputBuffer || !encodedSize)
return STATUS_INVALID_PARAMETER;

const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
SIZE_T outputNeeded = ((inputSize + 2) / 3) * 4;
if (outputNeeded + 1 > bufferSize) // +1 for null terminator if needed
return STATUS_BUFFER_TOO_SMALL;

SIZE_T i = 0, j = 0;
for (; i + 3 <= inputSize; i += 3) {
UCHAR a = input[i];
UCHAR b = input[i + 1];
UCHAR c = input[i + 2];

outputBuffer[j++] = base64_table[(a >> 2) & 0x3F];
outputBuffer[j++] = base64_table[((a & 0x3) << 4) | ((b & 0xF0) >> 4)];
outputBuffer[j++] = base64_table[((b & 0xF) << 2) | ((c & 0xC0) >> 6)];
outputBuffer[j++] = base64_table[c & 0x3F];
}

// Handle padding
if (i < inputSize) {
UCHAR a = input[i];
UCHAR b = (i + 1 < inputSize) ? input[i + 1] : 0;

outputBuffer[j++] = base64_table[(a >> 2) & 0x3F];
outputBuffer[j++] = base64_table[((a & 0x3) << 4) | ((b & 0xF0) >> 4)];
outputBuffer[j++] = (i + 1 < inputSize) ? base64_table[((b & 0xF) << 2)] : '=';
outputBuffer[j++] = '=';
}

encodedSize = j;
return STATUS_SUCCESS;
}


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


ULONG_PTR GetKernelDirBase() {
PUCHAR process = (PUCHAR)PsGetCurrentProcess();
ULONG_PTR cr3 = (PULONG_PTR)(process + 0x28); //dirbase x64, 32bit is 0x18
return cr3;

}

// Translate a virtual address to a physical address using the provided directory table base
uint64_t TranslateLinearAddress(uint64_t directoryTableBase, uint64_t virtualAddress) {
directoryTableBase &= 0xFFFFFFFFFF000ull; // 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 & 0x000FFFFFFFFFF000ull) + (virtualAddress & 0x3FFFFFFFull);

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 & 0x1FFFFFull);

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
// Callback function for WNF state changes
NTSTATUS notifCallback(
PWNF_SUBSCRIPTION Subscription,
WNF_STATE_NAME StateName,
ULONG ChangeStamp,
PVOID TypeId,
PVOID CallbackContext,
PVOID CallbackData,
ULONG CallbackDataSize
)
{
UNREFERENCED_PARAMETER(Subscription);
UNREFERENCED_PARAMETER(StateName);
UNREFERENCED_PARAMETER(TypeId);
UNREFERENCED_PARAMETER(CallbackContext);

NTSTATUS status = STATUS_SUCCESS;

// Verify that CallbackDataSize is sufficient for INSTRUCTIONS struct
if (CallbackData && CallbackDataSize >= sizeof(INSTRUCTIONS)) {
PINSTRUCTIONS instructions = (PINSTRUCTIONS)CallbackData;

if (instructions->read) {
// Handle read operation

// Validate parameters
if (instructions->processId == 0 || instructions->address == 0 || instructions->size == 0) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Invalid parameters for read operation.\n");
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Read operation failed: Invalid parameters.",
(ULONG)strlen("Read operation failed: Invalid parameters.") + 1, // Include null terminator
NULL,
NULL,
0
);
return status;
}

// Allocate memory to store read data
UCHAR* readBuffer = (UCHAR*)ExAllocatePoolWithTag(NonPagedPool, instructions->size, 'rdmK');
if (!readBuffer) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Read operation failed: Unable to allocate memory.\n");
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Read operation failed: Unable to allocate memory.",
(ULONG)strlen("Read operation failed: Unable to allocate memory.") + 1,
NULL,
NULL,
0
);
return status;
}

// Zero-initialize the buffer
RtlZeroMemory(readBuffer, instructions->size);

// Read memory from target process
SIZE_T bytesRead = 0;
status = ReadProcessMemory(
(int)(ULONG_PTR)instructions->processId,
(PVOID)(ULONG_PTR)instructions->address,
readBuffer,
instructions->size,
&bytesRead
);

if (!NT_SUCCESS(status) || bytesRead == 0) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] ReadProcessMemory failed: 0x%X (BytesRead: %zu)\n", status, bytesRead);
ExFreePoolWithTag(readBuffer, 'rdmK');
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Read operation failed.",
(ULONG)strlen("Read operation failed.") + 1,
NULL,
NULL,
0
);
return status;
}

// Encode the read data to Base64
SIZE_T base64Size = ((bytesRead + 2) / 3) * 4;
// Check if encoded data size exceeds maximum allowed (e.g., 4096 bytes)
if (base64Size + 1 > 4096) { // +1 for null terminator
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Read operation failed: Encoded data exceeds buffer size.\n");
ExFreePoolWithTag(readBuffer, 'rdmK');
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Read operation failed: Encoded data exceeds buffer size.",
(ULONG)strlen("Read operation failed: Encoded data exceeds buffer size.") + 1,
NULL,
NULL,
0
);
return status;
}

// Allocate buffer for Base64 encoded data
char* encodedData = (char*)ExAllocatePoolWithTag(NonPagedPool, base64Size + 1, 'encK'); // +1 for null terminator
if (!encodedData) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Read operation failed: Unable to allocate Base64 buffer.\n");
ExFreePoolWithTag(readBuffer, 'rdmK');
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Read operation failed: Unable to allocate Base64 buffer.",
(ULONG)strlen("Read operation failed: Unable to allocate Base64 buffer.") + 1,
NULL,
NULL,
0
);
return status;
}

// Encode to Base64
SIZE_T encodedLength = 0;
status = EncodeBase64(readBuffer, bytesRead, encodedData, base64Size + 1, &encodedLength);
ExFreePoolWithTag(readBuffer, 'rdmK'); // Free read buffer

if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] EncodeBase64 failed: 0x%X\n", status);
ExFreePoolWithTag(encodedData, 'encK');
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Read operation failed: Base64 encode failed.",
(ULONG)strlen("Read operation failed: Base64 encode failed.") + 1,
NULL,
NULL,
0
);
return status;
}

// Ensure null termination
if (encodedLength < base64Size + 1) {
encodedData[encodedLength] = '\0';
} else {
// Force null termination to prevent buffer overruns
encodedData[base64Size] = '\0';
}

// Publish the encoded data back via WNF
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
encodedData,
(ULONG)(encodedLength + 1), // Include null terminator
NULL,
NULL,
0
);

if (NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Read operation completed successfully. %zu bytes read, %zu bytes encoded.\n", bytesRead, encodedLength);
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] ExPublishWnfStateDataFunc failed: 0x%X\n", status);
}

ExFreePoolWithTag(encodedData, 'encK'); // Free encoded data buffer
}
else if (instructions->write) {
// Handle write operation

// Validate parameters
if (instructions->processId == 0 || instructions->address == 0 || instructions->size == 0 || instructions->data[0] == '\0') {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Invalid parameters for write operation.\n");
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Write operation failed: Invalid parameters.",
(ULONG)strlen("Write operation failed: Invalid parameters.") + 1,
NULL,
NULL,
0
);
return status;
}

// Decode the Base64 data
UCHAR decodedData[512];
SIZE_T decodedSize = 0;
status = DecodeBase64(instructions->data, decodedData, sizeof(decodedData), &decodedSize);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] DecodeBase64 failed: 0x%X\n", status);
// Inform user-mode about the failure
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Write operation failed: Base64 decode failed.",
(ULONG)strlen("Write operation failed: Base64 decode failed.") + 1,
NULL,
NULL,
0
);
return status;
}

// Proceed with writing memory using decodedData and decodedSize
SIZE_T bytesWritten = 0;
status = WriteProcessMemory(
(int)(ULONG_PTR)instructions->processId,
(PVOID)(ULONG_PTR)instructions->address,
decodedData,
decodedSize,
&bytesWritten
);

if (NT_SUCCESS(status) && bytesWritten == decodedSize) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Write operation completed successfully. %zu bytes written to process %lu at address 0x%llX\n", bytesWritten, instructions->processId, instructions->address);
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Write operation completed successfully.",
(ULONG)strlen("Write operation completed successfully.") + 1,
NULL,
NULL,
0
);
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] WriteProcessMemory failed: 0x%X (BytesWritten: %zu)\n", status, bytesWritten);
// Inform user-mode about the failure
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
"Write operation failed.",
(ULONG)strlen("Write operation failed.") + 1,
NULL,
NULL,
0
);
}
}
// Handle other instructions...
}
else if (CallbackData && CallbackDataSize > 0) {
// Handle messages that don't match INSTRUCTIONS struct
// For example: simple messages like "Hi"

// Allocate memory for the received message, ensuring no buffer overflows
CHAR* receivedMessage = (CHAR*)ExAllocatePoolWithTag(NonPagedPoolNx, CallbackDataSize + 1, 'msgK');
if (!receivedMessage) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to allocate memory for received message.\n");
return STATUS_INSUFFICIENT_RESOURCES;
}

// Zero-initialize the allocated memory
RtlZeroMemory(receivedMessage, CallbackDataSize + 1);

// Copy the received data, ensuring not to exceed the allocated buffer
RtlCopyMemory(receivedMessage, CallbackData, min(CallbackDataSize, (ULONG)strlen("Hi")));

DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Kernel received message: %s\n", receivedMessage);

// Check if the message is 'Hi' and respond accordingly
if (strcmp(receivedMessage, "Hi") == 0) {
// Send 'Hi' back to user-mode
CHAR replyMessage[] = "Hi";
status = ExPublishWnfStateDataFunc(
&MY_WNF_STATE_NAME,
replyMessage,
(ULONG)strlen(replyMessage) + 1, // Include null terminator
NULL,
NULL,
0
);
if (NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Kernel sent reply: %s\n", replyMessage);
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to send reply, status: 0x%08X\n", status);
}
}

// Free the allocated memory for the received message
ExFreePoolWithTag(receivedMessage, 'msgK');
}
else {
// Handle unexpected or insufficient CallbackDataSize
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, "[Driver] Received invalid or unsupported data.\n");
}

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, notifCallback, 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
NTSTATUS 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;
}