> Windows Syscalls
ntoskrnl.exeT1055.003T1055.004T1106

NtSetContextThread

Sets the CPU register context of a thread — the kernel primitive behind thread hijacking and shellcode redirection.

Prototype

NTSTATUS NtSetContextThread(
  HANDLE    ThreadHandle,
  PCONTEXT  ThreadContext
);

Arguments

NameTypeDirDescription
ThreadHandleHANDLEinHandle to the target thread. Requires THREAD_SET_CONTEXT access (and ideally THREAD_SUSPEND_RESUME).
ThreadContextPCONTEXTinFully populated architecture-specific CONTEXT struct (1232 B on x64 plus optional XSTATE). ContextFlags selects which register groups are applied.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x16Fwin10-1507
Win10 16070x178win10-1607
Win10 17030x17Ewin10-1703
Win10 17090x181win10-1709
Win10 18030x183win10-1803
Win10 18090x184win10-1809
Win10 19030x185win10-1903
Win10 19090x185win10-1909
Win10 20040x18Bwin10-2004
Win10 20H20x18Bwin10-20h2
Win10 21H10x18Bwin10-21h1
Win10 21H20x18Dwin10-21h2
Win10 22H20x18Dwin10-22h2
Win11 21H20x195win11-21h2
Win11 22H20x198win11-22h2
Win11 23H20x198win11-23h2
Win11 24H20x19Awin11-24h2
Server 20160x178winserver-2016
Server 20190x184winserver-2019
Server 20220x193winserver-2022
Server 20250x19Awinserver-2025

Kernel module

ntoskrnl.exeNtSetContextThread

Related APIs

SetThreadContextWow64SetThreadContextNtGetContextThreadNtContinueNtQueueApcThreadAddVectoredExceptionHandler

Syscall stub

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

Undocumented notes

The high-impact half of the context pair. The kernel validates the CONTEXT — CS must be a legal user-mode selector, EFlags has reserved bits masked, and on CET-enabled systems (Windows 10 20H2+ with HW shadow stack) SSP/Ssp adjustments are sanitized by KeVerifyContextRecord. The CONTEXT struct is *architecture-specific*: 1232 bytes on x64, 716 bytes on x86, and ~880 bytes on ARM64 — and Wow64SetThreadContext exists to bridge x86-on-x64 by writing the 32-bit CONTEXT into the corresponding WOW64 area. On CET-enforced builds, writes to Rip that point at non-CFG/IBT-tagged code can fail with STATUS_SET_CONTEXT_DENIED.

Common malware usage

The canonical shellcode-injection primitive (T1055.003 Thread Execution Hijacking). The full sequence: NtAllocateVirtualMemory(RWX) → NtWriteVirtualMemory(shellcode) → NtOpenThread → NtSuspendThread → NtGetContextThread → set ctx.Rip = shellcode → NtSetContextThread → NtResumeThread. Variants: (a) Early Bird — queue an APC then set context to fire it on first alertable wait. (b) Hardware breakpoint hooking — write DR0..DR3 with addresses of target functions and DR7 with control bits, then handle the resulting #DB exceptions in a Veh handler to implement userland hooks invisible to inline-hook scanners. (c) CET bypass attempts that abuse XSTATE / CET shadow-stack pointer fields.

Detection opportunities

NtSetContextThread on a remote thread is one of the highest-signal events in Windows. Microsoft-Windows-Threat-Intelligence ETW exposes EtwTiLogSetContextThread with source/target PIDs and the new Rip — alert on any cross-process call where the new Rip points to private (non-image-backed) memory. EDRs commonly hook in user-mode via ntdll!NtSetContextThread inline patches; direct syscalls bypass that, but TI-ETW remains. CET / shadow-stack on supported CPUs makes the classic Rip-rewrite less reliable and surfaces failed attempts as KERNEL_SECURITY_CHECK_FAILURE-class bugchecks if the attacker mis-configures the new context.

Direct syscall examples

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

MITRE ATT&CK mappings

Last verified: 2026-05-20