> Windows Syscalls
ntoskrnl.exeT1057T1106

NtGetNextProcess

Recorre la lista de procesos del kernel y devuelve un handle al siguiente proceso.

Prototipo

NTSTATUS NtGetNextProcess(
  HANDLE      ProcessHandle,
  ACCESS_MASK DesiredAccess,
  ULONG       HandleAttributes,
  ULONG       Flags,
  PHANDLE     NewProcessHandle
);

Argumentos

NameTypeDirDescription
ProcessHandleHANDLEinHandle al proceso anterior en la enumeración. NULL para empezar desde el inicio de la lista.
DesiredAccessACCESS_MASKinDerechos de acceso solicitados en el nuevo handle, p. ej. PROCESS_QUERY_LIMITED_INFORMATION.
HandleAttributesULONGinBanderas de atributos del handle como OBJ_INHERIT u OBJ_CASE_INSENSITIVE. Habitualmente 0.
FlagsULONGinReservado. Debe ser 0 en los builds actuales de Windows.
NewProcessHandlePHANDLEoutRecibe el handle del siguiente proceso. STATUS_NO_MORE_ENTRIES indica el final de la lista.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070xE8win10-1507
Win10 16070xEBwin10-1607
Win10 17030xEEwin10-1703
Win10 17090xEFwin10-1709
Win10 18030xF0win10-1803
Win10 18090xF1win10-1809
Win10 19030xF2win10-1903
Win10 19090xF2win10-1909
Win10 20040xF7win10-2004
Win10 20H20xF7win10-20h2
Win10 21H10xF7win10-21h1
Win10 21H20xF8win10-21h2
Win10 22H20xF8win10-22h2
Win11 21H20xFDwin11-21h2
Win11 22H20xFEwin11-22h2
Win11 23H20xFEwin11-23h2
Win11 24H20x100win11-24h2
Server 20160xEBwinserver-2016
Server 20190xF1winserver-2019
Server 20220xFCwinserver-2022
Server 20250x100winserver-2025

Módulo del kernel

ntoskrnl.exeNtGetNextProcess

APIs relacionadas

NtGetNextThreadNtQueryInformationProcessNtQuerySystemInformationCreateToolhelp32SnapshotEnumProcessesOpenProcess

Stub del syscall

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

NtGetNextProcess recorre la cadena EPROCESS anclada en PsActiveProcessHead y devuelve un handle nuevo a cada proceso por turnos. Pase NULL como ProcessHandle en la primera llamada; en las siguientes pase el handle devuelto previamente (la función lo cierra por usted solo en Windows 8+ cuando se devuelve STATUS_NO_MORE_ENTRIES — de lo contrario debe NtClose-arlo). La rutina hermana NtGetNextThread itera los ETHREAD dentro de un proceso dado. Ambas están exportadas por ntdll.dll pero no tienen un wrapper Win32 — `CreateToolhelp32Snapshot` y `EnumProcesses` siguen caminos completamente distintos (Toolhelp vía una sección de snapshot, PSAPI vía NtQuerySystemInformation(SystemProcessInformation)).

Uso común por malware

El tradecraft moderno consciente de OPSEC evita cada vez más Toolhelp32 y PSAPI porque están muy hookeados por los agentes user-mode de los EDR y producen eventos ETW ruidosos. Iterar PsActiveProcessHead vía NtGetNextProcess da al implante una lista de procesos sin llamar nunca a las API de enumeración obvias. Usos típicos: localizar lsass.exe antes de un volcado de credenciales, encontrar procesos EDR/AV para des-hookearlos o suplantarlos, escoger un host sacrificial para inyección, y evitar procesos de herramientas de análisis (Process Hacker, x64dbg) antes del desempaquetado. PoolParty y varios PoC públicos usan la pareja NtGetNextProcess + NtGetNextThread para enumerar hilos del thread pool sin tocar Toolhelp.

Oportunidades de detección

