BYOVD: Detection and Prevention on a Hardened Windows 11 Endpoint
Written by 0xM4L · OFFCEPT Security Research
We tested BYOVD against a fully hardened Windows 11 25H2 endpoint with MDE and ASR in Block mode. Seven of twenty-one publicly known drivers were caught on write. Nine loaded cleanly into the kernel and killed every PPL-protected Defender process. Blocklist coverage lags public disclosure by months. WDAC allow-listing and behavioural detection are the only controls that close the gap.
Bring Your Own Vulnerable Driver (BYOVD) is one of the most reliable ways attackers still get code running in the Windows kernel in 2026. Instead of finding a fresh kernel exploit, the attacker brings a legitimately signed but vulnerable driver, loads it, and abuses its exposed functionality (usually via IOCTLs) to read and write kernel memory, terminate protected processes, or blind and disable the endpoint's own defences.
This post walks through a hands-on lab where we tested BYOVD against a fully managed, EDR-protected Windows 11 machine, and shows that even with the "Block Vulnerable Drivers" controls turned on, a meaningful number of known-vulnerable drivers still load and run. We finish with a set of mitigations and defences that actually move the needle.
Disclaimer. This research was performed in an isolated lab we own and control, for defensive purposes. Do not run vulnerable drivers or EDR-killing tooling against systems you are not authorised to test.
Methodology
We group vulnerable drivers into three categories based on how "known" they are to defenders:
- Category 1: Blacklisted + public PoC. Drivers already on public blocklists (Microsoft's Vulnerable Driver Blocklist, LOLDrivers) with a ready-to-use exploit published.
- Category 2: Blacklisted, no public PoC. Drivers listed as vulnerable but with no turnkey exploit; you have to write your own.
- Category 3: Unlisted / unknown. Vulnerable drivers not yet catalogued anywhere. Zero-days: unknown vulnerability, unknown driver, no blocklist entry.
For this lab we used a fork of BlackSnufkin's BYOVD project, a Rust PoC that abuses driver IOCTLs to terminate arbitrary processes by PID from kernel mode, shipping ~21 vulnerable drivers (Category 1), plus one additional driver from LOLDrivers with no public exploit (Category 2), and drivers discovered in third-party Windows software not yet listed anywhere, for which we developed a PoC abusing a zero-day (Category 3). This article focuses on Category 1, because that is the case defenders should be best equipped to stop, and, as we'll see, often aren't.
Lab Setup
| Component | Detail |
|---|---|
| OS | Windows 11 25H2 |
| EDR | Microsoft Defender for Endpoint (MDE) |
| Management | Enrolled via Microsoft Intune |
| Policies | Attack Surface Reduction (ASR), Block Abuse of Exploited Vulnerable Signed Drivers |
The endpoint is onboarded to MDE and enrolled via Intune → Endpoint security → Attack surface reduction, using the Attack Surface Reduction Rules profile. The specific rule is "Block abuse of exploited vulnerable signed drivers", set to Block. This is exactly the configuration a security-conscious organisation would deploy: a modern OS, a top-tier EDR, MDM management, and the vendor's own anti-BYOVD control switched on.

Part 1: On-Write Detection
We copied all 21 BlackSnufkin drivers to disk under C:\drivers\BYOVD-main\. 7 drivers were flagged and quarantined by MDE the moment they touched disk, detected as VulnerableDriver:* (and one as Trojan:Win64/KillAV):
- Ksapi64-Killer
- NSec-Killer
- PoisonX-Killer
- STProcessMonitor-Killer
- TfSysMon-Killer
- UnknownKiller
- Viragt64-Killer

The remaining 14 drivers stayed on disk; Defender did not flag them on write: AppRemover-Killer, Astra64-RW, BdApiUtil-Killer, CcProtect-Killer, EnPortv-Killer, GameDriverX64-Killer, GoFlyDrv-Killer, HWAudioOs2Ec-Killer, K7Terminator, MonProcessEX-Killer, PCTcore64-Killer, Wsftprm-Killer, Xhunter1-Killer, Xkpsm-Killer.
Takeaway: on-write/on-access detection only caught a third of a publicly known, Category 1 driver set. The rest depend on load-time controls, which we test next.
Part 2: Registering and Starting the Drivers
A kernel driver becomes interesting only once it is registered as a service and started. We used a small PowerShell wrapper around sc.exe to register every .sys as a kernel-mode, demand-start service. Every service was created successfully; registering the service and writing the HKLM\SYSTEM\CurrentControlSet\Services\<name> key is not itself blocked.
#Requires -RunAsAdministrator
$SC = "$env:SystemRoot\System32\sc.exe"
$DriverRoot = 'C:\drivers\BYOVD-main'
Get-ChildItem -LiteralPath $DriverRoot -Filter *.sys -Recurse -File -Force |
ForEach-Object {
$name = $_.BaseName
$key = "HKLM:\SYSTEM\CurrentControlSet\Services\$name"
if (Test-Path $key) {
& $SC delete $name 2>&1 | Out-Null
Remove-Item $key -Recurse -Force -ErrorAction SilentlyContinue
}
$out = & $SC create $name binPath= $_.FullName type= kernel start= demand
if ($LASTEXITCODE -eq 0) {
Write-Host "[+] $name -> $($_.FullName)" -ForegroundColor Cyan
} else {
Write-Warning "[-] $name : $($out -join ' ')"
}
}
The interesting part is sc start, which triggers the actual kernel load. Here the layered protections finally kick in, but only for some drivers:
| Result | Meaning | Which control stopped it |
|---|---|---|
RUNNING | Driver loaded into the kernel | Nothing stopped it |
FAILED 577: Windows cannot verify the digital signature | Blocked by code-integrity / signature enforcement | DSE / WHQL |
FAILED 0x800B010C: certificate explicitly revoked | Revoked signing cert | Cert revocation |
FAILED 5: Access is denied | Blocked by the Vulnerable Driver Blocklist / ASR | Blocklist |
FAILED 31 / 183 | Device/driver init error (not a security block) | n/a |
One driver even surfaced an explicit message: PCTcore64.sys: A security setting is detecting this as a vulnerable driver and blocking it from loading. You will need to adjust your settings to load this driver.
Despite every protection being enabled, 9 known-vulnerable drivers reached the RUNNING state and were live in the kernel: ardrv, ASTRA64, CcProtect, EnPortv, GoFly64, HWAudioOs2Ec_1, MonProcessEX, STProcessMonitor_v114, xkpsm. These are not obscure drivers. Most were catalogued on LOLDrivers months before this test. The oldest entry was published in March 2026; as of 8 August 2026, nearly five months later, they still load and run on a fully patched, policy-hardened machine. That is the core finding: blocklist coverage lags public disclosure, and the gap is measured in months.
Part 3: Abusing a Loaded Driver to Kill Defender
To show that a "still loads" driver is not a theoretical problem, we took one of the running drivers (CcProtect.sys) and used the BYOVD PoC to terminate protected processes from the kernel. The PoC opens the driver's device object (\\.\CcProtect) and sends IOCTL 0x222024 with a target process name; the driver, running in kernel mode, terminates the corresponding PID, bypassing the user-mode protection (PPL) that normally shields these processes.
We aimed it squarely at the entire Defender / MDE stack:
MsMpEng.exe- Defender Antivirus engineMpDefenderCoreService.exe- Defender core serviceSecurityHealthService.exeNisSrv.exe- Network inspection serviceMsSense.exe,SenseIR.exe,SenseTVM.exe,SenseNdr.exe- MDE sensor components

All of them run as Protected Processes (PsProtectedSignerAntimalware-Light / PsProtectedSignerWindows-Light). The kills succeed. A user-mode tool could never do this to a PPL-protected antimalware process, but a kernel driver can.
One caveat on persistence: Defender has self-healing. The antimalware services driven by MpDefenderCoreService eventually respawn. To keep the endpoint blind, an attacker must either continuously re-kill the processes or chain into full SYSTEM/kernel persistence to stop and remove the services outright. Either way, the point stands: BYOVD is a real, current threat to endpoint security, even with antimalware and EDR installed and configured.
Custom Detection Rules in Microsoft Defender XDR
Since the built-in blocklist misses unknown and still-signed drivers, we can close part of the gap with a custom detection rule in Microsoft Defender XDR that alerts on any new kernel-driver registration, vulnerable or not. A naive approach would watch for sc.exe create ... type= kernel, but that is trivially bypassed: an attacker can register the driver via the Service Control Manager API directly, via NtLoadDriver, or with indirect syscalls, none of which touch sc.exe. Filtering on suspicious file paths is just as weak.
The robust chokepoint is the registry. Every one of those techniques (including NtLoadDriver) still requires a service key under ...\CurrentControlSet\Services\<name> with an ImagePath pointing at the .sys. We detect that write on any path and apply a default-deny model: alert on every kernel-driver registration except those on a maintained known-good baseline.
// Alert on ANY new kernel-driver service registration, regardless of tool or path.
// The Services\<name>\ImagePath write is the invariant that sc.exe, the SCM API,
// and NtLoadDriver all funnel through.
DeviceRegistryEvents
| where ActionType in ("RegistryValueSet", "RegistryKeyCreated")
| where RegistryKey has @"\CurrentControlSet\Services\"
| where RegistryValueName == "ImagePath" and RegistryValueData endswith ".sys"
// default-deny: keep only what is NOT on your approved baseline.
// Maintain KnownGoodDrivers as a watchlist, ideally keyed on hash/signer rather than path.
| where RegistryValueData !in~ (KnownGoodDrivers)
| project Timestamp, DeviceName, RegistryKey, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessAccountNameSave it as a custom detection rule in Advanced Hunting; set frequency, severity, and the machine/file entities to map so a matching event raises an alert and, optionally, triggers an automated response. A registration pointing outside \Windows\System32\drivers\ is worth a higher severity.
Mitigations and Defences
No single control stops BYOVD; it takes defence-in-depth. Roughly in order of impact:
- 1. Enforce the Microsoft Vulnerable Driver Blocklist, and don't rely on it alone. Turn it on (it ships enabled with HVCI/SAC on modern Windows), but treat it as necessary-not-sufficient. As shown above, the blocklist trails public disclosure by months and missed 9 of 21 well-known drivers here.
- 2. Enable VBS, HVCI/Memory Integrity, and Device Guard. Hypervisor-Enforced Code Integrity (HVCI) runs kernel-mode code integrity inside a VBS-isolated environment and forces signature validation and certificate-revocation checks at load time. This is the single most valuable platform hardening step. Roll it out to a test ring first; incompatible drivers can cause boot failures.
- 3. Move to an allow-list with App Control for Business (WDAC). Instead of blocking known-bad, allow only known-good: whitelist specific driver publishers, signers, and hashes. This is the only approach that also addresses Category 3 (unknown/zero-day) drivers, because an unlisted driver is denied by default.
- 4. Enforce modern driver-signing requirements (EV / WHQL) and block legacy drivers. Many abused drivers are old, cross-signed binaries. Enforcing modern signing closes off a large slice of the Category 1/2 catalogue.
- 5. Restrict who can register and load drivers. Driver load requires local admin /
SeLoadDriverPrivilege. Enforce least privilege and JIT/JEA admin for IT staff. - 6. Audit and remove risky third-party drivers. Gaming anti-cheat, GPU/CPU overclocking utilities, and old vendor system-information tools are repeat offenders on production endpoints. Patch or ban software bundling known-vulnerable driver versions.
- 7. Keep signatures, engine, and blocklist current. Ensure MDE cloud-delivered protection and the driver blocklist auto-update. A stale blocklist is the exact failure mode illustrated above.
- 8. Keep ASR in Block mode. Use
Auditfirst to baseline, then move toBlockfor "Block abuse of exploited vulnerable signed drivers" and pair it with the broader ASR rule set. - 9. Monitor for behaviour, not just the binary. Sysmon Event ID 6 (driver load) correlating hash, signature status, and path; Event ID 7045 (new service installed); new kernel-service registry writes; WDAC block events (3023 / 3033). Alert on loads from user-writable paths (
\AppData\,\Temp\,\ProgramData\) and cross-reference hashes against loldrivers.io. - 10. Watch for the impact. Anomalous IOCTL requests to a freshly loaded device object, kernel-mode callback removal, and kernel-initiated termination of PPL/antimalware processes are high-fidelity signals a kill is already in progress. Configure immediate alerting on security-process termination and sensor heartbeat gaps in the MDE portal.
- 11. Validate continuously and plan for failure. Assume some driver will get through. Run BYOVD simulations and red-team exercises, isolate hosts showing suspicious kernel activity, and ensure backup and recovery plans account for a scenario where prevention failed.
Where Blocklists Fall Short
The consistent theme is that block-by-reputation is always reactive. A driver has to be discovered, analysed, catalogued, and pushed to the blocklist before it's stopped, and every day in that window it loads cleanly. Category 2 and 3 drivers never enter that window at all. Here is how each control fares against the hardest case: a validly signed but unknown (Category 3 / zero-day) vulnerable driver:
| Control | Stops a signed, unknown vulnerable driver? | Why |
|---|---|---|
| Vulnerable Driver Blocklist | No | Unknown driver is not on any list |
| HVCI / Memory Integrity | No | Validly signed, so it loads; data-only kernel read/write attacks never introduce new executable code for HVCI to catch |
| WDAC / App Control allow-list | Yes | Default-deny: a driver not explicitly approved never loads, known-bad or not |
| Behavioural driver-load detection | Detect | Flags the load of any non-baselined driver, plus anomalous IOCTL / callback-removal activity after load |
Signature and reputation controls are structurally blind to an unknown-but-signed driver. Only load-authorisation (allow-listing) and behavioural detection address it, and even allow-listing fails if the vulnerable driver is one you legitimately approved, leaving behavioural detection of the impact (unexpected PPL kills, callback removal, suspicious IOCTLs) as the last line.
That is why the more durable answer is to detect and control the act of loading a driver, vulnerable or not, rather than matching against a list of bad ones. In the next part of this series we'll dig into exactly that: customising EDR/XDR detection logic (Defender Advanced Hunting KQL and analogous Elastic queries) plus WDAC allow-listing, to surface any driver load event and decide on it by policy, closing the gap that signature- and blocklist-based BYOVD defences leave open.
References
- Microsoft: Enable memory integrity (HVCI / VBS)
- Microsoft: Strategies to monitor and prevent vulnerable driver attacks
- BlackSnufkin: BYOVD project
- LOLDrivers
- Check Point Research: Breaking Boundaries: Investigating Vulnerable Drivers and Mitigating Risks
- Cisco Talos: Exploring vulnerable Windows drivers
- CrowdStrike: Falcon Prevents Vulnerable Driver Attacks in a Real-World Intrusion