#include <ntifs.h>
#include <ntddk.h>

// ==========================================================
// Definitions and Macros
// ==========================================================
#define WINDOWS_1803 17134
#define WINDOWS_1809 17763
#define WINDOWS_1903 18362
#define WINDOWS_1909 18363
#define WINDOWS_2004 19041
#define WINDOWS_20H2 19042
#define WINDOWS_21H1 19043
#define WINDOWS_22H2 19044

#define PAGE_OFFSET_SIZE 12
#define PAGE_SIZE 0x1000
#define PMASK (0xFFFULL)

typedef unsigned __int64 QWORD;

// ==========================================================
// Function Declarations
// ==========================================================
NTSTATUS ReadPhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T* BytesRead);
NTSTATUS WritePhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T* BytesWritten);
DWORD GetUserDirectoryTableBaseOffset();
ULONG_PTR GetProcessCr3(PEPROCESS pProcess);

uint64_t TranslateLinearAddress(uint64_t directoryTableBase, uint64_t virtualAddress);

// Memory operations
NTSTATUS ReadVirtualMemory(ULONG_PTR directoryTableBase, ULONG_PTR virtualAddress, PVOID buffer, SIZE_T size, SIZE_T* bytesRead);
NTSTATUS WriteVirtualMemory(ULONG_PTR directoryTableBase, ULONG_PTR virtualAddress, PVOID buffer, SIZE_T size, SIZE_T* bytesWritten);

// Process memory operations
NTSTATUS ReadProcessMemory(ULONG_PTR pid, PVOID Address, PVOID Buffer, SIZE_T Size, SIZE_T* BytesRead);
NTSTATUS WriteProcessMemory(ULONG_PTR pid, PVOID Address, PVOID Buffer, SIZE_T Size, SIZE_T* BytesWritten);

// WNF communication
typedef NTSTATUS(EXSUBSCRIBEWNFSTATECHANGE)(
PVOID Subscription,
ULONG64 StateName,
ULONG ChangeStamp,
PVOID Callback,
PVOID CallbackContext,
PVOID TypeId,
ULONG SerializationGroup,
ULONG Unknown
);

typedef NTSTATUS(*EXPUBLISHWNFSTATEDATA)(
ULONG64 StateName,
PVOID Buffer,
ULONG Length,
PVOID TypeId,
PVOID ExplicitScope,
ULONG MatchingChangeStamp
);

typedef NTSTATUS(EXUNSUBSCRIBEWNFSTATECHANGE)(
PVOID Subscription
);

EXSUBSCRIBEWNFSTATECHANGE ExSubscribeWnfStateChange = NULL;
EXPUBLISHWNFSTATEDATA ExPublishWnfStateData = NULL;
EXUNSUBSCRIBEWNFSTATECHANGE ExUnsubscribeWnfStateChange = NULL;

ULONG64 MY_WNF_STATE_NAME = 0x41C64E6DA3BC1074; // Replace with your WNF State Name

PVOID g_WnfSubscription = NULL;

// ==========================================================
// Structures
// ==========================================================
#pragma pack(push, 1)
typedef struct _INSTRUCTIONS {
BYTE close;
BYTE read;
BYTE write;
BYTE reqBase;
UINT64 processId;
UINT64 address;
UINT64 size;
PVOID bufferAddress;
PVOID output;
UINT64 baseAddress;
char moduleName[256];
char data[1024]; // For data to write or for returning data read
} INSTRUCTIONS, PINSTRUCTIONS;
#pragma pack(pop)

// ==========================================================
// Function Definitions
// ==========================================================
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:
return 0x0280;
case WINDOWS_2004:
case WINDOWS_20H2:
case WINDOWS_21H1:
case WINDOWS_22H2:
default:
return 0x0388;
}
}

NTSTATUS ReadPhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T BytesRead)
{
MM_COPY_ADDRESS AddrToRead;
AddrToRead.PhysicalAddress.QuadPart = (LONGLONG)TargetAddress;
return MmCopyMemory(lpBuffer, AddrToRead, Size, MM_COPY_MEMORY_PHYSICAL, BytesRead);
}