NtGetNextProcess tiene una huella legítima en user-mode muy escasa — el propio Windows raramente lo invoca fuera de un puñado de componentes del sistema. Un proceso user-mode que lo invoque repetidamente debe considerarse anómalo. El ETW Microsoft-Windows-Threat-Intelligence no expone este syscall directamente, pero las operaciones *posteriores* (NtOpenProcess contra lsass, ProcessAccess con PROCESS_VM_READ) están bien instrumentadas (Sysmon Event ID 10). Los EDR con callbacks de kernel que hookean PspInsertProcess no verán esta enumeración — solo el acceso que sigue. Busque binarios sin firmar que resuelvan `NtGetNextProcess` desde la tabla de exports de ntdll.

Ejemplos de syscalls directos

cLSASS PID discovery without Toolhelp

// Locate lsass.exe by walking PsActiveProcessHead via NtGetNextProcess.
// Avoids CreateToolhelp32Snapshot / EnumProcesses entirely.
#include <windows.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI *PNT_GET_NEXT_PROCESS)(HANDLE, ACCESS_MASK, ULONG, ULONG, PHANDLE);
typedef NTSTATUS (NTAPI *PNT_QUERY_INFO_PROCESS)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);

DWORD FindLsassPid(void) {
    HMODULE nt = GetModuleHandleA("ntdll.dll");
    PNT_GET_NEXT_PROCESS pNtGetNextProcess = (PNT_GET_NEXT_PROCESS)GetProcAddress(nt, "NtGetNextProcess");
    PNT_QUERY_INFO_PROCESS pNtQueryInformationProcess = (PNT_QUERY_INFO_PROCESS)GetProcAddress(nt, "NtQueryInformationProcess");

    HANDLE hProcess = NULL;
    HANDLE hNext = NULL;
    while (pNtGetNextProcess(hProcess, PROCESS_QUERY_LIMITED_INFORMATION, 0, 0, &hNext) == 0) {
        if (hProcess) CloseHandle(hProcess);
        hProcess = hNext;

        BYTE buf[1024];
        ULONG ret = 0;
        if (pNtQueryInformationProcess(hProcess, ProcessImageFileName, buf, sizeof(buf), &ret) == 0) {
            PUNICODE_STRING us = (PUNICODE_STRING)buf;
            if (us->Buffer && wcsstr(us->Buffer, L"lsass.exe")) {
                PROCESS_BASIC_INFORMATION pbi = {0};
                pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &ret);
                CloseHandle(hProcess);
                return (DWORD)(ULONG_PTR)pbi.UniqueProcessId;
            }
        }
    }
    if (hProcess) CloseHandle(hProcess);
    return 0;
}

asmx64 direct stub (Win11 24H2, SSN 0x100)

; Direct syscall stub for NtGetNextProcess on Win11 24H2.
NtGetNextProcess PROC
    mov  r10, rcx
    mov  eax, 100h
    syscall
    ret
NtGetNextProcess ENDP

rustEDR process scan via windows-sys + dynamic SSN

// Cargo: windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_System_LibraryLoader"] }
use std::ffi::CString;
use std::ptr::null_mut;
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};

type NtGetNextProcessFn = unsafe extern "system" fn(HANDLE, u32, u32, u32, *mut HANDLE) -> i32;

const PROCESS_QUERY_LIMITED_INFORMATION: u32 = 0x1000;

fn enumerate_processes(mut callback: impl FnMut(HANDLE)) {
    unsafe {
        let ntdll = GetModuleHandleA(CString::new("ntdll.dll").unwrap().as_ptr() as *const u8);
        let name = CString::new("NtGetNextProcess").unwrap();
        let f: NtGetNextProcessFn = std::mem::transmute(GetProcAddress(ntdll, name.as_ptr() as *const u8));

        let mut prev: HANDLE = 0;
        let mut next: HANDLE = 0;
        while f(prev, PROCESS_QUERY_LIMITED_INFORMATION, 0, 0, &mut next) == 0 {
            if prev != 0 { CloseHandle(prev); }
            prev = next;
            callback(prev);
        }
        if prev != 0 { CloseHandle(prev); }
        let _ = null_mut::<u8>();
    }
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20