Имеется такой драйвер ядра:
#include <ntifs.h>

extern "C" {
	NTKERNELAPI NTSTATUS IoCreateDriver(PUNICODE_STRING DriverName,
										PDRIVER_INITIALIZE InitializationFunction);

	NTKERNELAPI NTSTATUS MmCopyVirtualMemory(PEPROCESS SourceProcess, PVOID SourceAddress,
											 PEPROCESS TargetProcess, PVOID TargetAddress,
											 SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode,
											 PSIZE_T ReturnSize);
}

void debug_print(PCSTR text) {
#ifndef DEBUG
	UNREFERENCED_PARAMETER(text);
#endif // DEBUG

	KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, text));
}

namespace driver {
	namespace codes {
		// Used to setup the driver.
		constexpr ULONG attach =
			CTL_CODE(FILE_DEVICE_UNKNOWN, 0x696, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);

		// Read process memory.
		constexpr ULONG read =
			CTL_CODE(FILE_DEVICE_UNKNOWN, 0x697, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);

		// Write process memory.
		constexpr ULONG write =
			CTL_CODE(FILE_DEVICE_UNKNOWN, 0x698, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
	} // namespace codes

	// Shares between user mode & kernel mode.
	struct Request {
		HANDLE process_id;
		
		PVOID target;
		PVOID buffer;
		
		SIZE_T size;
		SIZE_T return_size;
	};

	NTSTATUS create(PDEVICE_OBJECT device_object, PIRP irp) {
		UNREFERENCED_PARAMETER(device_object);

		IoCompleteRequest(irp, IO_NO_INCREMENT);

		return irp->IoStatus.Status;
	}

	NTSTATUS close(PDEVICE_OBJECT device_object, PIRP irp) {
		UNREFERENCED_PARAMETER(device_object);

		IoCompleteRequest(irp, IO_NO_INCREMENT);

		return irp->IoStatus.Status;
	}

	NTSTATUS device_control(PDEVICE_OBJECT device_object, PIRP irp) {
		UNREFERENCED_PARAMETER(device_object);

		debug_print("[+] Device control called.\n");

		NTSTATUS status = STATUS_UNSUCCESSFUL;

		// We need this to determine with code passed through.
		PIO_STACK_LOCATION stack_irp = IoGetCurrentIrpStackLocation(irp);

		// Access the request object sent from user mode.
		auto request = reinterpret_cast<Request*>(irp->AssociatedIrp.SystemBuffer);

		if (stack_irp == nullptr || request == nullptr) {
			IoCompleteRequest(irp, IO_NO_INCREMENT);
			return status;
		}

		// The target process we want access to.
		static PEPROCESS target_process = nullptr;
		  
		const ULONG control_code = stack_irp->Parameters.DeviceIoControl.IoControlCode;
		switch (control_code) {
			case codes::attach:
				status = PsLookupProcessByProcessId(request->process_id, &target_process);
				break;

			case codes::read:
				if (target_process != nullptr) 
					status = MmCopyVirtualMemory(target_process, request->target,
												 PsGetCurrentProcess(), request->buffer, request->size,
												 KernelMode, &request->return_size);
				break;
			
			case codes::write:
				if (target_process != nullptr)
					status = MmCopyVirtualMemory(PsGetCurrentProcess(), request->buffer,
												 target_process, request->target, request->size,
												 KernelMode, &request->return_size);
				break;
			
			default:
				break;
		}

		irp->IoStatus.Status = status;
		irp->IoStatus.Information = sizeof(Request);

		IoCompleteRequest(irp, IO_NO_INCREMENT);

		return status;
	}

} // namespace driver

// Our "real" entry point.
NTSTATUS driver_main(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path) {
	UNREFERENCED_PARAMETER(registry_path);

	UNICODE_STRING device_name = {};
	RtlInitUnicodeString(&device_name, L"\\Device\\MotorolaDriver");

	// Create driver device obj.  
	PDEVICE_OBJECT device_object = nullptr;
	NTSTATUS status = IoCreateDevice(driver_object, 0, &device_name, FILE_DEVICE_UNKNOWN,
							FILE_DEVICE_SECURE_OPEN, FALSE, &device_object);
	if (status != STATUS_SUCCESS) {
		debug_print("[-] Failed to create driver device.\n");
		return status;
	}

	debug_print("[+] Driver device successfully created.\n");
	
	UNICODE_STRING symbolic_link = {};
	RtlInitUnicodeString(&symbolic_link, L"\\DosDevices\\MotorolaDriver");

	status = IoCreateSymbolicLink(&symbolic_link, &device_name);
	if (status != STATUS_SUCCESS) {
		debug_print("[-] Failed to establish symbolic link.\n");
		return status;
	}

	debug_print("[+] Driver symbolic link successfully established.\n");

	// Allow us to send small amount of data between um/km.
	SetFlag(device_object->Flags, DO_BUFFERED_IO);

	// Set the driver handlers to our functions with our logic.
	driver_object->MajorFunction[IRP_MJ_CREATE] = driver::create;
	driver_object->MajorFunction[IRP_MJ_CLOSE] = driver::close;
	driver_object->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver::device_control;

	// We have initialized our device.
	ClearFlag(device_object->Flags, DO_DEVICE_INITIALIZING);

	debug_print("[+] Driver initialized successfully.\n");

	return status;
}

NTSTATUS DriverEntry() {
	debug_print("[+] Alloe from the kernel!\n");

	UNICODE_STRING  driver_name = {};
	RtlInitUnicodeString(&driver_name, L"\\Driver\\MotorolaDriver");

	return IoCreateDriver(&driver_name,driver_main);
}


Приложение для него:
#include <iostream>
#include <iomanip>
#include <future>
#include <algorithm>
#include <string>
#include <memory>
#include <mutex>
#include <vector>
#include <Windows.h>
#include <TlHelp32.h>
#include "driver_code.h"

class MemoryPatcher {
    HANDLE driver_handle;
    DWORD pid;
    std::mutex print_mutex;
    std::uintptr_t turnback_patch_address = 0;
    bool is_patched_to_31 = false;

public:
    MemoryPatcher(HANDLE driverHandle, DWORD processId) : driver_handle(driverHandle), pid(processId) {}

