USER
explain the code fully
#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 // Replace with your unique value
// 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 (0xfull << 8) & 0xFFFFFFFFfull
// ==========================================================
// Function Pointer Types for WNF (Undocumented APIs)
// ==========================================================
typedef NTSTATUS (PExSubscribeWnfStateChange)(
PWNF_STATE_NAME StateName,
WNF_CHANGE_STAMP ChangeStamp,
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
);
// Global function pointers
PExPublishWnfStateData ExPublishWnfStateDataFunc = NULL;
// ==========================================================
// Data Structures
// ==========================================================
typedef struct _INSTRUCTIONS {
BOOLEAN close;
BOOLEAN read;
BOOLEAN reqBase;
PVOID bufferAddress;
UINT_PTR address;
ULONGLONG size;
PVOID output;
ULONG64 baseAddress;
const char moduleName;
} 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);
// ==========================================================
// 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 = (PULONG_PTR)(process + 0x28); // dirbase for x64
#else
ULONG_PTR process_dirbase = (PULONG_PTR)(process + 0x18); // dirbase for x86
#endif
if (process_dirbase == 0)
{
DWORD UserDirOffset = GetUserDirectoryTableBaseOffset();
ULONG_PTR process_userdirbase = (PULONG_PTR)(process + UserDirOffset);
return process_userdirbase;
}
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 &= 0xf;
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 & 0x80)
return (pde & 0x000FFFFFFFF000ULL) + (virtualAddress & 0x3FFFFFFFUL);
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 & 0x80)
return (pteAddr & PMASK) + (virtualAddress & 0x1FFFFFUL);
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 BytesRead = 0;
status = ReadPhysicalAddress((PVOID)paddress, buffer + CurOffset, ReadSize, &BytesRead);
if (!NT_SUCCESS(status) || BytesRead == 0)
break;
TotalSize -= BytesRead;
CurOffset += BytesRead;
}
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 process creation/termination
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 RtlQueryInformationProcess if SeLocateProcessImageName is unavailable
status = SeLocateProcessImageName(pProcess, &uProcessImageName);
if (NT_SUCCESS(status))
{
// Extract file name from full path
PWSTR processName = wcsrchr(uProcessImageName.Buffer, L'\');
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)
{
UNREFERENCED_PARAMETER(DriverObject);
// 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");
}
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 functionName;
RtlInitUnicodeString(&functionName, L"ExPublishWnfStateData");
ExPublishWnfStateDataFunc = (PExPublishWnfStateData)MmGetSystemRoutineAddress(&functionName);
if (ExPublishWnfStateDataFunc == NULL)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to resolve ExPublishWnfStateData\n");
return STATUS_UNSUCCESSFUL;
}
// Prepare your data
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(
(PCWNF_STATE_NAME)&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");
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;
}ASSISTANT
Certainly! This code is a Windows kernel-mode driver written in C that performs several advanced operations related to process monitoring, memory manipulation, and interaction with the Windows Notification Facility (WNF). Below is a comprehensive breakdown of the code, detailing its structure, components, and functionality.
---
## Table of Contents
1. [Includes and Headers](#includes-and-headers)
2. [Definitions and Macros](#definitions-and-macros)
3. [Function Pointer Types for WNF (Undocumented APIs)](#function-pointer-types-for-wnf-undocumented-apis)
4. [Global Function Pointers](#global-function-pointers)
5. [Data Structures](#data-structures)
6. [Function Declarations](#function-declarations)
7. [Function Definitions](#function-definitions)
- [Process Base Address Retrieval](#retrieve-process-base-address)
- [Directory Table Base Offset](#user-directory-table-base-offset)
- [CR3 Retrieval](#retrieve-cr3)
- [Kernel CR3 Retrieval (Example)](#example-get-the-kernels-cr3)
- [Address Translation](#translate-linear-address)
- [Physical Memory Access](#read-and-write-physical-address)
- [Virtual Memory Access](#read-and-write-virtual-memory)
- [Process Memory Read/Write](#readprocessmemory-and-writeprocessmemory)
- [Process Notification Callback](#process-notify-callback)
- [Driver Unload Routine](#driver-unload-routine)
8. [WNF Functions and DriverEntry](#wnf-functions-and-driverentry-implementation)
- [Initialize WNF Communication](#initialize-wnf-communication)
- [DriverEntry Function](#driverentry-main-function)
9. [Security and Stability Considerations](#security-and-stability-considerations)
10. [Summary](#summary)
---
## Includes and Headers
```c
#include <ntifs.h>
#include <ntddk.h>
```
- **`ntifs.h` and `ntddk.h`**: These are header files provided by the Windows Driver Kit (WDK) that include necessary definitions, structures, and functions for writing kernel-mode drivers. `ntifs.h` includes definitions for NTFS filesystem drivers, while `ntddk.h` is more general for driver development.
---
## Definitions and Macros
```c
#define TARGET_PROCESS_NAME L"notepad.exe" // Replace with your target process name
#define MY_WNF_STATE_NAME 0x41C64E6DA3BC1074ULL // Replace with your unique value
// 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 (0xfull << 8) & 0xFFFFFFFFfull
```
- **`TARGET_PROCESS_NAME`**: Specifies the target process (`notepad.exe` in this case) that the driver will monitor or interact with.
- **`MY_WNF_STATE_NAME`**: A unique identifier for WNF state. WNF is used for inter-process and system-wide notifications.
- **Windows Build Numbers**: Constants representing various Windows 10 build numbers. These are used to handle differences in OS versions within the driver.
- **Page Size Definitions**:
- **`PAGE_OFFSET_SIZE`**: Typically represents the offset within a memory page (4096 bytes for standard pages).
- **`PMASK`**: A mask used to extract the page-aligned address. However, the macro as defined `(0xfull << 8) & 0xFFFFFFFFfull` seems incorrect and likely intended to extract lower bits; it may require correction.
---
## Function Pointer Types for WNF (Undocumented APIs)
```c
typedef NTSTATUS (PExSubscribeWnfStateChange)(
PWNF_STATE_NAME StateName,
WNF_CHANGE_STAMP ChangeStamp,
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
);
```
- **Function Typedefs**: These define the signatures for two undocumented Windows kernel functions related to WNF:
- **`ExSubscribeWnfStateChange`**: Subscribes to changes in a specific WNF state.
- **`ExPublishWnfStateData`**: Publishes data to a specific WNF state.
---
## Global Function Pointers
```c
PExPublishWnfStateData ExPublishWnfStateDataFunc = NULL;
```
- **`ExPublishWnfStateDataFunc`**: A global function pointer to the `ExPublishWnfStateData` function. It is initialized to `NULL` and later resolved dynamically using `MmGetSystemRoutineAddress`.
---
## Data Structures
```c
typedef struct _INSTRUCTIONS {
BOOLEAN close;
BOOLEAN read;
BOOLEAN reqBase;
PVOID bufferAddress;
UINT_PTR address;
ULONGLONG size;
PVOID output;
ULONG64 baseAddress;
const char moduleName;
} INSTRUCTIONS, PINSTRUCTIONS;
```
- **`INSTRUCTIONS` Structure**: Defines a data structure likely used for passing instructions or data related to memory operations. Fields include:
- **Flags**: `close`, `read`, `reqBase` indicate various operations.
- **Addresses and Sizes**: `bufferAddress`, `address`, `size`, `output`, `baseAddress` for memory operations.
- **`moduleName`**: Holds the name of a module, possibly for identifying or interacting with specific process modules.
---
## Function Declarations
These declarations provide prototypes for functions used throughout the driver. Brief descriptions are provided for each:
```c
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);
```
- **`PsGetProcessSectionBaseAddress`**: Retrieves the base address of a process’s image section.
- **Memory Manipulation Functions**:
- **`GetProcessBaseAddress`**: Retrieves the base address of a process given its PID.
- **`GetUserDirectoryTableBaseOffset`**: Returns the offset for the `UserDirectoryTableBase` based on the OS version.
- **`GetProcessCr3`**: Retrieves the CR3 (Page Directory Base Register) value for a given process.
- **`GetKernelDirBase`**: Example function to get the kernel's CR3.
- **`ReadVirtual` and `WriteVirtual`**: Read from and write to virtual memory using a directory table base.
- **`ReadPhysicalAddress` and `WritePhysicalAddress`**: Read from and write to physical memory addresses.
- **`TranslateLinearAddress`**: Translates a virtual address to a physical address using the CR3 value.
- **`ReadProcessMemory` and `WriteProcessMemory`**: High-level functions to read from and write to a process's memory by PID.
- **Process Notification and Driver Management**:
- **`ProcessNotifyCallback`**: Callback function invoked on process creation or termination.
- **`DriverUnload`**: Routine called when the driver is unloaded.
---
## Function Definitions
### Retrieve Process Base Address
```c
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;
}
```
- **Purpose**: Retrieves the base address of a process given its PID.
- **Process**:
1. **`PsLookupProcessByProcessId`**: Obtains a pointer to the `PEPROCESS` structure corresponding to the given PID.
2. **`PsGetProcessSectionBaseAddress`**: Gets the base address of the process's image section.
3. **`ObDereferenceObject`**: Decrements the reference count on the `PEPROCESS` object to avoid memory leaks.
### User Directory Table Base Offset
```c
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;
}
}
```
- **Purpose**: Determines the offset of the `UserDirectoryTableBase` within the `PEPROCESS` structure based on the Windows version.
- **Process**:
1. **`RtlGetVersion`**: Retrieves the current OS version.
2. **Switch Statement**: Returns specific offsets for known Windows builds. The default case handles newer or unlisted versions.
### Retrieve CR3
```c
ULONG_PTR GetProcessCr3(PEPROCESS pProcess)
{
PUCHAR process = (PUCHAR)pProcess;
#ifdef _WIN64
ULONG_PTR process_dirbase = (PULONG_PTR)(process + 0x28); // dirbase for x64
#else
ULONG_PTR process_dirbase = (PULONG_PTR)(process + 0x18); // dirbase for x86
#endif
if (process_dirbase == 0)
{
DWORD UserDirOffset = GetUserDirectoryTableBaseOffset();
ULONG_PTR process_userdirbase = (PULONG_PTR)(process + UserDirOffset);
return process_userdirbase;
}
return process_dirbase;
}
```
- **Purpose**: Retrieves the CR3 (Page Directory Base Register) value from a `PEPROCESS` structure.
- **Process**:
1. **Platform Check**: Differentiates between 64-bit and 32-bit platforms to calculate the correct offset.
2. **Offset Calculation**: Adds the appropriate offset to the `PEPROCESS` pointer to access the CR3 value.
3. **Fallback**: If the initial `process_dirbase` is zero, it uses `GetUserDirectoryTableBaseOffset` to compute an alternative offset.
### Example Get the Kernel's CR3
```c
ULONG_PTR GetKernelDirBase()
{
// Note: Accessing the kernel's CR3 is generally unsafe and not recommended
// This is just for illustrative purposes
return __readcr3();
}
```
- **Purpose**: Retrieves the CR3 value of the kernel.
- **Note**: Accessing the kernel's CR3 directly is typically unsafe and can lead to system instability. This function is provided for illustrative purposes.
### Translate Linear Address
```c
uint64_t TranslateLinearAddress(uint64_t directoryTableBase, uint64_t virtualAddress) {
directoryTableBase &= 0xf;
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 & 0x80)
return (pde & 0x000FFFFFFFF000ULL) + (virtualAddress & 0x3FFFFFFFUL);
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 & 0x80)
return (pteAddr & PMASK) + (virtualAddress & 0x1FFFFFUL);
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;
}
```
- **Purpose**: Translates a virtual address to a physical address using the provided Directory Table Base (`CR3` value).
- **Process**:
1. **Page Tables Breakdown**:
- **Page Directory Pointer Table (PDP) Entry**: Bits 39-47
- **Page Directory (PD) Entry**: Bits 30-38
- **Page Table (PT) Entry**: Bits 21-29
- **Page Table Entry (PTE)**: Bits 12-20
- **Offset**: Bits 0-11
2. **Read Physical Addresses**: Uses `ReadPhysicalAddress` to traverse the multi-level page tables:
- Reads PDP entry.
- Reads PD entry.
- Checks for large pages (1GB or 2MB) and calculates the physical address accordingly.
- Finally, reads the PTE to get the physical page number.
3. **Combine Physical Page and Offset**: Adds the page offset to obtain the full physical address.
- **Error Handling**: If any read operation fails or invalid entries are detected (e.g., not present), the function returns `0`.
### Read and Write Physical Address
```c
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;
}
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;
}
```
- **`ReadPhysicalAddress`**:
- **Purpose**: Reads data from a specified physical memory address into a buffer.
- **Method**: Uses `MmCopyMemory` with the `MM_COPY_MEMORY_PHYSICAL` flag to perform the copy.
- **Parameters**:
- **`TargetAddress`**: Physical address to read from.
- **`lpBuffer`**: Buffer to store the read data.
- **`Size`**: Number of bytes to read.
- **`BytesRead`**: Number of bytes actually read.
- **`WritePhysicalAddress`**:
- **Purpose**: Writes data from a buffer to a specified physical memory address.
- **Method**:
1. **`MmMapIoSpaceEx`**: Maps the physical address into the virtual address space with read-write permissions.
2. **`RtlCopyMemory`**: Copies data from the buffer to the mapped memory.
3. **`MmUnmapIoSpace`**: Unmaps the memory after the write operation.
- **Security Note**: Direct physical memory access can lead to system instability and security vulnerabilities. This function should be used with extreme caution.
### Read and Write Virtual Memory
```c
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 BytesRead = 0;
status = ReadPhysicalAddress((PVOID)paddress, buffer + CurOffset, ReadSize, &BytesRead);
if (!NT_SUCCESS(status) || BytesRead == 0)
break;
TotalSize -= BytesRead;
CurOffset += BytesRead;
}
*read = CurOffset;
return status;
}
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;
}
```
- **`ReadVirtual`**:
- **Purpose**: Reads data from a virtual memory address within a process's address space by translating it to a physical address.
- **Parameters**:
- **`dirbase`**: CR3 value of the target process.
- **`address`**: Virtual address to read from.
- **`buffer`**: Buffer to store read data.
- **`size`**: Number of bytes to read.
- **`read`**: Number of bytes successfully read.
- **Process**:
1. **Address Translation**: Converts virtual addresses to physical addresses using `TranslateLinearAddress`.
2. **Page-wise Reading**: Reads data page by page to handle page boundaries and partial reads.
3. **Error Handling**: Stops reading if any translation or read operation fails.
- **`WriteVirtual`**:
- **Purpose**: Writes data to a virtual memory address within a process's address space by translating it to a physical address.
- **Parameters**:
- Similar to `ReadVirtual` but for writing data.
- **Process**:
1. **Address Translation**: Converts virtual addresses to physical addresses.
2. **Page-wise Writing**: Writes data page by page.
3. **Error Handling**: Stops writing if any translation or write operation fails.
**Security Note**: Manipulating another process's memory at the physical address level can bypass standard security mechanisms and can lead to system instability or security breaches. Use with extreme caution.
### ReadProcessMemory and WriteProcessMemory
```c
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;
}
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;
}
```
- **`ReadProcessMemory`**:
- **Purpose**: Reads memory from another process using its PID.
- **Process**:
1. Validates parameters.
2. Retrieves the `PEPROCESS` structure for the given PID.
3. Gets the process's CR3 value using `GetProcessCr3`.
4. Calls `ReadVirtual` to perform the read operation.
- **`WriteProcessMemory`**:
- **Purpose**: Writes memory to another process using its PID.
- **Process**:
Similar to `ReadProcessMemory`, but performs a write operation using `WriteVirtual`.
**Security Note**: These functions allow arbitrary memory reading and writing to other processes, which can be exploited for malicious purposes. Ensure proper access controls and usage restrictions are in place.
### Process Notify Callback
```c
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 RtlQueryInformationProcess if SeLocateProcessImageName is unavailable
status = SeLocateProcessImageName(pProcess, &uProcessImageName);
if (NT_SUCCESS(status))
{
// Extract file name from full path
PWSTR processName = wcsrchr(uProcessImageName.Buffer, L'\\');
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);
}
}
}
```
- **Purpose**: Callback function triggered on process creation (`Create = TRUE`) or termination (`Create = FALSE`).
- **Process on Process Creation**:
1. **Retrieve `PEPROCESS`**: Gets the `PEPROCESS` structure for the newly created process.
2. **Get Process Image Name**: Uses `SeLocateProcessImageName` to obtain the full image name (`UNICODE_STRING`).
3. **Extract Process Name**: Parses the image name to get the executable name (e.g., `notepad.exe`).
4. **Target Process Check**: Compares the extracted name with `TARGET_PROCESS_NAME`.
5. **Memory Operations**:
- **Read Operation**: Attempts to read the first 256 bytes from the process's base address.
- **Write Operation**: Placeholder code (commented out) for writing to the process's memory.
6. **Logging**: Uses `DbgPrintEx` to log success or failure of operations.
7. **Cleanup**: Frees allocated memory and dereferences objects to prevent leaks.
- **Notes**:
- **Security and Stability**: Interacting with another process's memory can be risky. Ensure that proper permissions and error handling are in place.
- **Uncommented Code**: The write operation code is commented out, indicating it’s provided as an example and should be used carefully.
### Driver Unload Routine
```c
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
// 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");
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[Driver] Driver unloaded\n");
}
```
- **Purpose**: Called when the driver is being unloaded.
- **Process**:
1. **Remove Process Notify Callback**: Calls `PsSetCreateProcessNotifyRoutine` with the `TRUE` parameter to unregister the `ProcessNotifyCallback`.
2. **Logging**: Logs whether the removal was successful.
3. **Final Log**: Indicates the driver has been unloaded.
---
## WNF Functions and DriverEntry Implementation
### Initialize WNF Communication
```c
NTSTATUS InitializeWNF()
{
// Dynamically resolve the ExPublishWnfStateData function
UNICODE_STRING functionName;
RtlInitUnicodeString(&functionName, L"ExPublishWnfStateData");
ExPublishWnfStateDataFunc = (PExPublishWnfStateData)MmGetSystemRoutineAddress(&functionName);
if (ExPublishWnfStateDataFunc == NULL)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Driver] Failed to resolve ExPublishWnfStateData\n");
return STATUS_UNSUCCESSFUL;
}
// Prepare your data
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(
(PCWNF_STATE_NAME)&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");
return STATUS_SUCCESS;
}
```
- **Purpose**: Initializes communication with WNF by publishing a custom state.
- **Process**:
1. **Dynamic Function Resolution**:
- **`RtlInitUnicodeString`**: Initializes a `UNICODE_STRING` with the function name `"ExPublishWnfStateData"`.
- **`MmGetSystemRoutineAddress`**: Retrieves the address of `ExPublishWnfStateData` dynamically.
2. **Error Handling**: Logs and returns an error if the function can't be resolved.
3. **Prepare Data**: Initializes an `INSTRUCTIONS` structure with example data.
4. **Publish WNF State**:
- Calls `ExPublishWnfStateDataFunc` to publish the `instructions` data to the specified WNF state.
5. **Logging**: Logs success or failure of the publish operation.
**Security Note**: Interacting with WNF using undocumented APIs can be unstable across different Windows versions and updates. Ensure that the `MY_WNF_STATE_NAME` is unique to avoid conflicts.
### DriverEntry Function
```c
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;
}
```
- **Purpose**: Entry point for the driver. Executes when the driver is loaded.
- **Process**:
1. **Process Notify Callback Registration**:
- Calls `PsSetCreateProcessNotifyRoutine` to register `ProcessNotifyCallback`.
- Registers for process creation and termination notifications.
2. **Error Handling**: If the registration fails, logs the error and exits.
3. **Initialize WNF Communication**:
- Calls `InitializeWNF`.
- If WNF initialization fails, unregisters the process notify callback and unloads the driver.
4. **Set Unload Routine**:
- Assigns `DriverUnload` to `DriverObject->DriverUnload` to handle driver unloading.
5. **Logging**: Logs the successful loading of the driver.
---
## Security and Stability Considerations
This driver performs several highly privileged operations, such as:
1. **Process Monitoring**: It monitors process creation and termination events.
2. **Memory Manipulation**:
- **Physical Memory Access**: Reads and writes to physical memory, which can bypass standard access controls.
- **Virtual Memory Access**: Translates virtual addresses to physical addresses and reads/writes process memory.
3. **Undocumented API Usage**: Uses undocumented Windows APIs (e.g., WNF functions), which can change across Windows updates and lead to instability.
4. **Risks**:
- **System Instability**: Incorrect memory access can crash the system.
- **Security Vulnerabilities**: Arbitrary memory access can be exploited for privilege escalation or other malicious activities.
- **Driver Crashes**: Bugs within the driver can lead to BSODs (Blue Screens of Death).
5. **Best Practices**:
- **Minimal Privilege**: Only perform necessary operations with the least privileges required.
- **Robust Error Handling**: Ensure all operations check for success and handle failures gracefully.
- **Testing**: Thoroughly test in controlled environments before deploying.
- **Code Signing**: Ensure the driver is properly signed to be loaded on Windows systems with driver signature enforcement.
---
## Summary
This kernel-mode driver performs the following key functions:
1. **Monitoring Processes**:
- Registers a callback to be notified of process creation and termination.
- Specifically targets a process named `notepad.exe` (modifiable via `TARGET_PROCESS_NAME`).
2. **Interacting with WNF**:
- Dynamically resolves and uses an undocumented WNF function to publish state data.
- Prepares and publishes an `INSTRUCTIONS` structure to a custom WNF state.
3. **Memory Manipulation**:
- Provides functions to translate virtual addresses to physical addresses.
- Reads and writes to both virtual and physical memory of processes.
- Uses these capabilities to read (and potentially write) memory of the target process upon its creation.
4. **Driver Lifecycle Management**:
- Implements `DriverEntry` to initialize functionality upon loading.
- Defines `DriverUnload` to clean up resources and unregister callbacks when unloading.
5. **Logging and Debugging**:
- Utilizes `DbgPrintEx` for logging informational and error messages, aiding in debugging and monitoring driver behavior.
**Overall**, this driver encapsulates complex mechanisms for process monitoring and memory manipulation, leveraging both documented and undocumented Windows kernel functionalities. It's essential to handle such drivers with care, given their high level of access and potential impact on system stability and security.
---
**Disclaimer**: Developing and deploying kernel-mode drivers requires deep knowledge of Windows internals and driver development best practices. Improper handling can lead to system instability, security vulnerabilities, and data loss. Always ensure thorough testing and adhere to security guidelines when working with such drivers.