Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thread-Origin Camouflage — A Bounded, Enumerable Class of ntdll-Origin Thread-Creation Primitives

Author: puffert (independent security researcher) · License: MIT · Paper: paper_hotpatch_thread_hijack.md

Security research on a bounded class of process-injection primitives whose defining property is that the immediate NtCreateThreadEx caller is Microsoft-signed ntdll.dll code — so the thread's birth is attributed to operating-system infrastructure, not user code. We call this thread-origin camouflage.

The contribution sits on top of known substrates, which are acknowledged as prior art: the subsystem-abuse injection family (MITRE T1055), Windows hotpatching internals (Signal Labs 2022; MSRC 2013; hasherezade), the remote-helper-thread debug behavior of RtlQueryProcessDebugInformation (Checkpoint; Evilcodecave), and debug-register/VEH patchless interception as a mechanism (CrowdStrike patchless AMSI bypass; 0xflux). What this work adds: the thread-origin provenance framing, a static ntdll direct-caller scanner, and two controllability case studies. Detailed in paper_hotpatch_thread_hijack.md.

Positioning. Not a new family and not a new interception mechanism. The novel kernel is the combination: using DR/VEH to steer LdrHotPatchNotify into its internal NtCreateThreadEx path without modifying ntdll bytes, plus formalizing ntdll-born thread creation as an enumerable surface. Orthogonal to memory-axis work (Process Parameter Poisoning); composable, not superior.


The class — two case studies

HTH (primary case study) RQPDI (second case study)
ntdll function LdrHotPatchNotify RtlQueryProcessDebugInformation
Substrate status hotpatch internals = prior art; instantiation new remote-thread debug behavior = prior art; controllability shown
Invoked via LdrHotPatchNotify() + NtManageHotPatch(class 9) documented debug API, PID argument
Scope in-process (self) cross-process (remote by PID)
Start routine arbitrary (own address space) target-resident (ntdll shared)
Technique patchless — 3 DR breakpoints + VEH, zero ntdll edits 1 DR breakpoint at +0x558
HVCI unaffected (no code-page writes) unaffected
Validated callback executes, calc pops control proven (injected start addr crashes victim)

HTH is the headline (the instantiation absent from public sources). RQPDI reuses a known debug substrate to show the start-routine controllability recurs. Both issue NtCreateThreadEx from within ntdll; neither uses CreateThread, CreateRemoteThread, QueueUserAPC, or NtSetContextThread.


Repository map

Paper & analysis

Discovery methodology — the generalized scanner

  • poc/ntdll_thread_creators.c — enumerates every ntdll function that internally calls NtCreateThreadEx/NtCreateThread/RtlCreateUserThread. PE .text parse + export enum + E8/E9 target scan. A reusable analytical lens that surfaced the second candidate (RQPDI); not a claim of a new class.

HTH (patchless, primary)

Second candidate — RQPDI

NtManageHotPatch probes (supporting the class-0 dead-end / class-9 no-priv findings)

  • poc/hp_*.c, poc/ntmhp_*.c — privilege-model and class-fuzzing probes

Staging / demo layer — the ambient loader (secondary)

The original three-stage loader (mutex delivery → section dual-map / module stomp → execution). Still present and working, but it is the demo substrate, not the contribution. The novel pieces are the thread-origin instances above.

  • HOWTO.md — full operational guide for the loader (build, run, custom shellcode)
  • readme_comparison_v1-v4.md — loader version comparison (v1–v6)
  • poc/parent_v2.c, poc/child_v2.cpoc/child_v6.c, poc/common.h — loader versions

Quick start

Requires Visual Studio 2022 (MSVC x64) and Windows 11 25H2+ (build 26200). No admin rights needed for any of the headline tools. From a Developer Command Prompt, cd poc.

Run the generalized scanner