    bool apply_patch(const std::string& patch_name, const std::vector<BYTE>& sequence, const std::vector<BYTE>& patch, std::uintptr_t start_address, std::uintptr_t end_address, int target_occurrence = 1) {
        std::lock_guard<std::mutex> guard(print_mutex);
        std::cout << "[+] Attempting to apply " << patch_name << " patch\n";
        int occurrence_count = 0;
        auto current_address = start_address;

        while (current_address < end_address) {
            auto found_address = driver::find_memory_sequence(driver_handle, pid, sequence, current_address, end_address);
            if (found_address != 0) {
                occurrence_count++;
                if (occurrence_count == target_occurrence) {
                    std::cout << "[+] " << patch_name << " sequence found at : 0x" << std::uppercase << std::hex << found_address << std::dec << "\n";
                    if (driver::replace_memory_sequence(driver_handle, found_address, patch)) {
                        std::cout << "[+] " << patch_name << " sequence has been successfully replaced!\n";
                        if (patch_name == "turnback" && occurrence_count == target_occurrence) {
                            turnback_patch_address = found_address;
                            std::cout << "[+] vision address set to : 0x" << std::hex << turnback_patch_address << std::dec << "\n";
                        }
                        return true;
                    }
                    else {
                        std::cout << "[-] Failed to apply " << patch_name << ".\n";
                            return false;
                    }
                }
                current_address = found_address + sequence.size();
            }
            else {
                break;
            }
        }
        std::cout << "[-] " << patch_name << " sequence not found.\n";
        return false;
    }

