Overview
During my talk at Milan0day 2026, I presented a practical Windows kernel exploitation scenario based on the abuse of a vulnerable signed driver exposing arbitrary physical memory read/write primitives through MmMapIoSpace.
This post focuses specifically on the AV/EDR killing technique shown during the demo, explaining how a vulnerable driver can be leveraged to temporarily patch kernel structures and redirect execution flow inside the Windows kernel.
The Windows Trust Boundary
Modern Windows systems heavily rely on CPU-enforced privilege separation.
At the lowest privilege level, Ring 3 (User Mode) processes operate in isolated virtual address spaces and must rely on syscalls to request privileged operations from the kernel.
Ring 0 (Kernel Mode), instead, has unrestricted access to:
- physical memory
- kernel structures
- privileged CPU instructions
- process internals
- hardware interfaces

The moment an attacker gains arbitrary kernel read/write capabilities, most user-mode security boundaries become irrelevant.
IOCTL Dispatch Pipeline
Before proceeding we should spend some words on how user-land applications can communicate with drivers. That is done through IOCTL codes (this is not the only way but we don't care right now).

The flow is the following:
- The driver exposes a Symbolic Link which allows to make it usable from user-land processes.
- Your user-mode
MyApplication.exeopens a handle to the driver throughCreateFile(), thenDeviceIoControlis called. - An IRP request is created and sent straight to the driver.
- The driver receives the request with the IOCTL code and checks in its dispatcher what that IOCTL should do (switch-case operation).
- The driver performs the instructions based on the received IOCTL code and returns the output buffer to the user-land application.
Vulnerable Drivers and MmMapIoSpace Abuse
The exploitation chain demonstrated during the talk abuses a vulnerable signed driver exposing dangerous IOCTL handlers.
The vulnerable IOCTLs internally invoke MmMapIoSpace, allowing user-controlled physical addresses to be mapped into virtual memory.
For my talk, I used ThrottleStop.sys vulnerable driver (CVE-2025-7771) which exposes two dangerous IOCTL which are:
- 0x80006498: arbitrary physical memory read
- 0x8000649C: arbitrary physical memory write
At this point, the attacker owns one of the most powerful primitives available on Windows.

Why Physical Memory Matters
Processes operate on virtual addresses, while the CPU transparently translates them into physical addresses using page tables.
However, MmMapIoSpace operates on physical addresses, not virtual ones.
This means that before patching kernel structures or kernel code, an attacker must first resolve: Virtual Address -> Physical Address
Historically, page tables could be traversed directly, but since Windows 10 1803 Microsoft hardened this approach. Modern exploitation chains instead rely on techniques such as:
- Superfetch/SysMain information leakage
- PFN database abuse
- alternative VA-to-PA translation primitives
Once the physical address is known, arbitrary patching becomes possible.
Why AV/EDR Processes Are Not Special
Every running process is represented internally by an _EPROCESS structure, and this is still valid for AV/EDR processes.
This structure contains:
- process identifiers
- token information
- linked list pointers
- protection metadata
- execution state
An EDR agent still has an _EPROCESS, kernel callbacks, executable memory, and dispatch routines. If an attacker gains arbitrary kernel write access, those protections can be modified just like any other kernel object.
From Physical Memory R/W to Kernel Code Execution
At this stage, the vulnerable driver already provides arbitrary physical memory read/write primitives through MmMapIoSpace. This is the core capability that enables the entire attack chain.
Once the physical address of a kernel routine is known, the exploit can directly overwrite kernel code in memory. In the demo, the exploit translates the virtual address of NtAddAtom into its corresponding physical address and uses the vulnerable IOCTL 0x8000649C to overwrite its first instructions with a small trampoline shellcode.
Which means the attacker can:
- patch syscall handlers
- modify
_EPROCESSstructures - disable
PS_PROTECTION - alter security callbacks
- overwrite kernel function prologues
- restore original bytes afterward
The vulnerable driver is therefore not just a "helper" component, it is the mechanism that directly enables kernel code modification.
Temporary Kernel Patching
The technique demonstrated during the talk relies on temporary kernel patching. The goal is simple: hijack execution flow inside the kernel, execute a desired exported kernel routine, then restore original bytes immediately afterward. This minimizes instability and reduces the time window before PatchGuard reacts.
The syscall chosen for the demo was NtAddAtom. I chose it because it is directly callable from user mode through ntdll.dll and relatively uncommon in normal application workflows, making it a convenient syscall target for temporary kernel patching.
Phase 1 – Obtaining the Target EPROCESS
The first stage patches the beginning of NtAddAtom with a small trampoline redirecting execution toward PsLookupProcessByProcessId. The flow becomes:
This allows retrieval of the target process _EPROCESS pointer directly from kernel context. After the pointer is obtained, the original bytes of NtAddAtom are restored immediately. This restoration step is critical to avoid PatchGuard crashes.
Phase 2 – Killing the AV/EDR
Once the target _EPROCESS is known, the syscall is patched again. This time the trampoline redirects execution toward PsTerminateProcess:
The previously leaked _EPROCESS pointer is supplied as an argument. The result is that the AV/EDR process is terminated directly from kernel mode.
Here's a view of how inline hooking of NtAddAtom works.
PatchGuard Considerations
One extremely important detail is that the patch must remain temporary. Windows PatchGuard periodically validates critical kernel code regions. Leaving patched syscall stubs in memory for too long will eventually trigger CRITICAL_STRUCTURE_CORRUPTION and a kernel panic / BSOD.
For this reason, the exploit immediately restores original bytes after every invocation. The pattern is therefore: Patch -> Trigger -> Restore
This pattern is extremely flexible and can be reused against many exported kernel routines.
Demo Time
Beyond Process Termination
The same primitive can be extended far beyond AV/EDR killing. Examples include:
- removing object callbacks
- unregistering image load callbacks
- disabling telemetry hooks
- tampering with kernel callback arrays
- arbitrary kernel memory overwrite
- token stealing (LPE)
- Protected Process Light (PPL) removal
Final Thoughts
The most important takeaway from this research is simple: driver signing does not imply driver safety.
The vulnerable driver used during the demo was fully signed and loadable on modern Windows systems. The real problem is insufficient validation inside kernel IOCTL handlers exposing dangerous primitives such as:
- physical memory mapping
- arbitrary MSR access
- unrestricted port I/O
- arbitrary kernel writes
As long as signed vulnerable drivers remain available, Bring Your Own Vulnerable Driver (BYOVD) attacks will continue to be one of the most effective ways to bypass modern endpoint protections.
No comments:
Post a Comment