NtGetNextThread
Retourne un handle vers le thread suivant d'un processus cible en parcourant la liste de threads du noyau.
Prototype
NTSTATUS NtGetNextThread( HANDLE ProcessHandle, HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, ULONG Flags, PHANDLE NewThreadHandle );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle vers le processus dont les threads sont énumérés. Nécessite PROCESS_QUERY_INFORMATION. |
| ThreadHandle | HANDLE | in | Handle vers le thread précédemment retourné. NULL pour démarrer l'énumération. |
| DesiredAccess | ACCESS_MASK | in | Masque d'accès pour le nouveau handle de thread, ex. THREAD_GET_CONTEXT | THREAD_SET_CONTEXT. |
| HandleAttributes | ULONG | in | Drapeaux d'attributs du handle. Généralement 0. |
| Flags | ULONG | in | Réservé. Passez 0. |
| NewThreadHandle | PHANDLE | out | Reçoit le nouveau handle de thread. STATUS_NO_MORE_ENTRIES marque la fin de la liste. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0xE9 | win10-1507 |
| Win10 1607 | 0xEC | win10-1607 |
| Win10 1703 | 0xEF | win10-1703 |
| Win10 1709 | 0xF0 | win10-1709 |
| Win10 1803 | 0xF1 | win10-1803 |
| Win10 1809 | 0xF2 | win10-1809 |
| Win10 1903 | 0xF3 | win10-1903 |
| Win10 1909 | 0xF3 | win10-1909 |
| Win10 2004 | 0xF8 | win10-2004 |
| Win10 20H2 | 0xF8 | win10-20h2 |
| Win10 21H1 | 0xF8 | win10-21h1 |
| Win10 21H2 | 0xF9 | win10-21h2 |
| Win10 22H2 | 0xF9 | win10-22h2 |
| Win11 21H2 | 0xFE | win11-21h2 |
| Win11 22H2 | 0xFF | win11-22h2 |
| Win11 23H2 | 0xFF | win11-23h2 |
| Win11 24H2 | 0x101 | win11-24h2 |
| Server 2016 | 0xEC | winserver-2016 |
| Server 2019 | 0xF2 | winserver-2019 |
| Server 2022 | 0xFD | winserver-2022 |
| Server 2025 | 0x101 | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
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
Notes non documentées
NtGetNextThread itère le ThreadListHead de l'EPROCESS ouvert dans ProcessHandle. C'est l'équivalent au niveau thread de NtGetNextProcess et n'a pas non plus de wrapper Win32 documenté — `Thread32First`/`Thread32Next` passent par le snapshot Toolhelp. Chaque appel retourne un handle noyau frais que l'appelant doit fermer (sauf quand STATUS_NO_MORE_ENTRIES termine le parcours). La fonction honore `PROCESS_QUERY_LIMITED_INFORMATION` si l'appelant n'a pas besoin de droits plus forts sur le processus, mais l'accès *thread* souhaité doit être demandé d'emblée — le handle n'est pas facilement améliorable.
Usage courant par les malwares
Utilisé avec NtGetNextProcess pour énumérer chaque thread du système sans toucher à Toolhelp32. La famille d'injection PoolParty (qui abuse des internals du thread pool) itère les threads worker du pool avec exactement ce couple d'API pour localiser un TP_WORKER cible à détourner. Également utilisé par les implants furtifs de détournement de thread qui choisissent un thread victime, capturent son contexte avec NtGetContextThread, modifient RIP, et reprennent — le tout sans jamais appeler OpenThread par TID. Éviter `Thread32First/Next` est significatif car Toolhelp crée une section `_THSnapshot` visible aux inspecteurs de table de handles.
Opportunités de détection
Comme NtGetNextProcess, ce syscall a peu de bruit de fond — la plupart des énumérateurs de processus légitimes utilisent PSAPI ou Toolhelp. Un pic d'invocations de NtGetNextThread depuis un seul processus non-système est suspect. Les requêtes de threat-hunting combinant l'ETW Microsoft-Windows-Kernel-Process (création de thread) avec l'*absence* des créations de section Toolhelp attendues peuvent révéler ce schéma. Sysmon Event ID 8 (CreateRemoteThread) ne se déclenche sur le détournement consécutif que si un nouveau thread est créé, pas sur les détournements par échange de contexte — le signal à haute valeur est en fait NtSetContextThread contre un thread étranger immédiatement après l'énumération.
Exemples de syscalls directs
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 ENDPMappings MITRE ATT&CK
Last verified: 2026-05-20