Kernel-mode driver
What this page is
The kernel-mode minifilter driver (edr-windows-km.sys) that
provides filesystem interception, process creation callbacks,
registry monitoring, and EDR process protection from kernel space.
This page describes the driver architecture, the IOCTL interface
between user-mode and kernel-mode, and the build requirements.
Why it exists this way
A user-mode EDR sensor can be terminated by any process with SYSTEM privileges or by kernel-mode malware. The kernel-mode driver operates at a higher trust level than user-mode processes and can:
- Intercept file operations before they reach the filesystem stack (minifilter at altitude 385000 in the Anti-Virus range).
- Receive process creation notifications synchronously via
PsSetCreateProcessNotifyRoutineEx, which can block or terminate process creation before the process initialises. - Protect the EDR user-mode process from handle manipulation via
ObRegisterCallbackswith a pre-operation callback that stripsPROCESS_TERMINATEandPROCESS_VM_WRITEfrom non-whitelisted handle open requests. - Monitor registry persistence keys via
CmRegisterCallbackwith synchronous pre-operation notification, which can block suspicious registry writes before they are committed.
Architecture
User-mode Kernel-mode
--------- -----------
edr-windows.exe edr-windows-km.sys
│ │
├─ DeviceIoControl ──────IOCTL─────→ IO_STACK_LOCATION handler
│ │
│ ┌──┤
│ │ ├── FltRegisterFilter (minifilter)
│ │ ├── PsSetCreateProcessNotifyRoutineEx
│ │ ├── CmRegisterCallback (registry)
│ │ └── ObRegisterCallbacks (object)
│ │
│ ├── Minifilter callbacks:
│ │ - IRP_MJ_CREATE (pre-operation)
│ │ - IRP_MJ_WRITE (pre-operation)
│ │ - IRP_MJ_CLEANUP (post-operation)
│ │
└─ gRPC stream ──────────→ Collector (not shown)
IOCTL interface
The user-mode service communicates with the driver via
DeviceIoControl on \\\\.\\AuroraEDRKM. Four IOCTL codes are
defined:
| IOCTL | Code | Direction | Purpose |
|---|---|---|---|
IOCTL_QUERY_CALLBACK_HEALTH | 0x8000_2200 | In/Out | Returns CallbackHealthReport with status of all 4 callbacks |
IOCTL_QUERY_MINIFILTER_STATUS | 0x8000_2204 | In/Out | Returns instance count, altitude, and filter status |
IOCTL_START_EVENT_STREAM | 0x8000_2208 | In/Out | Opens a token-based inline event stream from kernel to user mode |
IOCTL_STOP_EVENT_STREAM | 0x8000_220C | In/Out | Closes the event stream identified by token |
Callback health report
The IOCTL_QUERY_CALLBACK_HEALTH response tells the user-mode
service whether each kernel callback is still registered:
pub struct CallbackHealthReport {
pub process_notify_count: u32,
pub process_notify_health: CallbackHealth,
pub registry_callback_health: CallbackHealth,
pub object_callback_health: CallbackHealth,
pub minifilter_active: bool,
}
The user-mode guard module polls this IOCTL every 30 seconds. If
any callback returns NotRegistered, the guard escalates to
critical alert because this indicates a kernel-mode tamper
attempt.
Token-based event streaming
Tokens are 128-bit UUIDs generated by the driver at
IOCTL_START_EVENT_STREAM time. The user-mode service maps each
token to an overlapped I/O completion port. When a kernel callback
fires, the driver writes a serialised event header into the token's
output buffer and signals the completion port. This avoids
user-mode polling and keeps event latency under 1 millisecond.
Minifilter configuration
The minifilter is registered at altitude 385000, which places it
in the middle of the Anti-Virus range (380000-389999) per
Microsoft's minifilter altitude allocation. At this altitude,
the filter runs after the filesystem driver and before user-mode
applications see the operation.
Monitored extensions: .exe, .dll, .sys, .bat, .ps1,
.vbs, .js, .vba, .hta, .scr, .cmd, .msi
Sensitive paths: System32, SysWOW64, Program Files,
Program Files (x86), Start Menu\Programs\Startup
For each monitored operation, the minifilter's pre-operation callback inspects the file name extension and path. If both match, the event is written to all active event stream tokens. If no tokens are active, the event is dropped (zero-allocation fast path).
Build requirements
The driver requires the Windows Driver Kit (WDK) and cannot be cross-compiled from Linux or macOS. The build environment is:
- Host OS: Windows 10/11 (x86_64) or Windows Server 2019+
- SDK: Windows 10/11 SDK 10.0.22621 or later
- WDK: Windows Driver Kit for the matching SDK version
- Rust: nightly-2024-12-15 with
rust-srccomponent - Cargo:
cargo-wdksubcommand or manual WDK invocation viacargo build --target x86_64-pc-windows-msvc
The crate is a standalone workspace (not a member of the root
workspace) for the same reasons as crates/edr-linux-ebpf: the
no_std + custom-target constraints differ enough from the host
workspace that a unified workspace would force wrong defaults.
What goes wrong
FltRegisterFilterfails because another minifilter already occupies altitude 385000. The driver falls back to the next available altitude in the 380000-389999 range.PsSetCreateProcessNotifyRoutineExfails withSTATUS_ACCESS_DENIEDif the driver is not signed with a Microsoft cross-signing certificate. This is enforced on Windows 10 1607+ with Secure Boot enabled.ObRegisterCallbacksfails withSTATUS_ACCESS_DENIEDfor the same signing requirement. The driver starts inPartialProtectionmode if either process or object callbacks fail to register.CmRegisterCallbackis deprecated in Windows 11 24H2. The driver detects the OS version at load time and uses the replacement API (CmRegisterCallbackExwith theREG_CALLBACK_FLAG_ALTITUDEflag) when available.