cl.exe /nologo /W4 /D_CRT_SECURE_NO_WARNINGS /Fe:ntdll_thread_creators.exe ntdll_thread_creators.c
ntdll_thread_creators.exe

Expect three direct callers. The scanner reports the second as ntdll+0xA7DD0 (internal) (a boundary-heuristic limitation); disassembly confirms that call site is RtlCreateUserThread+0x1B5 — i.e. RtlQueryProcessDebugInformation, RtlCreateUserThread, LdrHotPatchNotify.

Run the patchless HTH (pops calc, zero ntdll edits)

cl.exe /nologo /W4 /D_CRT_SECURE_NO_WARNINGS /Fe:Hotpatch_Hijack_patchless.exe Hotpatch_Hijack_patchless.c /link ntdll.lib advapi32.lib
Hotpatch_Hijack_patchless.exe

Run the RQPDI control proof

cl.exe /nologo /W4 /D_CRT_SECURE_NO_WARNINGS /Fe:probe_RQPDI3.exe probe_RQPDI3.c /link ntdll.lib
probe_RQPDI3.exe

Spawns a sacrificial notepad; the control case leaves it alive, the injected case terminates it (0xC0000409).

Build helpers

Each tool has a matching build_*.bat that sources vcvars64.bat first (e.g. build_patchless.bat, build_thread_creators.bat, build_probe_RQPDI3.bat).


What this actually contributes

  1. The thread-origin provenance framing. "The immediate NtCreateThreadEx caller is signed ntdll code" treated as an enumerable detection input — the class lens.
  2. A static ntdll direct-caller scanner. Enumerates the surface; reusable across builds.
  3. HTH (primary). LdrHotPatchNotify coerced into its internal NtCreateThreadEx path — the instantiation absent from public sources — done patchlessly (3 DR breakpoints + VEH, zero ntdll byte writes), so HVCI has nothing to gate and the highest-fidelity memory signal (NtProtectVirtualMemory on ntdll) is absent.
  4. RQPDI (second case study). Reuses the known remote-thread debug substrate to show start-routine controllability recurs.
  5. Honest negative results. NtManageHotPatch class 0 is proven CI-gated across 30+ configurations — a documented dead end that motivates the usermode technique.

Substrates (subsystem-abuse family, hotpatch internals, RQPDI debug behavior, DR/VEH interception as a mechanism) are prior art — see paper §1.1 substrate/contribution table.


Status & limitations (honest)

  • Bounded novelty. The framing, the scanner, and the HTH instantiation are the claim; substrates are prior art (paper §1.1, §2). RQPDI's remote-thread behavior is prior art; only its controllability is shown.
  • Evidence gap (next). Provenance is argued from disassembly + behavioral probes; ETW/call-stack captures of the created thread are the planned tightening (paper §9.1).
  • HTH is self-process; the RQPDI case study is cross-process with a target-resident start-routine constraint (arbitrary code needs prior injection, re-introducing memory-axis signals).
  • HVCI claim is precise: the patchless technique does not depend on writing ntdll code pages, validated on an HVCI-configured host. Not tested under a WDAC policy enforcing UMCI on ntdll specifically.
  • Static enumeration covers direct callers; runtime cross-validation is future rigor work.
  • Validated on build 26200; HTH's in-function scanner handles 22631 as well.

All research conducted on author-owned systems for defensive purposes. Detection recommendations are in paper §8.


Responsible use

This repository is published for defensive security research and education. The proof-of-concept code demonstrates thread-creation primitives against processes you own, on systems you own. Do not use it against systems you are not authorized to test. No vulnerability is disclosed here — this is a technique study; no coordinated disclosure was required, but detection guidance (paper §8) is provided so defenders can treat the ntdll-origin property as a detection input.

Licensed under the MIT License. © 2026 puffert.

About

Thread-origin camouflage: a bounded, enumerable class of ntdll-origin thread-creation primitives on Windows 11 (HTH, RQPDI, static scanner)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages