> Windows Syscalls
ntoskrnl.exeT1027.011T1106T1029

NtSetTimer2

Arms a Timer2 object with a due time, optional period and a T2_SET_PARAMETERS block describing callback and flags.

Prototype

NTSTATUS NtSetTimer2(
  HANDLE              TimerHandle,
  PLARGE_INTEGER      DueTime,
  PLARGE_INTEGER      Period,
  PT2_SET_PARAMETERS  Parameters
);

Arguments

NameTypeDirDescription
TimerHandleHANDLEinHandle to a timer previously created with NtCreateTimer2.
DueTimePLARGE_INTEGERinAbsolute (positive) or relative (negative, in 100ns units) initial expiry time.
PeriodPLARGE_INTEGERinOptional periodic interval in milliseconds; NULL or 0 for one-shot.
ParametersPT2_SET_PARAMETERSinStructure with TolerableDelay, optional NoWakeTolerance and the apc/callback descriptor.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x192win10-1507
Win10 16070x19Bwin10-1607
Win10 17030x1A1win10-1703
Win10 17090x1A4win10-1709
Win10 18030x1A6win10-1803
Win10 18090x1A7win10-1809
Win10 19030x1A8win10-1903
Win10 19090x1A8win10-1909
Win10 20040x1AEwin10-2004
Win10 20H20x1AEwin10-20h2
Win10 21H10x1AEwin10-21h1
Win10 21H20x1B0win10-21h2
Win10 22H20x1B0win10-22h2
Win11 21H20x1B9win11-21h2
Win11 22H20x1BDwin11-22h2
Win11 23H20x1BDwin11-23h2
Win11 24H20x1C0win11-24h2
Server 20160x19Bwinserver-2016
Server 20190x1A7winserver-2019
Server 20220x1B6winserver-2022
Server 20250x1C0winserver-2025

Kernel module

ntoskrnl.exeNtSetTimer2

Related APIs

SetThreadpoolTimerCreateThreadpoolTimerNtSetTimerNtSetTimerExNtCancelTimer2NtCreateTimer2

Syscall stub

4C 8B D1            mov r10, rcx
B8 C0 01 00 00      mov eax, 0x1C0
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

Where the legacy `NtSetTimer` took the APC routine and context as separate `PTIMER_APC_ROUTINE` / `PVOID` parameters, `NtSetTimer2` collapses everything that *modifies* the armed-state into a single `T2_SET_PARAMETERS` block. The struct carries `Version`, a `TolerableDelay` (used by the coalescing scheduler), an optional `NoWakeTolerance` (avoids waking the CPU from C-states), and a union choosing between *workitem callback* (used by `SetThreadpoolTimer`) and *APC callback* (used when the timer drives an alertable wait). The kernel side is `ExpSetTimer2` → `KiSetTimer2` and the timer is rearmed via `KiTimerExpiration` running on the `PRCB`'s second-generation timer queue. Coalescing is the real reason Microsoft introduced Timer2: a stream of small timers across many threads can be merged at expiry to reduce wakeups by an order of magnitude.

Common malware usage

In sleep-mask designs that adopted Timer2 (Cronos / Zilean and several private red-team kits) `NtSetTimer2` is the call that *actually weaponises* the timer: the `T2_SET_PARAMETERS::Callback.Routine` field points at the first ROP gadget of the encryption chain, and a short `DueTime` (typically 1-5 ms relative) plus a non-zero `Period` makes the chain self-rearm. Setting `NoWakeTolerance` to a high value reduces CPU wakeups, making the implant's idle posture indistinguishable from a normal threadpool consumer such as Edge or Teams. Some **PoolParty** variants pass `Parameters` that point into a *different process*' memory via a smuggled work-item, abusing the threadpool worker factory to deliver execution.

Detection opportunities

Telemetry-wise `NtSetTimer2` is the chokepoint because `NtCreateTimer2` is too noisy in legitimate apps. ETW-TI (`Microsoft-Windows-Threat-Intelligence`) does *not* currently emit a dedicated event for Timer2 arming; ETW `Microsoft-Windows-Kernel-Process` and `Microsoft-Windows-Threading` together let you reconstruct it. The strong signal is: `Parameters->Callback.Routine` lying in unbacked / `MEM_PRIVATE` RWX memory, short `DueTime` (< 100 ms) combined with a `Period` that matches an Ekko-derived 50-100 ms cadence, and a calling thread whose entry point itself is private-memory. CrowdStrike, Defender for Endpoint and Elastic all detect Ekko *via the consequence* — the alertable wait inside RWX memory — rather than this syscall itself.

Direct syscall examples

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtSetTimer2 (SSN 0x1C0 on Win11 24H2 / Server 2025)
NtSetTimer2 PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 1C0h         ; SSN — drifts; resolve dynamically for portability
    syscall
    ret
NtSetTimer2 ENDP

cModern sleep-mask arming (Cronos-style)

// Arm the Timer2 created earlier; APC delivery will run the ROP chain that
// flips implant memory between encrypted RW and executable RX.
typedef struct _T2_SET_PARAMETERS_V0 {
    ULONG Version;
    ULONG Reserved;
    LONG64 NoWakeTolerance;
} T2_SET_PARAMETERS, *PT2_SET_PARAMETERS;

LARGE_INTEGER due    = { .QuadPart = -50000 };          // 5 ms relative
LARGE_INTEGER period = { .QuadPart = 50 };               // 50 ms
T2_SET_PARAMETERS p = { .Version = 0, .NoWakeTolerance = 50 * 10000 };

NTSTATUS st = NtSetTimer2(hTimer, &due, &period, &p);

rustSetThreadpoolTimer wrapper

// Cargo: windows-sys = "0.59" (Win32_System_Threading, Win32_Foundation)
use windows_sys::Win32::System::Threading::{SetThreadpoolTimer, PTP_TIMER};
use windows_sys::Win32::Foundation::FILETIME;

unsafe fn arm(t: PTP_TIMER) {
    let mut due: FILETIME = std::mem::zeroed();
    // Negative => relative 100ns; -50_000 == 5 ms.
    let rel: i64 = -50_000;
    due.dwLowDateTime  = (rel as u64) as u32;
    due.dwHighDateTime = ((rel as u64) >> 32) as u32;
    SetThreadpoolTimer(t, &due, 50 /* period ms */, 100 /* tolerance ms */);
}

MITRE ATT&CK mappings

Last verified: 2026-05-20