NTSTATUS WritePhysicalAddress(PVOID TargetAddress, PVOID lpBuffer, SIZE_T Size, SIZE_T BytesWritten)
{
if (!TargetAddress)
return STATUS_UNSUCCESSFUL;

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

PVOID pmapped_mem = MmMapIoSpaceEx(AddrToWrite, Size, PAGE_READWRITE);

if (!pmapped_mem)
return STATUS_UNSUCCESSFUL;

memcpy(pmapped_mem, lpBuffer, Size);

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

ULONG_PTR GetProcessCr3(PEPROCESS pProcess)
{
PUCHAR process = (PUCHAR)pProcess;
#ifdef _WIN64
ULONG_PTR process_dirbase = (ULONG_PTR)(process + 0x28); // dirbase for x64
#else
ULONG_PTR process_dirbase = (ULONG_PTR)(process + 0x18); // dirbase for x86
#endif
if (process_dirbase == 0)
{
DWORD UserDirOffset = GetUserDirectoryTableBaseOffset();
ULONG_PTR process_userdirbase = (ULONG_PTR)(process + UserDirOffset);
return process_userdirbase;
}
return process_dirbase;
}

uint64_t TranslateLinearAddress(uint64_t directoryTableBase, uint64_t virtualAddress)
{
directoryTableBase &= ~0xf;

uint64_t pageOffset = virtualAddress & 0xFFFULL;
uint64_t pteIndex = (virtualAddress >> 12) & 0x1FFULL;
uint64_t ptIndex = (virtualAddress >> 21) & 0x1FFULL;
uint64_t pdIndex = (virtualAddress >> 30) & 0x1FFULL;
uint64_t pdpIndex = (virtualAddress >> 39) & 0x1FFULL;

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

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

// 1GB large page
if (pde & 0x80)
return (pde & 0xFFFFFC0000000ULL) + (virtualAddress & 0x3FFFFFFFULL);

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

// 2MB large page
if (pteAddr & 0x80)
return (pteAddr & PMASK) + (virtualAddress & 0x1FFFFFULL);

uint64_t pageTableEntry = 0;
if (ReadPhysicalAddress((PVOID)((pteAddr & PMASK) + 8 * pteIndex), &pageTableEntry, sizeof(pageTableEntry), &readsize) != STATUS_SUCCESS)
return 0;
if (~pageTableEntry & 1)
return 0;

uint64_t physAddress = (pageTableEntry & PMASK) + pageOffset;
return physAddress;
}

NTSTATUS ReadVirtualMemory(ULONG_PTR directoryTableBase, ULONG_PTR virtualAddress, PVOID buffer, SIZE_T size, SIZE_T* bytesRead)
{
SIZE_T totalBytesRead = 0;
SIZE_T bytesToRead = size;
NTSTATUS status = STATUS_SUCCESS;
while (bytesToRead)
{
uint64_t physAddress = TranslateLinearAddress(directoryTableBase, virtualAddress);
if (!physAddress)
return STATUS_UNSUCCESSFUL;

ULONG64 readSize = min(PAGE_SIZE - (physAddress & 0xFFFULL), bytesToRead);
SIZE_T localBytesRead = 0;
status = ReadPhysicalAddress((PVOID)physAddress, (PVOID)((ULONG_PTR)buffer + totalBytesRead), readSize, &localBytesRead);
if (status != STATUS_SUCCESS)
break;
if (localBytesRead == 0)
break;

bytesToRead -= localBytesRead;
virtualAddress += localBytesRead;
totalBytesRead += localBytesRead;
}
if (bytesRead)
bytesRead = totalBytesRead;
return status;
}

NTSTATUS WriteVirtualMemory(ULONG_PTR directoryTableBase, ULONG_PTR virtualAddress, PVOID buffer, SIZE_T size, SIZE_T bytesWritten)
{
SIZE_T totalBytesWritten = 0;
SIZE_T bytesToWrite = size;
NTSTATUS status = STATUS_SUCCESS;
while (bytesToWrite)
{
uint64_t physAddress = TranslateLinearAddress(directoryTableBase, virtualAddress);
if (!physAddress)
return STATUS_UNSUCCESSFUL;

ULONG64 writeSize = min(PAGE_SIZE - (physAddress & 0xFFFULL), bytesToWrite);
SIZE_T localBytesWritten = 0;
status = WritePhysicalAddress((PVOID)physAddress, (PVOID)((ULONG_PTR)buffer + totalBytesWritten), writeSize, &localBytesWritten);
if (status != STATUS_SUCCESS)
break;
if (localBytesWritten == 0)
break;

bytesToWrite -= localBytesWritten;
virtualAddress += localBytesWritten;
totalBytesWritten += localBytesWritten;
}
if (bytesWritten)
bytesWritten = totalBytesWritten;
return status;
}

NTSTATUS ReadProcessMemory(ULONG_PTR pid, PVOID Address, PVOID Buffer, SIZE_T Size, SIZE_T BytesRead)
{
PEPROCESS pProcess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)pid, &pProcess);
if (!NT_SUCCESS(status))
return status;

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

