NtDelayExecution
Suspende el hilo llamante durante un intervalo dado, opcionalmente en estado alertable.
Prototipo
NTSTATUS NtDelayExecution( BOOLEAN Alertable, PLARGE_INTEGER DelayInterval );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| Alertable | BOOLEAN | in | Si TRUE, la espera puede interrumpirse por una APC encolada; si FALSE, el hilo duerme de forma no alertable. |
| DelayInterval | PLARGE_INTEGER | in | Unidades de 100 ns. Negativo = retardo relativo (lo más común); positivo = tiempo UTC absoluto. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x34 | win10-1507 |
| Win10 1607 | 0x34 | win10-1607 |
| Win10 1703 | 0x34 | win10-1703 |
| Win10 1709 | 0x34 | win10-1709 |
| Win10 1803 | 0x34 | win10-1803 |
| Win10 1809 | 0x34 | win10-1809 |
| Win10 1903 | 0x34 | win10-1903 |
| Win10 1909 | 0x34 | win10-1909 |
| Win10 2004 | 0x34 | win10-2004 |
| Win10 20H2 | 0x34 | win10-20h2 |
| Win10 21H1 | 0x34 | win10-21h1 |
| Win10 21H2 | 0x34 | win10-21h2 |
| Win10 22H2 | 0x34 | win10-22h2 |
| Win11 21H2 | 0x34 | win11-21h2 |
| Win11 22H2 | 0x34 | win11-22h2 |
| Win11 23H2 | 0x34 | win11-23h2 |
| Win11 24H2 | 0x34 | win11-24h2 |
| Server 2016 | 0x34 | winserver-2016 |
| Server 2019 | 0x34 | winserver-2019 |
| Server 2022 | 0x34 | winserver-2022 |
| Server 2025 | 0x34 | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 34 00 00 00 mov eax, 0x34 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
Las APIs Win32 `Sleep` y `SleepEx` son envoltorios delgados sobre `NtDelayExecution`. El SSN ha sido `0x34` en cada build publicado de Win10/11, siendo uno de los más predecibles en user-mode. El kernel encamina la llamada por `KeDelayExecutionThread` y termina bloqueando en el dispatcher de timers. Una llamada con intervalo relativo cero o NULL cede el resto del quantum — equivalente a `SwitchToThread()`.
Uso común por malware
Primitiva canónica de evasión por tiempo / anti-sandbox (MITRE T1497.003). Sleeps largos no alertables (5–30 min) sirven para superar la ventana de análisis de sandboxes automáticos que sólo observan una muestra 1–2 minutos. Frameworks modernos de sleep mask (Ekko, Foliage, Cronos, Sleep Mask Kit de Cobalt Strike, Brute Ratel C4) encierran `NtDelayExecution` entre cifrado y descifrado: el `.text` y el heap del beacon se cifran in-place, se llama a `NtDelayExecution`, y al despertar se descifra — un escaneo de memoria durante el sueño sólo encuentra cifrado. Combinado con un `ntdll!NtDelayExecution` no hookeado o syscall directo/indirecto, evade los hooks de EDR en user-mode durante la dormancia.
Oportunidades de detección
La detección es difícil porque `Sleep` es universal. Telemetría útil: sleeps anormalmente largos desde procesos no interactivos; bucles `NtQuerySystemTime` → `NtDelayExecution` (chequeo de aceleración de sandbox); snapshots de pila de hilo donde la dirección de retorno apunta a memoria RWX/privada en vez de a un módulo cargado (firma de beacon dormido). El proveedor ETW `Microsoft-Windows-Kernel-Process` y Threat Intelligence exponen razones de espera. Los EDRs escanean cada vez más la memoria RX privada periódicamente para detectar implantes con sleep mask entre cifrado y descifrado.
Ejemplos de syscalls directos
asmx64 direct stub
; Direct syscall stub for NtDelayExecution (SSN 0x34, all Win10/11 builds)
NtDelayExecution PROC
mov r10, rcx ; syscall convention
mov eax, 34h ; SSN
syscall
ret
NtDelayExecution ENDPcSandbox time-skip detector
// Sleep 10 s of wall time, verify the kernel actually slept that long.
// Sandboxes that patch Sleep / accelerate timers will return early.
#include <windows.h>
#include <winternl.h>
extern "C" NTSTATUS NTAPI NtDelayExecution(BOOLEAN, PLARGE_INTEGER);
extern "C" NTSTATUS NTAPI NtQuerySystemTime(PLARGE_INTEGER);
BOOL LooksLikeSandbox(void) {
LARGE_INTEGER t0, t1, delay;
NtQuerySystemTime(&t0);
delay.QuadPart = -10LL * 10000000LL; // 10 s, relative
NtDelayExecution(FALSE, &delay);
NtQuerySystemTime(&t1);
LONGLONG elapsedSec = (t1.QuadPart - t0.QuadPart) / 10000000LL;
return elapsedSec < 9; // sandbox skipped the sleep
}rustNaked sleep stub (windows-sys)
// Cargo: windows-sys = "0.59"
use std::arch::asm;
#[unsafe(naked)]
unsafe extern "system" fn nt_delay_execution(
_alertable: u8,
_interval: *const i64,
) -> i32 {
asm!(
"mov r10, rcx",
"mov eax, 0x34",
"syscall",
"ret",
options(noreturn),
);
}
pub fn sleep_seconds(s: i64) {
// 100 ns units, negative = relative
let interval: i64 = -s * 10_000_000;
unsafe { nt_delay_execution(0, &interval as *const _); }
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20