NtDelayExecution
Suspend le thread appelant pendant un intervalle donné, éventuellement en état alertable.
Prototype
NTSTATUS NtDelayExecution( BOOLEAN Alertable, PLARGE_INTEGER DelayInterval );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| Alertable | BOOLEAN | in | Si TRUE, l'attente peut être interrompue par une APC en file ; si FALSE, le thread dort de façon non alertable. |
| DelayInterval | PLARGE_INTEGER | in | Unités de 100 ns. Négatif = délai relatif (le plus courant) ; positif = temps UTC absolu. |
IDs de syscalls par version de Windows
| Version 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 |
Module noyau
APIs liées
Stub du 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
Notes non documentées
Les API Win32 `Sleep` et `SleepEx` sont de fines enveloppes autour de `NtDelayExecution`. Le SSN est resté `0x34` sur toutes les builds Win10/11 publiées, ce qui en fait l'un des numéros de syscall les plus prévisibles côté user-mode. Côté noyau, le handler passe par `KeDelayExecutionThread` et bloque finalement sur le dispatcher de timers. Un appel avec un intervalle relatif nul ou NULL cède le reste du quantum — équivalent à `SwitchToThread()`.
Usage courant par les malwares
Primitive canonique d'évasion temporelle / anti-sandbox (MITRE T1497.003). De longs sleeps non alertables (5–30 min) servent à dépasser la fenêtre d'analyse des sandboxes automatisées qui n'observent un échantillon que 1 à 2 minutes. Les frameworks modernes de sleep mask (Ekko, Foliage, Cronos, Sleep Mask Kit de Cobalt Strike, Brute Ratel C4) encadrent `NtDelayExecution` entre un chiffrement et un déchiffrement : le `.text` et le heap du beacon sont chiffrés sur place, on appelle `NtDelayExecution`, puis on déchiffre au réveil — un scan mémoire pendant le sommeil ne voit que du chiffré. Associé à un `ntdll!NtDelayExecution` patché ou un syscall direct/indirect, l'appel échappe aussi aux hooks EDR user-mode pendant la dormance.
Opportunités de détection
Détection difficile car `Sleep` est universel. Télémétrie comportementale utile : sleeps anormalement longs depuis des processus non interactifs ; boucles `NtQuerySystemTime` → `NtDelayExecution` (vérification d'accélération sandbox) ; snapshots de pile de thread où l'adresse de retour pointe dans une zone RWX/privée plutôt que dans un module chargé (signature d'un beacon endormi). Les fournisseurs ETW `Microsoft-Windows-Kernel-Process` et Threat Intelligence exposent les raisons d'attente. Les EDR scannent de plus en plus la mémoire RX privée à intervalle régulier pour attraper les implants sleep-maskés entre chiffrement et déchiffrement.
Exemples de syscalls directs
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 _); }
}Mappings MITRE ATT&CK
Last verified: 2026-05-20