NtSetTimer
Bewaffnet ein Timer-Objekt mit Due-Time, optionaler Periode und optionaler beim Ablauf feuernder APC-Routine.
Prototyp
NTSTATUS NtSetTimer( HANDLE TimerHandle, PLARGE_INTEGER DueTime, PTIMER_APC_ROUTINE TimerApcRoutine, PVOID TimerContext, BOOLEAN ResumeTimer, LONG Period, PBOOLEAN PreviousState );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| TimerHandle | HANDLE | in | Handle auf einen mit NtCreateTimer erzeugten Timer mit TIMER_MODIFY_STATE-Zugriff. |
| DueTime | PLARGE_INTEGER | in | Ablaufzeit in 100-ns-Einheiten; negativ = relativ zu jetzt, positiv = absolute UTC-FILETIME. |
| TimerApcRoutine | PTIMER_APC_ROUTINE | in | Optionale Usermode-Routine, beim Ablauf als APC im rufenden Thread eingereiht. |
| TimerContext | PVOID | in | Kontextwert, der TimerApcRoutine als erstes Argument übergeben wird. |
| ResumeTimer | BOOLEAN | in | TRUE fordert einen Wake-Timer an, der das System aus dem Suspend (S1-S3) wecken kann. |
| Period | LONG | in | Periode in Millisekunden für einen wiederkehrenden Timer; 0 macht ihn zum One-Shot. |
| PreviousState | PBOOLEAN | out | Optional; erhält den vorherigen Signalzustand des Timers. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x62 | win10-1507 |
| Win10 1607 | 0x62 | win10-1607 |
| Win10 1703 | 0x62 | win10-1703 |
| Win10 1709 | 0x62 | win10-1709 |
| Win10 1803 | 0x62 | win10-1803 |
| Win10 1809 | 0x62 | win10-1809 |
| Win10 1903 | 0x62 | win10-1903 |
| Win10 1909 | 0x62 | win10-1909 |
| Win10 2004 | 0x62 | win10-2004 |
| Win10 20H2 | 0x62 | win10-20h2 |
| Win10 21H1 | 0x62 | win10-21h1 |
| Win10 21H2 | 0x62 | win10-21h2 |
| Win10 22H2 | 0x62 | win10-22h2 |
| Win11 21H2 | 0x62 | win11-21h2 |
| Win11 22H2 | 0x62 | win11-22h2 |
| Win11 23H2 | 0x62 | win11-23h2 |
| Win11 24H2 | 0x62 | win11-24h2 |
| Server 2016 | 0x62 | winserver-2016 |
| Server 2019 | 0x62 | winserver-2019 |
| Server 2022 | 0x62 | winserver-2022 |
| Server 2025 | 0x62 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 62 00 00 00 mov eax, 0x62 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
Undokumentierte Hinweise
Bewaffnet ein mit `NtCreateTimer` erzeugtes Timer-Objekt über `KeSetTimerEx` / `ExpSetTimer` in ntoskrnl.exe. SSN `0x62` ist über jeden Win10/11-Build stabil. `DueTime` ist in NT-100-ns-Einheiten angegeben (negativ für relativ). Wenn `TimerApcRoutine` nicht NULL ist, reiht der Kernel bei jedem Ablauf einen *normalen User-APC* in den **Thread ein, der `NtSetTimer` aufgerufen hat** — nicht in einen anderen Thread — und der APC feuert nur, wenn dieser Thread in einen alertable Wait eintritt. Ein nichtnull `Period` macht den Timer wiederkehrend (`SetWaitableTimer`-Semantik); folgende Abläufe reihen weiter APCs ein, bis `NtCancelTimer` aufgerufen wird.
Häufige Malware-Nutzung
**Der Zünder für Ekko-artige Sleep-Masks.** Das Implant baut eine Kette: NtCreateTimer → NtSetTimer mit `TimerApcRoutine`, das auf eine Folge von ROP-Gadgets `RtlCaptureContext` / `SystemFunction032` / `NtProtectVirtualMemory` zeigt → NtWaitForSingleObject(alertable). Bei Ablauf führt der APC jede ROP-Stufe nacheinander aus: Kontext erfassen, RWX-Implant-Region entschlüsseln, beim nächsten Ablauf neu verschlüsseln + erneut schlafen. Ergebnis: ein Beacon, dessen Speicherbild in jedem *inaktiven* Moment für einen Scanner unlesbar ist. Foliage, Cronos und Zilean liefern Varianten; Havoc aktiviert eine Ekko-Sleep-Mask standardmäßig. Auch missbraucht als geräuscharmer Persistenz-Beacon — periodischer Timer + kleiner APC, der den C2 abfragt.
Erkennungsmöglichkeiten
ETW Threat Intelligence emittiert das eingereihte-APC-Event für den kernelseitig eingereihten User-APC bei jedem Ablauf — *jeder* Tick einer Ekko-Sleep-Mask ist also prinzipiell protokollierbar, auch wenn die meisten EDRs das Volumen drosseln. Die sauberste Erkennung: merken, dass der RIP des `NtSetTimer`-Callsites in nicht-gemapptem Speicher liegt und die APC-Routinenadresse im selben RWX-Bereich — ein self-modifying Timer-Arm-and-Fire aus anonymem Speicher ist in benigner Software extrem selten. Wake-Timer-Missbrauch lässt sich zudem offline via `powercfg /waketimers` aufzählen.
Direkte Syscall-Beispiele
asmx64 direct stub
; Direct syscall stub for NtSetTimer (SSN 0x62, stable across Win10/11)
NtSetTimer PROC
mov r10, rcx ; syscall convention
mov eax, 62h ; SSN
syscall
ret
NtSetTimer ENDPcEkko sleep-mask one-shot arm
// hTimer was just created with NtCreateTimer(SynchronizationTimer).
// hEvent is the rendezvous the APC will signal at end of the ROP chain.
LARGE_INTEGER due;
due.QuadPart = -(LONGLONG)(60 * 1000 * 10000); // 60s relative
NtSetTimer(hTimer,
&due,
(PTIMER_APC_ROUTINE)EkkoStage1, // ROP gadget: encrypt → sleep → decrypt
(PVOID)hEvent, // ctx forwarded as APC arg1
FALSE, // not a wake timer
0, // one-shot; we re-arm at end of chain
NULL);
// Implant thread then alertably waits on hEvent; APC fires on expiry.
NtWaitForSingleObject(hEvent, TRUE, NULL);cRecurring beacon timer (T1029 Scheduled Transfer)
// Beacon every 30s with a small callback that issues an HTTPS check-in.
LARGE_INTEGER due;
due.QuadPart = -(LONGLONG)(30 * 1000 * 10000); // first fire +30s
NtSetTimer(hTimer,
&due,
(PTIMER_APC_ROUTINE)BeaconTickApc,
NULL,
FALSE,
30 * 1000, // period 30s
NULL);
for (;;) NtWaitForSingleObject(hTimer, TRUE, NULL); // alertable, APC fires each tickMITRE ATT&CK-Mappings
Last verified: 2026-05-20