About OFFCEPT
Built by operators who spent years breaking into networks before building a company around it.
Learn More
← Back
Technical Research24 May 202614 min read

Killing EDR visibility at the kernel: BYOVD

BYOVD kernel callback manipulation: killing EDR visibility at the kernel level

Most EDR products share a fundamental architectural assumption: they rely on kernel callbacks to observe what is happening on the endpoint. Process creation, thread injection, image loading, registry modification. These are the eyes and ears of every major EDR on the market. The assumption is that if the EDR driver is loaded and its callbacks are registered, the SOC has visibility. But what happens when someone simply removes those callbacks from the kernel?

This post walks through the technique we presented at rootedPT 2026. It is not theoretical. The attack chain works against currently deployed EDR products, the tools are publicly available, and the core problem has been known for years without a comprehensive fix. We are publishing this because defenders need to understand exactly how fragile their visibility is, and what they can actually do about it.

How EDRs actually see your endpoints

When an EDR agent installs its driver on a Windows machine, it does not monitor every system call directly. That would be far too expensive. Instead, it registers callback routines with the Windows kernel. These callbacks are functions that the kernel promises to call whenever specific events occur. The most important ones:

  • PspCreateProcessNotifyRoutine fires every time a process is created or exits. This is how your EDR knows when notepad.exe spawns cmd.exe.
  • PspCreateThreadNotifyRoutine fires on thread creation. This is how your EDR detects remote thread injection, where one process creates a thread inside another.
  • PspLoadImageNotifyRoutine fires when a DLL or EXE is loaded into memory. This is how your EDR sees when a suspicious DLL gets injected into a legitimate process.
  • CmCallbackList fires on registry operations. This is how your EDR tracks persistence mechanisms being installed in the registry.

The kernel stores these callbacks in fixed arrays. PspCreateProcessNotifyRoutine, for example, is an array that can hold up to 64 entries. When a process is created, the kernel iterates through this array and calls each registered function. The EDR driver receives the event, inspects it, and decides whether to log it, alert on it, or block it.

This architecture is efficient and well-designed. It also has a critical weakness: the arrays live in kernel memory, and they are writable.

BYOVD: the attack chain

Bring Your Own Vulnerable Driver (BYOVD) is exactly what it sounds like. The attacker loads a legitimately signed kernel driver that contains a known vulnerability, then exploits that vulnerability to gain arbitrary kernel read and write capabilities. No zero-day required. The driver is real, signed by a real vendor, and in many cases still distributed through Windows Update or vendor software packages.

The chain runs in five steps, all from user mode, with no special privileges beyond local administrator:

  • Drop the vulnerable driver. The attacker places a known vulnerable .sys file on disk. Dell, ASRock, Gigabyte, and others have all shipped drivers later found to contain exploitable vulnerabilities. The file passes signature verification because the signature is valid. It was meant to be there.
  • Load the driver. Standard Windows service creation APIs. The Service Control Manager accepts it because the signature is valid. The EDR sees a driver load event, checks the signature, sees a legitimate vendor name, and moves on.
  • Exploit the vulnerability. Open a handle to the driver's device object and send a crafted IOCTL. The vulnerability triggers arbitrary memory read or write operations in kernel space. The attacker now has a read/write primitive for the entire kernel address space, from user mode.
  • Locate the callback arrays. Using the kernel read primitive, scan kernel memory for the callback arrays. They sit at fixed offsets from exported kernel symbols. The offsets vary by Windows build but are trivially discoverable through reverse analysis of ntoskrnl.exe. Tools like CallbackDb maintain up-to-date offset databases for every build. Read the array contents and you know exactly what the EDR is watching.
  • Zero the callbacks. Using the kernel write primitive, overwrite each callback function pointer with null (0x0000000000000000). The kernel still iterates through the array when events occur, but every entry is null. The callbacks never fire. The EDR driver is still loaded, its service is still running, the dashboard still shows green. But the kernel is no longer telling it anything.

At this point, the attacker has full freedom to execute techniques on the endpoint. Process injection, credential dumping, lateral movement. The EDR will not see any of it because the kernel callbacks that would have reported these events have been silenced.

Proof: WinDbg before and after

WinDbg kernel memory dump showing PspCreateProcessNotifyRoutine callback array before zeroing - populated with EDR function pointers

We demonstrated this at rootedPT using WinDbg connected to a live Windows kernel. Before the attack, dumping PspCreateProcessNotifyRoutine shows a populated array. Multiple function pointers are visible, each one corresponding to a registered EDR or security product callback. The array is active and functional.