return ReadVirtualMemory(dirBase, (ULONG_PTR)Address, Buffer, Size, BytesRead);
}

NTSTATUS WriteProcessMemory(ULONG_PTR pid, PVOID Address, PVOID Buffer, SIZE_T Size, SIZE_T* BytesWritten)
{
PEPROCESS pProcess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)pid, &pProcess);
if (!NT_SUCCESS(status))
return status;

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

return WriteVirtualMemory(dirBase, (ULONG_PTR)Address, Buffer, Size, BytesWritten);
}

// WNF Callback function
NTSTATUS OnWnfCallback(
PVOID Subscription,
ULONG64 StateName,
ULONG ChangeStamp,
PVOID TypeId,
PVOID CallbackContext,
PVOID Buffer,
ULONG BufferSize
)
{
UNREFERENCED_PARAMETER(Subscription);
UNREFERENCED_PARAMETER(StateName);
UNREFERENCED_PARAMETER(ChangeStamp);
UNREFERENCED_PARAMETER(TypeId);
UNREFERENCED_PARAMETER(CallbackContext);

if (Buffer && BufferSize >= sizeof(INSTRUCTIONS))
{
PINSTRUCTIONS instructions = (PINSTRUCTIONS)Buffer;
if (instructions->read)
{
// Handle read request
SIZE_T bytesRead = 0;
PUCHAR readBuffer = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, instructions->size, 'rdrd');
if (!readBuffer)
return STATUS_INSUFFICIENT_RESOURCES;

NTSTATUS status = ReadProcessMemory(
instructions->processId,
(PVOID)instructions->address,
readBuffer,
(SIZE_T)instructions->size,
&bytesRead
);
if (NT_SUCCESS(status))
{
// Copy the data back to user-mode via WNF
RtlZeroMemory(instructions->data, sizeof(instructions->data));
RtlCopyMemory(instructions->data, readBuffer, min(bytesRead, sizeof(instructions->data)));
instructions->size = bytesRead;
// Publish the data back
ExPublishWnfStateData(MY_WNF_STATE_NAME, instructions, sizeof(INSTRUCTIONS), NULL, NULL, 0);
}
else
{
// Handle read error
}
ExFreePoolWithTag(readBuffer, 'rdrd');
}
else if (instructions->write)
{
// Handle write request
SIZE_T bytesWritten = 0;
NTSTATUS status = WriteProcessMemory(
instructions->processId,
(PVOID)instructions->address,
instructions->data,
(SIZE_T)instructions->size,
&bytesWritten
);
if (NT_SUCCESS(status))
{
instructions->size = bytesWritten;
// Notify user-mode of success
ExPublishWnfStateData(MY_WNF_STATE_NAME, instructions, sizeof(INSTRUCTIONS), NULL, NULL, 0);
}
else
{
// Handle write error
}
}
else if (instructions->close)
{
// Handle close instruction
// Perform any necessary cleanup
}
else if (instructions->reqBase)
{
// Handle base address request
PEPROCESS pProcess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)instructions->processId, &pProcess);
if (NT_SUCCESS(status))
{
instructions->baseAddress = (UINT64)PsGetProcessSectionBaseAddress(pProcess);
ObDereferenceObject(pProcess);
// Send back the base address
ExPublishWnfStateData(MY_WNF_STATE_NAME, instructions, sizeof(INSTRUCTIONS), NULL, NULL, 0);
}
else
{
// Handle error
}
}
// Handle other instructions as needed
}
else if (Buffer && BufferSize > 0)
{
// Handle simple messages like "Hi"
const char* receivedMessage = (const char*)Buffer;
if (strcmp(receivedMessage, "Hi") == 0)
{
// Send handshake response
const char* responseMessage = "Hello from kernel mode";
ExPublishWnfStateData(MY_WNF_STATE_NAME, (PVOID)responseMessage, (ULONG)strlen(responseMessage) + 1, NULL, NULL, 0);
}
}

return STATUS_SUCCESS;
}

// Driver unload function
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);

// Unsubscribe from WNF
if (g_WnfSubscription && ExUnsubscribeWnfStateChange)
{
ExUnsubscribeWnfStateChange(g_WnfSubscription);
g_WnfSubscription = NULL;
}
}

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

