BYOVD Ring 0: Kernel Callback Zeroing via RTCore64.sys
Validated Arbitrary kernel read/write via RTCore64.sys, Windows 11, Defender RTP enabled.
Validated 10 callbacks zeroed in PspCreateProcessNotifyRoutine — confirmed 2026-07-06.
Validated Zero Defender alerts across all phases of kill chain.
Research EDR blind without BSOD (pool reads + RET-patch still in progress).
Background: The Two Worlds Inside Windows
Windows splits the processor into two privilege levels. Your browser, your game, your AV agent — they all live in Ring 3 (user mode). The kernel — the part that manages memory, processes, drivers, and the hardware — lives in Ring 0. A user mode program can't just read or write kernel memory. The CPU enforces that boundary in silicon.
EDRs (Endpoint Detection and Response tools like Defender, CrowdStrike, SentinelOne) register callbacks inside the kernel. Every time a new process starts, the kernel walks a private list of registered callbacks and calls each one — that's how the security product sees what's happening. It's a phone-tap installed directly into the OS nervous system.
The structure in question: PspCreateProcessNotifyRoutine
A private kernel array — not exported, not documented — that holds up to 64 callback pointers. When any process starts, ntoskrnl.exe loops through this array and calls each registered function. EDRs register here. Zero their entry and they stop getting called. They go blind to new processes.
The problem: to write to kernel memory, you need to be running in Ring 0. An attacker in user mode shouldn't be able to do that. This is the wall BYOVD is designed to break.
The BYOVD Concept: The Traitor Knight
BYOVD — Bring Your Own Vulnerable Driver — is the technique of loading a legitimately signed, Windows-trusted kernel driver that contains a vulnerability allowing arbitrary memory access. You exploit the driver's own bug from user mode. The driver, running in Ring 0, performs the read or write on your behalf.
Windows has no way to distinguish "this driver is being used legitimately by MSI Afterburner" from "this driver is being weaponised." A signed driver is a signed driver. The kernel trusts the signature. The exploit lives inside the driver.
The Driver: RTCore64.sys (CVE-2019-16098)
RTCore64.sys is a kernel driver shipped with MSI Afterburner — the GPU overclocking tool used by millions of PC gamers. It exposes arbitrary kernel memory read and write from user mode via a device control interface (IOCTL). That's not a bug introduced by attackers. That's literally what the driver does, by design, to let the overclocking software touch hardware registers.
CVE-2019-16098 — the vulnerability
The driver does not validate that the memory addresses passed via IOCTL are safe to access. Any process can open the device \\.\RTCore64 and send read/write requests targeting arbitrary kernel addresses. The certificate on the driver is valid. Windows loads it without complaint.
The driver supports two operations via IOCTL:
- Read primitive: IOCTL
0x80002048— read a DWORD from any kernel address - Write primitive: IOCTL
0x8000204c— write a DWORD to any kernel address
Important: the struct layout matters
Public PoCs for CVE-2019-16098 describe struct layouts based on older driver versions. Our binary (hash 01aa278b) has a different layout. Sending the wrong struct returns Error 87 — The parameter is incorrect. The driver silently rejects it. We had to reverse our exact binary in Ghidra to get the correct field offsets before anything worked.
Reversing the struct in Ghidra
The IOCTL dispatch handler lives at the driver's IRP_MJ_DEVICE_CONTROL major function. In DRIVER_OBJECT, the MajorFunction array starts at offset 0x70, each pointer is 8 bytes, so MajorFunction[14] (IOCTL handler) = 0x70 + (14 × 8) = 0xE0. The handler was FUN_00011450.
Inside the handler, the read primitive wrote its result to puVar11[7] — offset 28 in the struct. Reconstructed struct:
// RTCore64 read request — reversed from binary hash 01aa278b typedef struct _RTCore64Read { ULONG64 Address; // offset 0 — kernel address to read from ULONG32 Offset; // offset 8 — added to Address ULONG32 _pad1; // offset 12 ULONG64 _pad2; // offset 16 ULONG32 Value; // offset 28 — result placed here by driver } RTCore64Read;
The write struct is identical with Value as the input instead of output, sent via IOCTL 0x8000204c.
Five Steps to Kernel Write
Requires SYSTEM or Administrator. Use sc.exe to register and start the driver as a kernel service. Windows checks the Authenticode signature — valid, loads without question. The device \\.\RTCore64 becomes available to any process that opens it.
Load ntoskrnl.exe as a flat file to find its base address (the loaded image VA). Read 4 bytes from that address via IOCTL. A valid kernel read returns 0x00905A4D — that's MZ\x90\x00, the PE magic bytes at the start of every Windows executable. If you get MZ, the read works. There is no other acceptable proof.
Kernel read confirmed — validated output
Read at ntoskrnl.exe base returned value=905a4d — little-endian MZ header. Defender RTP on. No alert. No log. The CPU boundary crossed silently through a trusted driver.
The callback array is private — not exported, no public symbol. But PsSetCreateProcessNotifyRoutine IS exported, and it references the private array via a RIP-relative LEA instruction. Load ntoskrnl as a flat file, scan its bytes for the LEA pattern in PsSetCreateProcessNotifyRoutine, compute the array's kernel virtual address. No hardcoded addresses — works across kernel updates.
Each entry in the array is an EX_FAST_REF structure — a pointer with the low 4 bits used as a reference counter. Read each slot to find registered callbacks. On a standard Windows 11 box: 5 process callbacks registered at PspCreateProcessNotifyRoutine.
Use the write primitive (IOCTL 0x8000204c) to write zero to each callback slot in the array. After zeroing, when a new process starts, ntoskrnl walks the array and finds nothing registered. Defender's callback never fires. The process appears on disk — but no notification reaches the AV engine.
PatchGuard Detonation: Why the BSOD Is a Feature
Windows has a second layer of kernel self-defence called PatchGuard (Kernel Patch Protection). It runs in encrypted, randomly-timed kernel threads. Periodically — the timing is intentionally unpredictable — it checksums critical kernel structures. If any checksum fails, it calls KeBugCheckEx(0x109) immediately. The system dies. Blue screen.
PspCreateProcessNotifyRoutine is one of the structures PatchGuard monitors. Zero its entries and PatchGuard will eventually notice the checksum mismatch and fire a BSOD.
0x109 CRITICAL_STRUCTURE_CORRUPTION — the proof the write worked
PatchGuard only triggers if the modification landed. If the write had failed, PatchGuard would have found the array intact and done nothing. So a BSOD with bugcheck 0x109 after zeroing callbacks is not a failure — it is confirmation the write primitive works exactly as expected.
Validated result — 2026-07-06
strike.exe zeroed 10 callbacks at fffff801240ec1e0. PatchGuard fired 0x109 CRITICAL_STRUCTURE_CORRUPTION. VM BSODed. Exfiltration and encryption had already completed before the BSOD detonated. Zero Defender alerts across all phases. The crash interrupted forensics and covered tracks — exactly the design.
The operational pattern: BYOVD + callback zeroing is not used to keep the machine running indefinitely. It's used to create a window of EDR blindness long enough to complete the objective (exfil, encryption, lateral movement). The BSOD lands afterward. It looks like a driver crash, not an intrusion.
What the Full Kill Chain Can Do
The validated BYOVD primitive (vader-rootkit Phase LIMA) exposes three separate capabilities via the same RTCore64.sys write primitive:
| Command | What it does | Status |
|---|---|---|
callbacks |
Zero PspCreateProcessNotifyRoutine — EDR blind to process creation | Validated |
token <PID> |
Walk EPROCESS list, steal SYSTEM token, inject into target process — instant SYSTEM | Built |
dse |
Disable Driver Signature Enforcement — load unsigned drivers | Built |
all <PID> |
Full chain: token steal + callback removal in one pass | Built |
Two drivers are supported. RTCore64.sys (CVE-2019-16098, MSI Afterburner) and dbutil_2_3.sys (CVE-2021-21551, Dell BIOS Utility). Same primitives, different IOCTL structs. The codebase handles both.
The Next Wall: Staying Alive After the Window
PatchGuard monitors the array data — the pointer values in each callback slot. Zeroing the data triggers the checksum failure. The next evolution is to leave the array intact and instead patch the callback function itself.
RET-patch approach (in progress)
Instead of zeroing the EX_FAST_REF entry in the array, find the actual callback function the entry points to (which lives in kernel pool memory, not kernel image space). Write a single byte 0xC3 (RET instruction) at its entry point. The callback gets called — PatchGuard's checksum of the array is intact — but the function immediately returns without executing. EDR blind, no checksum failure, no BSOD.
The difficulty: the EX_CALLBACK_ROUTINE_BLOCK lives in kernel pool memory (ffffc40b... addresses). RTCore64's primitives work cleanly against kernel image space (fffff801...) but crash the system when targeting pool addresses — the driver dereferences them in kernel mode with no exception handling. Pool reads require a different approach.
The RTCore64 + dbutil BYOVD approach is also supported by dbutil_2_3.sys (Dell, CVE-2021-21551), which uses the same general IOCTL pattern but may have different pool access behaviour. This is the current research direction.
Detection
| Detection signal | Where | Reliability | Notes |
|---|---|---|---|
| Sysmon Event ID 6 — driver load | Sysmon + Windows Event Log | Medium | RTCore64.sys load will be logged. Check SHA256 hash against known vulnerable driver lists (LOLDrivers project). |
| Certificate revocation check | Network / proxy | Low | Driver certificate is valid. Revocation requires vendor to revoke it. MSI has not revoked CVE-2019-16098 certs as of 2026. |
sc.exe create + sc.exe start for kernel-type service |
Process creation logs | Medium | Unusual for a user to manually load a kernel driver via sc.exe. Alert on kernel service creation outside standard install paths. |
| 0x109 BSOD crash dump | Windows Event Log, crash dump | High (post-mortem) | CRITICAL_STRUCTURE_CORRUPTION after the fact. Confirms callback modification happened. Dump will show the zeroed array if captured before reboot. |
| Microsoft Vulnerable Driver Blocklist | Windows Defender Credential Guard / HVCI | High | HVCI (Hypervisor-Protected Code Integrity) with the blocklist enabled blocks RTCore64.sys entirely. Most enterprise deployments have this. Consumer Windows 11 Home does not enable it by default. |
The effective defence: HVCI + blocklist
Hypervisor-Protected Code Integrity (HVCI) with Microsoft's Vulnerable Driver Blocklist prevents any driver on the list from loading. RTCore64.sys (CVE-2019-16098) is on the blocklist. On a system with HVCI enabled, this entire attack chain fails at Step 1 — the driver load is blocked before it reaches the kernel. The gap: HVCI is off by default on Windows Home. Enterprise is largely covered. Consumer is not.
Defensive Recommendations
- Enable HVCI: Settings → Windows Security → Device Security → Core Isolation → Memory Integrity. This is the single most effective control against BYOVD.
- Enable Sysmon with Event ID 6 alerting: Driver loads should be logged and hash-checked against the LOLDrivers known-vulnerable driver list.
- Alert on
sc.exekernel service creation: Legitimate software doesn't load kernel drivers via manual sc.exe commands at runtime. - Monitor for 0x109 BSODs: Any
CRITICAL_STRUCTURE_CORRUPTIONcrash on an endpoint should be treated as a potential kernel modification event, not a random driver crash.
What's Not Covered Here
The BYOVD loader, the IOCTL structs, and the callback enumeration logic are from rainfantry's private research (vader-rootkit, ring-stack). No source code is published. The validated results and technique description are shared to enable detection and defence.
The next phase of this research — pool-safe R/W for RET-patching callback functions — is ongoing. Results will be published when validated.
Further Reading
- Ch11: The Syscall Lattice — Hell's Gate, direct syscalls, the 0-day development workflow that feeds techniques like this
- AV Evasion Fundamentals — AMSI/ETW bypass, sandbox detection (the layers above Ring 0)
- ChromElevator: ABE Bypass via COM Injection — another kernel-adjacent technique using direct syscalls
- LOLDrivers project — known vulnerable driver blocklist maintained by community