After the attack, the same memory region shows all zeroes. Every callback pointer has been overwritten. The EDR driver is completely unaware that its callback has been removed because the driver itself has no mechanism to verify that its callback is still in the array. It registered the callback at startup and assumes it will be called forever.

WinDbg kernel memory dump showing PspCreateProcessNotifyRoutine and PspLoadImageRoutine callback arrays after zeroing - all null pointers

We then executed a series of attack techniques: process injection, credential dumping via LSASS access, and lateral movement via PsExec. The EDR detected zero of these. Its console showed no alerts. Its process tree showed no suspicious activity. The endpoint appeared completely normal while we had full access.

Gap 1 is solved. What about Gap 2?

So the kernel callbacks are zeroed. The EDR's primary detection mechanism is blind. But modern EDR products also have ML models, behavioral analysis engines, and cloud-based reputation systems. Even without kernel callbacks, surely these would catch suspicious activity?

This is the second gap, and it is independent of the first.

ML and behavioral detection relies on collecting telemetry about processes, file writes, network connections, and registry changes. But the quality of this telemetry matters enormously. If the attacker crafts their payload carefully, the behavioral signals look benign:

  • Valid PE headers and metadata. The payload has correct compilation timestamps, legitimate-looking version resources, and proper digital signatures (self-signed certificates with believable organization names). ML models that rely on static analysis see a normal executable.
  • Legitimate import table. The imported functions are standard Windows APIs with no suspicious patterns. No VirtualAllocEx, no WriteProcessMemory, no CreateRemoteThread. The dangerous calls are resolved dynamically at runtime.
  • Behavioral baseline alignment. The payload's behavior profile matches legitimate admin tooling. It runs from expected directories, accesses expected resources, and follows expected execution patterns. Behavioral models that flag statistical anomalies see nothing unusual.
  • ETW provider tampering. Many EDRs supplement kernel callbacks with Event Tracing for Windows (ETW) providers. But ETW providers are also kernel structures. An attacker with a kernel write primitive can disable specific ETW providers by patching their GUID registration in the same way they zero callback arrays. The ML model receives no telemetry to analyze because the pipeline feeding it has been cut at the source.
ML behavioral detection bypass showing how crafted payloads align with legitimate behavioral baselines to evade EDR analysis

The key insight is that Gap 1 (kernel callback blindness) and Gap 2 (ML/behavioral bypass) are independent. Fixing one does not fix the other. You can harden kernel callbacks (we will discuss HVCI shortly) and still have an attacker walk right past your ML detection. Or you can have perfect behavioral detection and still miss everything because your kernel callbacks were zeroed.

This two-gap model is what makes BYOVD attacks so dangerous in practice. Defenders who focus only on driver blocklists are addressing Gap 1 but ignoring Gap 2. Defenders who focus only on behavioral detection are addressing Gap 2 but ignoring Gap 1. Both gaps need to be closed simultaneously.

HVCI: the mitigation that should work

Hypervisor-Protected Code Integrity (HVCI) uses the Windows hypervisor to create isolated memory regions. Kernel code and data pages that should not be modified are placed in memory that is only accessible from the Virtual Trust Level 0 (VTL 0) host, not from the guest operating system. In theory, this means that even if an attacker has a kernel write primitive, they cannot modify protected kernel structures.

Microsoft has been positioning HVCI as the primary defense against kernel-level attacks, including BYOVD. And in a fully hardened HVCI deployment, it works. The callback arrays are protected. The attacker cannot zero them.

But HVCI has three bypass surfaces that matter operationally:

Bypass surface 1: Vulnerable signed drivers

HVCI restricts which drivers can load based on code integrity requirements. But drivers that were signed before HVCI requirements were tightened may still load through compatibility exceptions. In our testing, we consistently find vulnerable drivers that load cleanly on HVCI-enabled systems.

Bypass surface 2: Detonation attacks

Even when HVCI protects the callback arrays directly, an attacker with a kernel write primitive can target other unprotected kernel structures to achieve the same result. HVCI does not protect everything. The callback arrays themselves may be guarded, but the dispatcher structures that invoke them, the ETW provider registrations that feed telemetry, and the object manager callbacks that track handle access are not all covered. An attacker who studies which structures are and are not protected can redirect the attack through an unprotected path.

Bypass surface 3: Deployment reality

HVCI requires hardware support (nested virtualization on the CPU) and specific firmware configurations. In enterprise environments, we routinely find machines where HVCI is enabled in policy but not actually running. Legacy applications, custom drivers, and older hardware often prevent HVCI from activating. A significant percentage of the endpoints we test during EDR engagements have HVCI listed as enabled in the security dashboard but are not actually protected at the hypervisor level.

