> Windows Syscalls
ntoskrnl.exeT1057T1055.003T1106

NtGetNextThread

Returns a handle to the next thread within a target process by walking the kernel thread list.

Prototype

NTSTATUS NtGetNextThread(
  HANDLE      ProcessHandle,
  HANDLE      ThreadHandle,
  ACCESS_MASK DesiredAccess,
  ULONG       HandleAttributes,
  ULONG       Flags,
  PHANDLE     NewThreadHandle
);

Arguments

NameTypeDirDescription
ProcessHandleHANDLEinHandle to the process whose threads are being enumerated. Requires PROCESS_QUERY_INFORMATION.
ThreadHandleHANDLEinHandle to the previously returned thread. NULL to begin enumeration.
DesiredAccessACCESS_MASKinAccess mask for the new thread handle, e.g. THREAD_GET_CONTEXT | THREAD_SET_CONTEXT.
HandleAttributesULONGinHandle attribute flags. Usually 0.
FlagsULONGinReserved. Pass 0.
NewThreadHandlePHANDLEoutReceives the new thread handle. STATUS_NO_MORE_ENTRIES indicates the end of the list.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070xE9win10-1507
Win10 16070xECwin10-1607
Win10 17030xEFwin10-1703
Win10 17090xF0win10-1709
Win10 18030xF1win10-1803
Win10 18090xF2win10-1809
Win10 19030xF3win10-1903
Win10 19090xF3win10-1909
Win10 20040xF8win10-2004
Win10 20H20xF8win10-20h2
Win10 21H10xF8win10-21h1
Win10 21H20xF9win10-21h2
Win10 22H20xF9win10-22h2
Win11 21H20xFEwin11-21h2
Win11 22H20xFFwin11-22h2
Win11 23H20xFFwin11-23h2
Win11 24H20x101win11-24h2
Server 20160xECwinserver-2016
Server 20190xF2winserver-2019
Server 20220xFDwinserver-2022
Server 20250x101winserver-2025

Kernel module

ntoskrnl.exeNtGetNextThread

Related APIs

NtGetNextProcessNtOpenThreadNtGetContextThreadNtSetContextThreadThread32FirstThread32Next

Syscall stub

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

Undocumented notes

NtGetNextThread iterates the ThreadListHead of the EPROCESS opened in ProcessHandle. It's the thread-level counterpart to NtGetNextProcess and similarly has no documented Win32 wrapper — `Thread32First`/`Thread32Next` go through Toolhelp's snapshot. Each call returns a fresh kernel handle that the caller must close (except when STATUS_NO_MORE_ENTRIES terminates the walk). The function honours `PROCESS_QUERY_LIMITED_INFORMATION` if the caller doesn't need stronger rights on the process, but the desired *thread* access must be requested up front — the handle isn't easily upgradeable.

Common malware usage

Used together with NtGetNextProcess to enumerate every thread on the system without touching Toolhelp32. The PoolParty injection family (which abuses thread-pool internals) iterates the thread pool's worker threads with this exact API pair to locate a target TP_WORKER thread to hijack. It's also used by stealthy thread-hijacking implants that pick a victim thread, snapshot its context with NtGetContextThread, modify RIP, and resume — all without ever calling OpenThread by TID. Avoiding `Thread32First/Next` is meaningful because Toolhelp creates a `_THSnapshot` section visible to handle-table inspectors.

Detection opportunities

Like NtGetNextProcess, this syscall has low background noise — most legitimate process enumerators use PSAPI or Toolhelp. A spike of NtGetNextThread invocations from a single non-system process is suspicious. Threat-hunting queries that combine ETW Microsoft-Windows-Kernel-Process (thread-create) with the *absence* of expected Toolhelp section creations can surface this pattern. Sysmon Event ID 8 (CreateRemoteThread) will fire on the follow-on hijack only if a new thread is created, not on context-swap hijacks — so the high-value signal is actually NtSetContextThread against a foreign thread immediately following enumeration.

Direct syscall examples

cStealth thread enumeration of a target process

// Walk every thread of a given process without Toolhelp32.
#include <windows.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI *PNT_GET_NEXT_THREAD)(HANDLE, HANDLE, ACCESS_MASK, ULONG, ULONG, PHANDLE);

void EnumerateThreads(HANDLE hProcess) {
    HMODULE nt = GetModuleHandleA("ntdll.dll");
    PNT_GET_NEXT_THREAD pNtGetNextThread = (PNT_GET_NEXT_THREAD)GetProcAddress(nt, "NtGetNextThread");

    HANDLE hThread = NULL;
    HANDLE hNext   = NULL;
    while (pNtGetNextThread(hProcess, hThread, THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME,
                            0, 0, &hNext) == 0) {
        if (hThread) CloseHandle(hThread);
        hThread = hNext;
        // ... inspect TEB / GetThreadContext / hijack candidate ...
    }
    if (hThread) CloseHandle(hThread);
}

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

NtGetNextThread PROC
    mov  r10, rcx
    mov  eax, 101h
    syscall
    ret
NtGetNextThread ENDP

MITRE ATT&CK mappings

Last verified: 2026-05-20