// Resolve WNF functions
UNICODE_STRING routineName;
RtlInitUnicodeString(&routineName, L"ExSubscribeWnfStateChange");
ExSubscribeWnfStateChange = (EXSUBSCRIBEWNFSTATECHANGE)MmGetSystemRoutineAddress(&routineName);
if (!ExSubscribeWnfStateChange)
return STATUS_UNSUCCESSFUL;

RtlInitUnicodeString(&routineName, L"ExPublishWnfStateData");
ExPublishWnfStateData = (EXPUBLISHWNFSTATEDATA)MmGetSystemRoutineAddress(&routineName);
if (!ExPublishWnfStateData)
return STATUS_UNSUCCESSFUL;

RtlInitUnicodeString(&routineName, L"ExUnsubscribeWnfStateChange");
ExUnsubscribeWnfStateChange = (EXUNSUBSCRIBEWNFSTATECHANGE)MmGetSystemRoutineAddress(&routineName);
if (!ExUnsubscribeWnfStateChange)
return STATUS_UNSUCCESSFUL;

// Subscribe to WNF state changes
NTSTATUS status = ExSubscribeWnfStateChange(
&g_WnfSubscription,
MY_WNF_STATE_NAME,
0,
(PVOID)OnWnfCallback,
NULL,
NULL,
0,
0
);
if (!NT_SUCCESS(status))
return status;

DriverObject->DriverUnload = DriverUnload;

// Send handshake message to user-mode
const char* handshakeMessage = "Kernel driver loaded";
ExPublishWnfStateData(MY_WNF_STATE_NAME, (PVOID)handshakeMessage, (ULONG)strlen(handshakeMessage) + 1, NULL, NULL, 0);

return STATUS_SUCCESS;
}

in one code block i want u to finish the read and write process memory in my driver

using System;
using NtApiDotNet;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;

namespace WnfSubscriber
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct INSTRUCTIONS
{
public byte close; // 1 byte
public byte read; // 1 byte
public byte write; // 1 byte
public byte reqBase; // 1 byte
public ulong processId; // 8 bytes (matches UINT_PTR in C code)
public ulong address; // 8 bytes
public ulong size; // 8 bytes
public UIntPtr bufferAddress; // 8 bytes (unused, can be set to zero)
public UIntPtr output; // 8 bytes (unused, can be set to zero)
public ulong baseAddress; // 8 bytes

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

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
public byte[] data; // Data to write or read
}


