NtGetNextThread
Devuelve un handle al siguiente hilo dentro de un proceso recorriendo la lista de hilos del kernel.
Prototipo
NTSTATUS NtGetNextThread( HANDLE ProcessHandle, HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, ULONG Flags, PHANDLE NewThreadHandle );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle al proceso cuyos hilos se enumeran. Requiere PROCESS_QUERY_INFORMATION. |
| ThreadHandle | HANDLE | in | Handle al hilo devuelto previamente. NULL para iniciar la enumeración. |
| DesiredAccess | ACCESS_MASK | in | Máscara de acceso para el nuevo handle de hilo, p. ej. THREAD_GET_CONTEXT | THREAD_SET_CONTEXT. |
| HandleAttributes | ULONG | in | Banderas de atributos del handle. Habitualmente 0. |
| Flags | ULONG | in | Reservado. Pase 0. |
| NewThreadHandle | PHANDLE | out | Recibe el nuevo handle de hilo. STATUS_NO_MORE_ENTRIES indica el final de la lista. |
IDs de syscalls por versión de Windows
| Versión 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 |
Módulo del kernel
APIs relacionadas
Stub del 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
Notas no documentadas
NtGetNextThread itera el ThreadListHead del EPROCESS abierto en ProcessHandle. Es el equivalente a nivel de hilo de NtGetNextProcess y tampoco tiene wrapper Win32 documentado — `Thread32First`/`Thread32Next` pasan por el snapshot de Toolhelp. Cada llamada devuelve un handle de kernel nuevo que el llamante debe cerrar (excepto cuando STATUS_NO_MORE_ENTRIES termina el recorrido). La función honra `PROCESS_QUERY_LIMITED_INFORMATION` si el llamante no necesita derechos más fuertes sobre el proceso, pero el acceso *hilo* deseado debe pedirse desde el principio — el handle no es fácilmente actualizable.
Uso común por malware
Se usa junto con NtGetNextProcess para enumerar cada hilo del sistema sin tocar Toolhelp32. La familia de inyección PoolParty (que abusa de los internals del thread pool) itera los hilos worker del pool con exactamente esta pareja de API para localizar un TP_WORKER objetivo que secuestrar. También lo usan implantes sigilosos de secuestro de hilo que eligen un hilo víctima, capturan su contexto con NtGetContextThread, modifican RIP y reanudan — todo sin llamar nunca a OpenThread por TID. Evitar `Thread32First/Next` es significativo porque Toolhelp crea una sección `_THSnapshot` visible para inspectores de tabla de handles.
Oportunidades de detección
Como NtGetNextProcess, este syscall tiene poco ruido de fondo — la mayoría de los enumeradores de procesos legítimos usan PSAPI o Toolhelp. Un pico de invocaciones de NtGetNextThread desde un único proceso no-sistema es sospechoso. Las consultas de threat-hunting que combinan ETW Microsoft-Windows-Kernel-Process (creación de hilo) con la *ausencia* de creaciones de sección Toolhelp esperadas pueden destapar este patrón. Sysmon Event ID 8 (CreateRemoteThread) solo se dispara en el secuestro consecutivo si se crea un hilo nuevo, no en secuestros por intercambio de contexto — así que la señal de alto valor es realmente NtSetContextThread contra un hilo ajeno inmediatamente después de la enumeración.
Ejemplos de syscalls directos
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 ENDPMapeos MITRE ATT&CK
Last verified: 2026-05-20