    void toggle_vision() {
        std::lock_guard<std::mutex> guard(print_mutex);
        if (turnback_patch_address == 0) {
            std::cout << "[-] vision address is not set.\n";
            return;
        }

        std::vector<BYTE> new_patch;
        std::string vision_state_before_toggle = is_patched_to_31 ? "ON" : "OFF";

        if (is_patched_to_31) {
            new_patch = { 0x00, 0x00, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x65, 0x00, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

        } else {
            new_patch = { 0x00, 0x00, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x65, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        }

        if (driver::replace_memory_sequence(driver_handle, turnback_patch_address, new_patch)) {
            is_patched_to_31 = !is_patched_to_31;
            std::string vision_state_after_toggle = is_patched_to_31 ? "ON" : "OFF";
            std::cout << "[+] vision " << vision_state_after_toggle << " at 0x" << std::hex << turnback_patch_address << "\n";
        }
        else {
            std::cout << "[-] failed to toggle Vision.\n";
        }
    }
};

int main() {
    auto pid = ProcessHelper::getProcessId(L"l2.bin");
    if (pid == 0) {
        std::cout << "[-] Failed to find l2.bin.\n";
        std::cin.get();
        return 1;
    }

    HANDLE driver_handle = CreateFile(L"\\\\.\\MotorolaDriver", GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (driver_handle == INVALID_HANDLE_VALUE) {
        std::cout << "[-] Failed to create our driver handle.\n";
        std::cin.get();
        return 1;
    }

    if (driver::attach_to_process(driver_handle, pid)) {
        std::cout << "[+] Attachment successful.\n";
    }
    else {
        std::cout << "[-] Failed to attach to process.\n";
        std::cin.get();
        return 1;
    }

    MemoryPatcher patcher(driver_handle, pid);

    std::vector<BYTE> page_up_down_bytes = { 0x44, 0x00, 0x65, 0x00, 0x62, 0x00, 0x75, 0x00, 0x67, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x75, 0x00, 0x2E, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x69, 0x00, 0x78, 0x00, 0x65, 0x00, 0x64, 0x00, 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x74, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x72, 0x00, 0x61, 0x00, 0x20, 0x00, 0x44, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x46, 0x00, 0x69, 0x00, 0x78, 0x00, 0x65, 0x00, 0x64, 0x00, 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x74, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x72, 0x00, 0x61, 0x00, 0x20, 0x00, 0x55, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x79, 0x00, 0x62, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x50, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x76, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    std::vector<BYTE> page_up_down_patch = { 0x44, 0x00, 0x65, 0x00, 0x62, 0x00, 0x75, 0x00, 0x67, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x75, 0x00, 0x2E, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x32, 0x00, 0x52, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x20, 0x00, 0x70, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x79, 0x00, 0x62, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x50, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x76, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

    std::vector<BYTE> turnback_bytes = { 0x00, 0x00, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x75, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x42, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00 };
    std::vector<BYTE> turnback_patch = { 0x00, 0x00, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x65, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

    // Асинхронно применяем патчи
    auto pageUpDownPatchFuture = std::async(std::launch::async, &MemoryPatcher::apply_patch, &patcher, "page up - page down", page_up_down_bytes, page_up_down_patch, 0x00000000, 0x7FFFFFFF, 1);
    auto turnBackPatchFuture = std::async(std::launch::async, &MemoryPatcher::apply_patch, &patcher, "turnback", turnback_bytes, turnback_patch, 0x0000000, 0x7FFFFFFF, 2);

    // Ожидаем завершения
    pageUpDownPatchFuture.wait();
    turnBackPatchFuture.wait();

    std::cout << "\n[+] Waiting for vision action...\n";

    // Цикл для отслеживания нажатия кнопки колесика мыши
    while (true) {
        Sleep(100); // задержка для снижения загрузки CPU
        if ((GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0) {
            patcher.toggle_vision();
            Sleep(350);
        }
    }
    CloseHandle(driver_handle);
    std::cin.get();
    return 0;
}

И файл driver_code.h:
class ProcessHelper {
public:
	static DWORD getProcessId(const std::wstring& processName) {
		DWORD processId = 0;
		HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		if (snapShot == INVALID_HANDLE_VALUE) {
			return processId;
		}

		PROCESSENTRY32W entry;
		entry.dwSize = sizeof(PROCESSENTRY32W);

		if (Process32FirstW(snapShot, &entry)) {
			do {
				if (_wcsicmp(processName.c_str(), entry.szExeFile) == 0) {
					processId = entry.th32ProcessID;
					break;
				}
			} while (Process32NextW(snapShot, &entry));
		}

		CloseHandle(snapShot);
		return processId;
	}

	static std::uintptr_t getModuleBase(DWORD pid, const std::wstring& moduleName) {
		std::uintptr_t moduleBase = 0;
		HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
		if (snapShot == INVALID_HANDLE_VALUE) {
			return moduleBase;
		}

		MODULEENTRY32W entry;
		entry.dwSize = sizeof(MODULEENTRY32W);

		if (Module32FirstW(snapShot, &entry)) {
			do {
				if (moduleName == entry.szModule) {
					moduleBase = reinterpret_cast<std::uintptr_t>(entry.modBaseAddr);
					break;
				}
			} while (Module32NextW(snapShot, &entry));
		}

		CloseHandle(snapShot);
		return moduleBase;
	}
};

namespace driver {
	namespace codes {
		// Used to setup the driver.
		constexpr ULONG attach =
			CTL_CODE(FILE_DEVICE_UNKNOWN, 0x696, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);

		// Read process memory.
		constexpr ULONG read =
			CTL_CODE(FILE_DEVICE_UNKNOWN, 0x697, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);

		// Write process memory.
		constexpr ULONG write =
			CTL_CODE(FILE_DEVICE_UNKNOWN, 0x698, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
	} // namespace codes

	// Shares between user mode & kernel mode.
	struct Request {
		HANDLE process_id;

		PVOID target;
		PVOID buffer;

		SIZE_T size;
		SIZE_T return_size;
	};

	bool attach_to_process(HANDLE driver_handle, const DWORD pid) {
		Request r;
		r.process_id = reinterpret_cast<HANDLE>(pid);

		return DeviceIoControl(driver_handle, codes::attach, &r, sizeof(r), &r, sizeof(r), nullptr, nullptr);
	}

	// Обновлено для поддержки размера буфера и обработки указателей на данные
	bool read_memory(HANDLE driver_handle, std::uintptr_t address, PVOID buffer, SIZE_T size) {
		Request r;
		r.target = reinterpret_cast<PVOID>(address);
		r.buffer = buffer;
		r.size = size;

		DWORD bytes_returned;
		return DeviceIoControl(driver_handle, codes::read, &r, sizeof(r), &r, sizeof(r), &bytes_returned, nullptr);
	}

	bool write_memory(HANDLE driver_handle, std::uintptr_t address, const void* buffer, SIZE_T size) {
		Request r;
		r.target = reinterpret_cast<PVOID>(address);
		r.buffer = const_cast<PVOID>(buffer);
		r.size = size;

		DWORD bytes_returned;
		return DeviceIoControl(driver_handle, codes::write, &r, sizeof(r), &r, sizeof(r), &bytes_returned, nullptr);
	}

	std::uintptr_t find_memory_sequence(HANDLE driver_handle, DWORD pid, const std::vector<BYTE>& sequence, std::uintptr_t start_address, std::uintptr_t end_address) {
		std::vector<BYTE> buffer(10240); // Буфер для чтения памяти
		std::uintptr_t current_address = start_address;

		while (current_address < end_address) {
			// Чтение памяти процесса
			SIZE_T read_size = buffer.size();
			if (current_address + read_size > end_address) {
				read_size = end_address - current_address;
			}
			if (!read_memory(driver_handle, current_address, buffer.data(), read_size)) {
				// Заметьте, здесь мы принимаем решение продолжать, даже если чтение не удалось
				// Переход к следующему блоку памяти, даже если текущий блок нельзя прочитать
				current_address += buffer.size();
				continue;
			}

			// Поиск последовательности в буфере
			auto it = std::search(buffer.begin(), buffer.begin() + read_size, sequence.begin(), sequence.end());

			// Проверка, нашли ли мы последовательность
			if (it != buffer.begin() + read_size) {
				return current_address + std::distance(buffer.begin(), it);
			}

			// Перемещаем current_address вперед, чтобы искать в следующем блоке памяти
			if (read_size == buffer.size()) {
				current_address += buffer.size() - sequence.size() + 1;
			}
			else {
				// Если размер последнего прочитанного блока меньше размера буфера,
				// значит мы достигли конца доступной области памяти.
				break;
			}
		}

		return 0; // Если не найдено
	}

	bool replace_memory_sequence(HANDLE driver_handle, std::uintptr_t address, const std::vector<BYTE>& new_bytes) {
		return write_memory(driver_handle, address, new_bytes.data(), new_bytes.size());
	}
}  // namespace driver

Спустя некоторое время после работы приложения происходит отключение сетевого драйвера, а после BSOD. Найди проблемный участок, исправь его и отправь мне готовый код.