public class KernelCom : IDisposable
{
private const ulong MY_WNF_STATE_NAME = 0x41C64E6DA3BC1074;
private readonly WnfClient _client;
private bool _initialized = false;

public KernelCom()
{
_client = new WnfClient();
}

public void Initialize()
{
if (_initialized)
return;

_client.SubscribeState(new WnfStateName(MY_WNF_STATE_NAME), OnWnfStateChange);
Console.WriteLine("[+] Subscribed to WNF state.");
_initialized = true;
}

public void SendMessage(string message)
{
byte[] dataToSend = Encoding.ASCII.GetBytes(message);
NtWnf.PublishStateData(new WnfStateName(MY_WNF_STATE_NAME), dataToSend);
Console.WriteLine("[+] Sent message to kernel-mode: " + message);
}

public void SendInstructions(INSTRUCTIONS instructions)
{
int size = Marshal.SizeOf(typeof(INSTRUCTIONS));
byte[] dataToSend = new byte[size];

IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(instructions, ptr, false);
Marshal.Copy(ptr, dataToSend, 0, size);
NtWnf.PublishStateData(new WnfStateName(MY_WNF_STATE_NAME), dataToSend);
Console.WriteLine("[+] Sent instructions to kernel-mode.");
}
catch (Exception ex)
{
Console.WriteLine($"[!] Failed to send instructions: {ex.Message}");
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}

// New Method: ReadMemory
public void ReadMemory(uint processId, ulong address, ulong size)
{
INSTRUCTIONS instructions = new INSTRUCTIONS
{
close = 0,
read = 1,
write = 0,
reqBase = 0,
processId = processId,
address = address,
size = size,
bufferAddress = UIntPtr.Zero,
output = UIntPtr.Zero,
baseAddress = 0,
moduleName = string.Empty,
data = new byte[1024]
};

SendInstructions(instructions);
Console.WriteLine($"[+] Requested to read {size} bytes from process {processId} at address 0x{address:X}.");
}

// New Method: WriteMemory
public void WriteMemory(uint processId, ulong address, byte[] data)
{
if (data.Length > 1024) // Adjust the limit as per your requirements and kernel driver capability
{
Console.WriteLine("[!] Data size exceeds the maximum allowed (1024 bytes).");
return;
}

INSTRUCTIONS instructions = new INSTRUCTIONS
{
close = 0,
read = 0,
write = 1,
reqBase = 0,
processId = processId,
address = address,
size = (ulong)data.Length,
bufferAddress = UIntPtr.Zero,
output = UIntPtr.Zero,
baseAddress = 0,
moduleName = string.Empty,
data = new byte[1024]
};

Array.Copy(data, instructions.data, data.Length);

SendInstructions(instructions);
Console.WriteLine($"[+] Requested to write {data.Length} bytes to process {processId} at address 0x{address:X}.");
}

private void OnWnfStateChange(WnfStateChange change)
{
Console.WriteLine("[] WNF State Changed!");

if (change.Data == null || change.Data.Length == 0)
{
Console.WriteLine("[!] Received empty data.");
return;
}

// Attempt to parse the data as INSTRUCTIONS struct
try
{
INSTRUCTIONS response = ByteArrayToStructure<INSTRUCTIONS>(change.Data);

if (response.read == 1)
{
Console.WriteLine($"[] Read Memory Response from process {response.processId}:");

if (response.size == 0)
{
Console.WriteLine("[!] No data received for read operation.");
return;
}

// Extract the data from the response
byte[] readData = new byte[response.size];
Array.Copy(response.data, readData, (int)response.size); // Ensure casting to int is safe

Console.WriteLine("[] Data: " + BitConverter.ToString(readData));
}
else if (response.write == 1)
{
Console.WriteLine("[] Write Memory operation completed successfully.");
}
else if (response.close == 1)
{
Console.WriteLine("[] Received instruction to close the driver.");
// Perform any cleanup if necessary
}
else if (response.reqBase == 1)
{
Console.WriteLine($"[] Base Address for module '{response.moduleName}': 0x{response.baseAddress:X}");
}
else
{
string receivedMessage = Encoding.ASCII.GetString(change.Data).TrimEnd('\0');
Console.WriteLine("[] Received message from kernel-mode: " + receivedMessage);

// Handle other messages or results
}
}
catch (Exception)
{
// If parsing as INSTRUCTIONS struct fails, treat it as a simple string message
string receivedMessage = Encoding.ASCII.GetString(change.Data).TrimEnd('\0');
Console.WriteLine("[] Received message from kernel-mode: " + receivedMessage);
}
}

private T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
int size = Marshal.SizeOf(typeof(T));
if (bytes.Length < size)
throw new ArgumentException("Byte array is too small for the specified structure.");

IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, ptr, size);
return Marshal.PtrToStructure<T>(ptr);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}

public void CloseDriver()
{
INSTRUCTIONS instructions = new INSTRUCTIONS
{
close = 1,
read = 0,
write = 0,
reqBase = 0,
processId = 0,
address = 0,
size = 0,
bufferAddress = UIntPtr.Zero,
output = UIntPtr.Zero,
baseAddress = 0,
moduleName = string.Empty,
data = new byte[1024]
};

SendInstructions(instructions);
}

public void GetModuleBaseAddress(string moduleName)
{
INSTRUCTIONS instructions = new INSTRUCTIONS
{
close = 0,
read = 0,
write = 0,
reqBase = 1,
processId = 0,
address = 0,
size = 0,
bufferAddress = UIntPtr.Zero,
output = UIntPtr.Zero,
baseAddress = 0,
moduleName = moduleName,
data = new byte[1024]
};

SendInstructions(instructions);
}

public void Dispose()
{
_client.Dispose();
}
}

