NtLoadDriver
Carga un driver en modo kernel desde una entrada de servicio descrita en el registro — entrada BYOVD.
Prototipo
NTSTATUS NtLoadDriver( PUNICODE_STRING DriverServiceName );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| DriverServiceName | PUNICODE_STRING | in | Ruta NT a la clave de servicio bajo \Registry\Machine\System\CurrentControlSet\Services\<Name>. El invocador debe tener SeLoadDriverPrivilege. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0xF5 | win10-1507 |
| Win10 1607 | 0xF9 | win10-1607 |
| Win10 1703 | 0xFC | win10-1703 |
| Win10 1709 | 0xFD | win10-1709 |
| Win10 1803 | 0xFE | win10-1803 |
| Win10 1809 | 0xFF | win10-1809 |
| Win10 1903 | 0x100 | win10-1903 |
| Win10 1909 | 0x100 | win10-1909 |
| Win10 2004 | 0x105 | win10-2004 |
| Win10 20H2 | 0x105 | win10-20h2 |
| Win10 21H1 | 0x105 | win10-21h1 |
| Win10 21H2 | 0x106 | win10-21h2 |
| Win10 22H2 | 0x106 | win10-22h2 |
| Win11 21H2 | 0x10B | win11-21h2 |
| Win11 22H2 | 0x10C | win11-22h2 |
| Win11 23H2 | 0x10C | win11-23h2 |
| Win11 24H2 | 0x10E | win11-24h2 |
| Server 2016 | 0xF9 | winserver-2016 |
| Server 2019 | 0xFF | winserver-2019 |
| Server 2022 | 0x10A | winserver-2022 |
| Server 2025 | 0x10E | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 0E 01 00 00 mov eax, 0x10E F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Notas no documentadas
Syscall de un solo argumento que toma la **ruta NT de registro** a la clave de servicio del driver — *no* una ruta filesystem al .sys. El kernel lee `ImagePath`, `Type`, `Start`, `Group` y otras entradas de esa clave, carga la imagen con `MmLoadSystemImage`, llama a `DriverEntry` y luego inserta el driver en `PsLoadedModuleList`. El invocador debe tener `SeLoadDriverPrivilege` (por defecto: Administrators, Print Operators) y el privilegio debe estar **habilitado** en el token del thread — este es el único prerrequisito duro. El SSN deriva mucho entre builds (0xF5 → 0x10E), resolución dinámica obligatoria. Drivers cargados así lo son en *demand-start* (Type=3), lo cual aparece distinto en telemetría PnP frente a servicios `Start=2` (auto) que el SCM monta al arranque.
Uso común por malware
Puerta de entrada **BYOVD (Bring Your Own Vulnerable Driver)** — el syscall al que toda ataque a nivel kernel en Windows moderno termina convergiendo. Cadena completa: (1) soltar el driver vulnerable pero firmado por Microsoft (RTCore64.sys, gdrv.sys, mhyprot2.sys, dbutil_2_3.sys, procexp152.sys, kprocesshacker.sys, etc.) a disco; (2) crear la clave de servicio bajo `\Registry\Machine\System\CurrentControlSet\Services\<RandomName>` con `ImagePath=\??\C:\path\drv.sys`, `Type=1`, `Start=3` (con NtSetValueKey directo o por `OpenSCManager`+`CreateServiceW` que es más ruidoso); (3) habilitar `SeLoadDriverPrivilege` vía `NtAdjustPrivilegesToken`; (4) llamar NtLoadDriver con la ruta a la clave de servicio; (5) abrir `\\.\<DeviceName>` y enviar IOCTLs vía `NtDeviceIoControlFile`. El mismo syscall se abusa también para **instalación de rootkit (T1014)** — Necurs, FU, TDL3/4 y GrayFish del Equation Group cargaron sus componentes kernel por esta vía. La persistencia legítima vía driver de servicio (T1543.003) usa la misma llamada pero a través del SCM.
Oportunidades de detección
**Telemetría de carga de driver de máxima fidelidad en Windows**, y el syscall más vigilado por los EDR. Fuentes: (a) **Microsoft-Windows-Kernel-PnP** ETW (`{9C205A39-1250-487D-ABD7-E831C6290539}`) emite evento de carga con ruta imagen, detalles de firma y proceso cargador; (b) **Sysmon Event ID 6 (DriverLoad)** — imagen, hash, estado firmado/no firmado, firmante; (c) **Security Event 4673/4674** cuando se ejerce `SeLoadDriverPrivilege`; (d) **PsSetLoadImageNotifyRoutine** se dispara para *cada* carga de imagen incluyendo drivers y alimenta a todo EDR moderno. La Vulnerable Driver Blocklist de Microsoft (`HVCI` + policy Code Integrity `SiPolicy.p7b`, activa por defecto desde Windows 11 22H2 y Server 2022) bloquea por hash las familias BYOVD públicas antes del load; LOLDrivers.io mantiene la lista comunitaria. Cazas: cualquier llamada NtLoadDriver por un proceso vivo < 60 s; cualquier driver cargado desde fuera de `%SystemRoot%\System32\drivers`; cualquier driver cuyo firmante esté en LOLDrivers; cualquier clave de servicio creada < 5 minutos antes de NtLoadDriver. La regla ASR de Microsoft Defender `Block abuse of exploited vulnerable signed drivers` (D1E49AAC-8F56-4280-B9BA-993A6D77406C) bloquea activamente el catálogo.
Ejemplos de syscalls directos
cBYOVD load chain: privilege + service key + NtLoadDriver
// 1) Caller has already enabled SeLoadDriverPrivilege in its token.
// 2) Service registry entry has been written under HKLM\SYSTEM\CurrentControlSet\Services\Evil:
// ImagePath = \??\C:\Users\Public\rtcore64.sys
// Type = 1 (kernel driver)
// Start = 3 (demand)
// ErrorCtrl = 1
UNICODE_STRING svc;
RtlInitUnicodeString(&svc,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Evil");
NTSTATUS s = NtLoadDriver(&svc);
// On success the driver's DriverEntry has run and \\.\RTCore64 is now openable.asmDirect stub (SSN 0x10E, Win11 24H2)
; SSN heavily build-dependent. Always resolve dynamically for portable code.
NtLoadDriver PROC
mov r10, rcx
mov eax, 10Eh ; Win11 24H2
syscall
ret
NtLoadDriver ENDPrustEnd-to-end BYOVD helper (privilege+key+load)
// Cargo: ntapi = "0.4", winapi = { version = "0.3", features = ["ntdef","ntsecapi"] }
use ntapi::ntpsapi::NtCurrentProcess;
use ntapi::ntrtl::RtlInitUnicodeString;
use winapi::shared::ntdef::{HANDLE, UNICODE_STRING};
extern "system" {
fn NtLoadDriver(name: *mut UNICODE_STRING) -> i32;
fn NtOpenProcessToken(p: HANDLE, da: u32, t: *mut HANDLE) -> i32;
fn NtAdjustPrivilegesToken(
t: HANDLE, dis: u8, n: *mut u8, len: u32,
prev: *mut u8, ret: *mut u32) -> i32;
}
pub unsafe fn byovd_load(service_key_nt_path: *const u16) -> i32 {
// 1) enable SeLoadDriverPrivilege in our own token (token plumbing omitted)
// 2) issue the load
let mut us: UNICODE_STRING = core::mem::zeroed();
RtlInitUnicodeString(&mut us, service_key_nt_path);
NtLoadDriver(&mut us)
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20