Case study: DsArk64

During our rootedPT presentation, we walked through a real example that demonstrates why driver blocklists alone are insufficient. DsArk64 is a WHQL-signed driver. WHQL (Windows Hardware Quality Labs) signing is the highest trust level for Windows drivers. It means Microsoft tested and approved the driver. It ships bundled with 360 Total Security, a legitimate security product.

DsArk64 GitHub repository showing WHQL-signed driver with kernel read/write primitives and process kill capability

DsArk64 exposes IOCTLs that provide process termination and arbitrary kernel memory read and write capabilities. These capabilities are by design. The driver was built to provide them. The signing is legitimate because the driver was submitted through the proper channels and passed Microsoft's validation.

This is the problem with reactive blocklists. DsArk64 was not added to any blocklist because it was signed through the proper process. It provides full kernel read and write primitives. It ships with a security product that users install intentionally. An attacker who drops DsArk64 on a target machine is loading a driver that was approved by Microsoft, signed with a WHQL certificate, and distributed through legitimate channels.

The takeaway is not that WHQL signing is broken. It is that any system that relies on enumerating bad drivers will always be behind. New vulnerable drivers are discovered continuously. Legitimate drivers with dangerous capabilities exist in large numbers.

Kernel Data Protection: the fix that is not deployed

Microsoft has developed a technology called Kernel Data Protection (KDP) that would directly address the callback zeroing problem. KDP uses the hypervisor to mark specific kernel data structures as read-only, even from kernel mode code. If KDP were applied to the callback arrays, the write primitive from a BYOVD attack would simply fail.

The problem is deployment. KDP exists in Windows, but it is not widely enabled for callback arrays in practice. The reasons are complex: performance concerns, compatibility testing requirements, and the fact that applying KDP to frequently-modified kernel structures requires careful coordination across the Windows kernel team. As of our testing in 2026, callback arrays on fully patched Windows systems are still writable from kernel mode.

What defenders should actually do

The standard vendor response to BYOVD is "enable HVCI and update your driver blocklist." This is correct but insufficient. Here is what we recommend based on what we see in real engagements:

  • Enable HVCI and verify it is actually running. Do not trust the dashboard. Check that the hypervisor is actively protecting kernel memory on your endpoints. We find a 20-30% failure rate in enterprise deployments where policy says enabled but the hypervisor is not active.
  • Deploy Windows Defender Application Control (WDAC) with a strict policy. WDAC allows you to define which drivers are permitted to load on your machines. A well-configured WDAC policy is more effective than any blocklist because it blocks everything except what you explicitly allow.
  • Monitor for driver load events, not just known-bad drivers. Alert on any new driver load that occurs outside your baseline. If a machine suddenly loads a driver from a vendor you do not use, that is suspicious regardless of whether it is on a blocklist.
  • Assume your EDR can be blinded and build secondary visibility. ETW (Event Tracing for Windows) providers, Sysmon, and Windows Event Logs provide independent data sources. If your EDR goes blind, these sources may still capture evidence. But note: ETW providers can also be disabled from kernel mode, so this is a layer, not a guarantee.
  • Test your EDR against BYOVD techniques. Run the attack chain we described against your own endpoints in a controlled test. See what your EDR catches and what it misses. The results are usually sobering.
  • Map your detection coverage to MITRE ATT&CK. Specifically look at T1068 (Exploitation for Privilege Escalation), T1569 (System Services), and T1543 (Create or Modify System Process). If your detection rules for these techniques rely solely on kernel callbacks, they will fail during a BYOVD attack.

The uncomfortable truth

The BYOVD problem is not new. Vulnerable driver abuse has been documented since at least 2018. Kernel callback manipulation has been discussed in offensive security circles for years. The techniques we presented at rootedPT are built on publicly known vulnerabilities and publicly documented kernel internals.

What makes this persistent is that the fundamental architecture has not changed. EDRs still rely on kernel callbacks stored in writable arrays. The mitigation (KDP) exists but is not deployed at scale. The defense (HVCI) works when properly configured but often is not. The band-aid (driver blocklists) is always reactive.

For defenders, the message is straightforward: your EDR's kernel-level visibility is not guaranteed. It depends on data structures that can be silently modified by anyone who can load a driver on your machine. Test that assumption regularly. Build detection layers that do not all depend on the same fragile kernel infrastructure. And when you evaluate EDR products, ask specifically how they handle callback removal, not just how they detect known malware.

If you want to find out whether your EDR survives this attack, we can test it for you. We run the full BYOVD chain, callback zeroing included, against your actual endpoints and show you exactly what your EDR sees and what it misses. Get in touch.