NtUnloadDriver
Descarga un driver en modo kernel previamente cargado — la primitiva de limpieza BYOVD.
Prototipo
NTSTATUS NtUnloadDriver( 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 | 0x1A9 | win10-1507 |
| Win10 1607 | 0x1B2 | win10-1607 |
| Win10 1703 | 0x1B8 | win10-1703 |
| Win10 1709 | 0x1BC | win10-1709 |
| Win10 1803 | 0x1BE | win10-1803 |
| Win10 1809 | 0x1BF | win10-1809 |
| Win10 1903 | 0x1C0 | win10-1903 |
| Win10 1909 | 0x1C0 | win10-1909 |
| Win10 2004 | 0x1C6 | win10-2004 |
| Win10 20H2 | 0x1C6 | win10-20h2 |
| Win10 21H1 | 0x1C6 | win10-21h1 |
| Win10 21H2 | 0x1C8 | win10-21h2 |
| Win10 22H2 | 0x1C8 | win10-22h2 |
| Win11 21H2 | 0x1D2 | win11-21h2 |
| Win11 22H2 | 0x1D6 | win11-22h2 |
| Win11 23H2 | 0x1D6 | win11-23h2 |
| Win11 24H2 | 0x1D9 | win11-24h2 |
| Server 2016 | 0x1B2 | winserver-2016 |
| Server 2019 | 0x1BF | winserver-2019 |
| Server 2022 | 0x1CE | winserver-2022 |
| Server 2025 | 0x1D9 | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 D9 01 00 00 mov eax, 0x1D9 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
NtUnloadDriver es la contraparte de NtLoadDriver. Dada una ruta NT a una clave de servicio, pide al I/O manager invocar la rutina `DriverUnload` del driver y retirar su `DRIVER_OBJECT` de `\Driver`. La imagen del driver se desmapea del espacio kernel y se rellena el slot `MmUnloadedDrivers` correspondiente (el kernel mantiene un pequeño ring buffer con los ~50 últimos nombres de drivers descargados — `!drvobj /unloaded` en WinDbg). NtUnloadDriver requiere que el token llamante tenga `SeLoadDriverPrivilege`, que los Administradores tienen por defecto pero las cuentas de servicio raramente poseen.
Uso común por malware
La llamada definitoria de la **limpieza BYOVD (Bring Your Own Vulnerable Driver)**. La kill chain clásica: 1) soltar un driver firmado-pero-vulnerable de una lista codificada (RTCore64, gdrv, mhyprot2, kArmor, etc.); 2) NtLoadDriver para crear el servicio y cargar; 3) IOCTL al driver para deshabilitar callbacks EDR, terminar procesos protegidos, escribir en MSRs o memoria kernel arbitraria; 4) NtUnloadDriver para retirar el driver del sistema vivo y reducir la huella forense. Familias: EDRKillShifter (herramientas afiliadas a RansomHub), Terminator de Spyboy, módulo BYOVD de AvosLocker, variantes KillAV, abuso de RTCore64 por BlackByte, loader del rootkit FudModule de Lazarus. El unload es *necesario* — dejar el driver vulnerable cargado es una señal de detección estridente para enumeración de imágenes kernel.
Oportunidades de detección
Crítico: la mayoría de la telemetría BYOVD pública se centra en el *load* (`Microsoft-Windows-Kernel-PnP` event 400, Sysmon Event ID 6) — el *unload* ha estado históricamente subinstrumentado. La detección depende de correlación: Sysmon Event 6 registró la carga inicial, y la ausencia de la imagen del driver en enumeraciones posteriores `Get-WindowsDriver` / `driverquery` / callbacks kernel es el único delta. `Microsoft-Windows-Kernel-PnP` event 410 dispara al parar el servicio. La blocklist de drivers vulnerables de Microsoft Defender (`DriverSiPolicy.p7b`) impide enteramente el load en sistemas habilitados — cuando está presente, la ruta BYOVD falla en NtLoadDriver y NtUnloadDriver nunca se alcanza. ELAM y HVCI también muerden en el load, no en el unload.
Ejemplos de syscalls directos
asmx64 direct stub
; Direct syscall stub for NtUnloadDriver (SSN 0x1D9 on Win11 24H2)
NtUnloadDriver PROC
mov r10, rcx ; DriverServiceName
mov eax, 1D9h ; SSN — verify per-build
syscall
ret
NtUnloadDriver ENDPcBYOVD cleanup after exploitation
// Final stage of a BYOVD chain: tear down the vulnerable driver and its service key.
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI *pNtUnloadDriver)(PUNICODE_STRING);
BOOL BYOVDCleanup(PCWSTR ntServicePath /* L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Vuln" */) {
UNICODE_STRING us;
RtlInitUnicodeString(&us, ntServicePath);
pNtUnloadDriver fn = (pNtUnloadDriver)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtUnloadDriver");
NTSTATUS s = fn(&us);
// Best practice: also delete the service-key and the .sys on disk to slim forensics.
// (omitted) RegDeleteTreeW + DeleteFileW
return s >= 0;
}rustEDR-kill teardown
// Cargo: ntapi = "0.4", widestring = "1"
use ntapi::ntioapi::NtUnloadDriver;
use ntapi::ntrtl::RtlInitUnicodeString;
use winapi::shared::ntdef::UNICODE_STRING;
use widestring::U16CString;
unsafe fn unload(svc_nt_path: &str) -> i32 {
let w = U16CString::from_str(svc_nt_path).unwrap();
let mut us: UNICODE_STRING = std::mem::zeroed();
RtlInitUnicodeString(&mut us, w.as_ptr());
NtUnloadDriver(&mut us)
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20