class Program
{
static void Main(string[] args)
{
using (KernelCom kernelCom = new KernelCom())
{
// Initialize WNF subscription
kernelCom.Initialize();

Console.WriteLine("[>] Subscribed to WNF state. Sending 'Hi' to kernel-mode.");

// Send 'Hi' message to kernel-mode
kernelCom.SendMessage("Hi");

Console.WriteLine("[>] Waiting for kernel-mode responses...");

// Start the interactive menu in a separate thread to keep listening for WNF notifications
Thread menuThread = new Thread(() => InteractiveMenu(kernelCom));
menuThread.Start();

// Keep the main thread alive to receive WNF notifications
menuThread.Join();
}
}

static void InteractiveMenu(KernelCom kernelCom)
{
while (true)
{
Console.WriteLine("\nSelect an option:");
Console.WriteLine("1. Read Memory");
Console.WriteLine("2. Write Memory");
Console.WriteLine("3. Get Module Base Address");
Console.WriteLine("4. Close Driver");
Console.WriteLine("5. Exit");

Console.Write("Choice: ");
string choice = Console.ReadLine();

switch (choice)
{
case "1":
PerformReadMemory(kernelCom);
break;

case "2":
PerformWriteMemory(kernelCom);
break;

case "3":
PerformGetModuleBaseAddress(kernelCom);
break;

case "4":
kernelCom.CloseDriver();
Console.WriteLine("[>] Driver close instruction sent.");
break;

case "5":
Console.WriteLine("[>] Exiting application.");
return;

default:
Console.WriteLine("[!] Invalid choice. Please select a valid option.");
break;
}

// Allow some time for the kernel driver to respond
Thread.Sleep(500);
}
}

static void PerformReadMemory(KernelCom kernelCom)
{
Console.Write("Enter Process ID: ");
if (uint.TryParse(Console.ReadLine(), out uint readPid))
{
Console.Write("Enter Address (hex, e.g., 7FF6C2D40000): ");
if (ulong.TryParse(Console.ReadLine(), System.Globalization.NumberStyles.HexNumber, null, out ulong readAddress))
{
Console.Write("Enter Size (in bytes): ");
if (ulong.TryParse(Console.ReadLine(), out ulong readSize))
{
kernelCom.ReadMemory(readPid, readAddress, readSize);
}
else
{
Console.WriteLine("[!] Invalid size. Please enter a numeric value.");
}
}
else
{
Console.WriteLine("[!] Invalid address format. Please enter a hexadecimal value.");
}
}
else
{
Console.WriteLine("[!] Invalid Process ID. Please enter a numeric value.");
}
}

static void PerformWriteMemory(KernelCom kernelCom)
{
Console.Write("Enter Process ID: ");
if (uint.TryParse(Console.ReadLine(), out uint writePid))
{
Console.Write("Enter Address (hex, e.g., 7FF6C2D40000): ");
if (ulong.TryParse(Console.ReadLine(), System.Globalization.NumberStyles.HexNumber, null, out ulong writeAddress))
{
Console.Write("Enter Data to Write (hex, e.g., DEADBEEF): ");
string dataInput = Console.ReadLine();

try
{
byte[] dataBytes = StringToByteArray(dataInput);
kernelCom.WriteMemory(writePid, writeAddress, dataBytes);
}
catch
{
Console.WriteLine("[!] Invalid data format. Please enter hexadecimal digits only.");
}
}
else
{
Console.WriteLine("[!] Invalid address format. Please enter a hexadecimal value.");
}
}
else
{
Console.WriteLine("[!] Invalid Process ID. Please enter a numeric value.");
}
}

static void PerformGetModuleBaseAddress(KernelCom kernelCom)
{
Console.Write("Enter Process ID: ");
if (uint.TryParse(Console.ReadLine(), out uint pid))
{
Console.Write("Enter Module Name (e.g., notepad.exe): ");
string moduleName = Console.ReadLine();

if (!string.IsNullOrWhiteSpace(moduleName))
{
// Update the instructions to include the process ID
INSTRUCTIONS instructions = new INSTRUCTIONS
{
close = 0,
read = 0,
write = 0,
reqBase = 1,
processId = pid,
address = 0,
size = 0,
bufferAddress = UIntPtr.Zero,
output = UIntPtr.Zero,
baseAddress = 0,
moduleName = moduleName,
data = new byte[1024]
};

kernelCom.SendInstructions(instructions);
}
else
{
Console.WriteLine("[!] Module name cannot be empty.");
}
}
else
{
Console.WriteLine("[!] Invalid Process ID. Please enter a numeric value.");
}
}

// Helper method to convert hex string to byte array
static byte[] StringToByteArray(string hex)
{
hex = hex.Replace("0x", "").Replace(" ", "").Replace("-", "");
if (hex.Length % 2 != 0)
throw new FormatException("Invalid hex string length.");

byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
string byteValue = hex.Substring(i * 2, 2);
bytes[i] = Convert.ToByte(byteValue, 16);
}
return bytes;
}
}
}