NtSetContextThread
Establece el contexto de registros de la CPU de un hilo — primitiva del kernel detrás del secuestro de hilo y la redirección de shellcode.
Prototipo
NTSTATUS NtSetContextThread( HANDLE ThreadHandle, PCONTEXT ThreadContext );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ThreadHandle | HANDLE | in | Handle al hilo objetivo. Requiere acceso THREAD_SET_CONTEXT (y, idealmente, THREAD_SUSPEND_RESUME). |
| ThreadContext | PCONTEXT | in | Struct CONTEXT específica de la arquitectura totalmente rellenada (1232 B en x64 más XSTATE opcional). ContextFlags selecciona qué grupos de registros se aplican. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x16F | win10-1507 |
| Win10 1607 | 0x178 | win10-1607 |
| Win10 1703 | 0x17E | win10-1703 |
| Win10 1709 | 0x181 | win10-1709 |
| Win10 1803 | 0x183 | win10-1803 |
| Win10 1809 | 0x184 | win10-1809 |
| Win10 1903 | 0x185 | win10-1903 |
| Win10 1909 | 0x185 | win10-1909 |
| Win10 2004 | 0x18B | win10-2004 |
| Win10 20H2 | 0x18B | win10-20h2 |
| Win10 21H1 | 0x18B | win10-21h1 |
| Win10 21H2 | 0x18D | win10-21h2 |
| Win10 22H2 | 0x18D | win10-22h2 |
| Win11 21H2 | 0x195 | win11-21h2 |
| Win11 22H2 | 0x198 | win11-22h2 |
| Win11 23H2 | 0x198 | win11-23h2 |
| Win11 24H2 | 0x19A | win11-24h2 |
| Server 2016 | 0x178 | winserver-2016 |
| Server 2019 | 0x184 | winserver-2019 |
| Server 2022 | 0x193 | winserver-2022 |
| Server 2025 | 0x19A | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 9A 01 00 00 mov eax, 0x19A 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
La mitad de alto impacto del par de contexto. El kernel valida el CONTEXT — CS debe ser un selector de modo usuario legal, EFlags tiene los bits reservados enmascarados y, en sistemas con CET (Windows 10 20H2+ con shadow stack HW), los ajustes de SSP/Ssp son saneados por KeVerifyContextRecord. La estructura CONTEXT es *específica de la arquitectura*: 1232 bytes en x64, 716 en x86 y ~880 en ARM64 — y Wow64SetThreadContext existe para puentear x86-sobre-x64 escribiendo el CONTEXT de 32 bits en el área WOW64 correspondiente. En builds con CET aplicado, escrituras a Rip que apunten a código no etiquetado CFG/IBT pueden fallar con STATUS_SET_CONTEXT_DENIED.
Uso común por malware
La primitiva canónica de inyección de shellcode (T1055.003 Thread Execution Hijacking). Secuencia completa: NtAllocateVirtualMemory(RWX) → NtWriteVirtualMemory(shellcode) → NtOpenThread → NtSuspendThread → NtGetContextThread → ctx.Rip = shellcode → NtSetContextThread → NtResumeThread. Variantes: (a) Early Bird — encolar una APC y luego fijar el contexto para dispararla en la primera espera alertable. (b) Hooking por breakpoint hardware — escribir DR0..DR3 con direcciones de funciones objetivo y DR7 con bits de control, luego manejar las #DB resultantes en un handler VEH para implementar hooks de userland invisibles a escáneres de hooks inline. (c) Intentos de bypass de CET abusando de XSTATE / campos del puntero de shadow stack.
Oportunidades de detección
NtSetContextThread sobre un hilo remoto es uno de los eventos de mayor señal en Windows. ETW Microsoft-Windows-Threat-Intelligence expone EtwTiLogSetContextThread con PID origen/destino y el nuevo Rip — alertar sobre cualquier llamada entre procesos cuyo nuevo Rip apunte a memoria privada (no respaldada por imagen). Los EDR suelen hookear en modo usuario vía parches inline a ntdll!NtSetContextThread; los syscalls directos eluden eso, pero TI-ETW permanece. CET / shadow stack en CPUs compatibles hace que la reescritura clásica de Rip sea menos fiable y aflora intentos fallidos como bugchecks de tipo KERNEL_SECURITY_CHECK_FAILURE si el atacante configura mal el nuevo contexto.
Ejemplos de syscalls directos
cClassic SetThreadContext shellcode hijack
// Assumes shellcode already written to remoteShellcode in target process.
CONTEXT ctx = { .ContextFlags = CONTEXT_FULL };
NtSuspendThread(hThread, NULL);
NtGetContextThread(hThread, &ctx);
ctx.Rip = (DWORD64)remoteShellcode; // redirect on resume
NtSetContextThread(hThread, &ctx);
NtResumeThread(hThread, NULL);cHardware-breakpoint hook install
// Install a userland hook on a target function via DR0 + #DB VEH handler.
CONTEXT ctx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
NtGetContextThread(hTargetThread, &ctx);
ctx.Dr0 = (DWORD64)pTargetFn;
ctx.Dr7 = (1ULL << 0) /* L0 */ |
(0ULL << 16) /* RW0=00 execute */ |
(0ULL << 18) /* LEN0=00 1-byte */;
NtSetContextThread(hTargetThread, &ctx);
// AddVectoredExceptionHandler then catches EXCEPTION_SINGLE_STEP at pTargetFn.rustx64 stub (Win11 24H2)
use std::arch::asm;
#[unsafe(naked)]
unsafe extern "system" fn nt_set_context_thread_stub() {
asm!(
"mov r10, rcx",
"mov eax, 0x19A", // Win11 24H2 SSN
"syscall",
"ret",
options(noreturn),
);
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20