> Windows Syscalls
ntoskrnl.exeT1057T1106

NtGetNextProcess

Durchläuft die Prozessliste des Kernels und liefert ein Handle auf den nächsten Prozess.

Prototyp

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

Argumente

NameTypeDirDescription
ProcessHandleHANDLEinHandle auf den vorherigen Prozess in der Aufzählung. NULL beginnt am Listenanfang.
DesiredAccessACCESS_MASKinAuf dem neuen Handle angeforderte Zugriffsrechte, z. B. PROCESS_QUERY_LIMITED_INFORMATION.
HandleAttributesULONGinHandle-Attributflags wie OBJ_INHERIT oder OBJ_CASE_INSENSITIVE. Üblicherweise 0.
FlagsULONGinReserviert. Muss in aktuellen Windows-Builds 0 sein.
NewProcessHandlePHANDLEoutEmpfängt das Handle auf den nächsten Prozess. STATUS_NO_MORE_ENTRIES markiert das Listenende.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
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

Kernel-Modul

ntoskrnl.exeNtGetNextProcess

Verwandte APIs

NtGetNextThreadNtQueryInformationProcessNtQuerySystemInformationCreateToolhelp32SnapshotEnumProcessesOpenProcess

Syscall-Stub

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

Undokumentierte Hinweise

NtGetNextProcess durchläuft die in PsActiveProcessHead verankerte EPROCESS-Kette und liefert reihum ein neues Handle für jeden Prozess. Beim ersten Aufruf NULL als ProcessHandle übergeben; bei Folgeaufrufen das zuvor erhaltene Handle (die Funktion schließt es nur unter Windows 8+ selbst, wenn STATUS_NO_MORE_ENTRIES zurückkommt — andernfalls müssen Sie NtClose aufrufen). Die Schwesterroutine NtGetNextThread iteriert ETHREAD-Einträge innerhalb eines gegebenen Prozesses. Beide werden von ntdll.dll exportiert, haben aber keinen Win32-Wrapper — `CreateToolhelp32Snapshot` und `EnumProcesses` nehmen völlig andere Wege (Toolhelp über eine Snapshot-Section, PSAPI über NtQuerySystemInformation(SystemProcessInformation)).

Häufige Malware-Nutzung

Modernes OPSEC-bewusstes Tradecraft meidet Toolhelp32 und PSAPI zunehmend, weil sie von EDR-User-Mode-Agenten stark gehookt werden und laute ETW-Events erzeugen. PsActiveProcessHead über NtGetNextProcess zu iterieren gibt dem Implant eine Prozessliste, ohne je die offensichtlichen Enumerations-APIs aufzurufen. Typische Anwendungen: lsass.exe vor einem Credential-Dump lokalisieren, EDR/AV-Prozesse zum Unhooken oder Identitätsdiebstahl finden, einen Opferhost für Process Injection wählen sowie Analyse-Tools (Process Hacker, x64dbg) vor dem Entpacken meiden. PoolParty und mehrere öffentliche PoCs nutzen das Paar NtGetNextProcess + NtGetNextThread, um Thread-Pool-Worker-Threads zu enumerieren, ohne Toolhelp anzufassen.

Erkennungs­möglichkeiten

NtGetNextProcess hat im User-Mode kaum legitimen Fußabdruck — Windows selbst ruft sie außerhalb einer Handvoll Systemkomponenten kaum auf. Ein User-Mode-Prozess, der sie wiederholt aufruft, sollte als anomal gelten. Microsoft-Windows-Threat-Intelligence-ETW exponiert diesen Syscall nicht direkt, aber die *Folge*-Operationen (NtOpenProcess gegen lsass, ProcessAccess mit PROCESS_VM_READ) sind gut instrumentiert (Sysmon Event ID 10). Kernel-Callback-basierte EDRs, die PspInsertProcess hooken, sehen diese Enumeration nicht — nur den nachfolgenden Zugriff. Achten Sie auf unsignierte Binaries, die `NtGetNextProcess` aus der Export-Tabelle von ntdll auflösen.

Direkte Syscall-Beispiele

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

MITRE ATT&CK-Mappings

Last verified: 2026-05-20