> Windows Syscalls
ntoskrnl.exeT1497.003T1497T1106

NtQueryPerformanceCounter

Liefert den aktuellen Wert des hochauflösenden Performance-Counters und optional dessen Frequenz.

Prototyp

NTSTATUS NtQueryPerformanceCounter(
  PLARGE_INTEGER PerformanceCounter,
  PLARGE_INTEGER PerformanceFrequency
);

Argumente

NameTypeDirDescription
PerformanceCounterPLARGE_INTEGERoutEmpfängt den aktuellen 64-Bit-Performance-Counter-Wert.
PerformanceFrequencyPLARGE_INTEGERoutOptional. Empfängt die Tick-Frequenz des Counters in Hz. NULL übergeben, falls nicht benötigt.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x31win10-1507
Win10 16070x31win10-1607
Win10 17030x31win10-1703
Win10 17090x31win10-1709
Win10 18030x31win10-1803
Win10 18090x31win10-1809
Win10 19030x31win10-1903
Win10 19090x31win10-1909
Win10 20040x31win10-2004
Win10 20H20x31win10-20h2
Win10 21H10x31win10-21h1
Win10 21H20x31win10-21h2
Win10 22H20x31win10-22h2
Win11 21H20x31win11-21h2
Win11 22H20x31win11-22h2
Win11 23H20x31win11-23h2
Win11 24H20x31win11-24h2
Server 20160x31winserver-2016
Server 20190x31winserver-2019
Server 20220x31winserver-2022
Server 20250x31winserver-2025

Kernel-Modul

ntoskrnl.exeNtQueryPerformanceCounter

Verwandte APIs

QueryPerformanceCounterQueryPerformanceFrequencyNtQuerySystemTimeGetTickCount64RtlQueryPerformanceCounter

Syscall-Stub

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

Bemerkenswert: Die SSN für NtQueryPerformanceCounter ist **seit Windows 10 1507 0x31 und bleibt unter Windows 11 24H2 / Server 2025 weiterhin 0x31** — einer der ganz wenigen Syscalls, die Microsoft über neun Jahre Builds perfekt stabil gehalten hat. Sein Win32-Wrapper `QueryPerformanceCounter` wird fast immer komplett aus KUSER_SHARED_DATA bedient (der `QpcData`-Block bei `0x7FFE0000`), ohne je in den Kernel zu wechseln; der Syscall wird nur als Fallback aufgerufen, wenn der User-Mode-Fast-Path eine Taktquellenänderung, ein Hypervisor-Enlightenment-Update erkennt oder wenn der Aufrufer in einem Prozess läuft, dessen KUSER_SHARED_DATA-Mapping absichtlich gestört wurde (selten). Auf moderner Hardware ist die Counter-Quelle der invariante TSC, skaliert auf eine Nominalfrequenz von 10 MHz.

Häufige Malware-Nutzung

Das ist das **Arbeitstier der Sandbox-Evasion**. Das Muster ist universell: QPC lesen, eine enge Schleife oder bekannt-teure Sequenz ausführen (z. B. eine Serie CPUID-Instruktionen, einen festen Iterationszahl-CryptoAPI-Hash oder eine kalibrierte Busy-Loop), QPC erneut lesen, durch die Frequenz teilen. Sandboxes, die Zeit beschleunigen, single-steppen oder den ausführenden Thread instrumentieren, zeigen Wall-Clock-Werte, die entweder viel zu schnell (zeitspringend) oder viel zu langsam (instrumentiert) sind. VMProtect und Themida verbacken aufwendige QPC-Leitern in ihre Stubs; GuLoader, AsyncRAT-Dropper, IcedID und Dutzende Commodity-Loader nutzen einfachere Zwei-Sample-QPC-Delta-Checks vor dem Entpacken. Der Vorteil gegenüber `rdtsc` ist, dass QPC in Hyper-V- / VMware- / KVM-Gästen funktioniert, in denen der Hypervisor den TSC virtualisiert — QPC liest die vom Hypervisor *exponierte* Uhr, die immer noch Anomalien zeigt, wenn Gastzeit zu Analysezwecken vom Host-Wall-Clock entkoppelt wird. NtDelayExecution + QPC ist die klassische Sleep-Skip-Erkennung (60 Sekunden anfordern, tatsächlich vergangenes QPC messen, abbrechen, wenn die Sandbox in Millisekunden zurückkehrt).

Erkennungs­möglichkeiten

QPC ist *zu* häufig, um direkt darauf zu alarmieren — jeder UI-Thread, jedes Spiel, jeder Browser-Tab ruft es ständig auf. Erkennung muss verhaltensbasiert sein: nach einem engen Muster `QPC → billige Berechnung → QPC → bedingter Branch auf Delta` suchen, besonders wenn der bedingte Branch der Unterschied zwischen Ausführung der nächsten Stage und sauberem Abbruch ist. ETW Microsoft-Windows-Threat-Intelligence exponiert das nicht. Memory-Scan-basierte Erkennung von VMProtect-/Themida-Stubs fängt das *Resultat* statt der QPC-Nutzung selbst. Der beste Verhaltensmarker ist die *Delay-Skip*-Signatur: NtDelayExecution(60000ms) unmittelbar gefolgt von NtQueryPerformanceCounter, mit Frühabbruch bei verstrichener Zeit < Schwellwert. Sandboxes, die das durch Einhalten der Delays beheben, fallen dann ins enge Schleifenmuster.

Direkte Syscall-Beispiele

cSleep-skip sandbox detection

// Request a long sleep; if the sandbox skips it, QPC reveals the lie.
#include <windows.h>

BOOL looks_like_sandbox(void) {
    LARGE_INTEGER freq, before, after;
    QueryPerformanceFrequency(&freq);
    QueryPerformanceCounter(&before);

    Sleep(60000);   // request 60 seconds

    QueryPerformanceCounter(&after);
    double elapsed_ms = (double)(after.QuadPart - before.QuadPart) * 1000.0 / (double)freq.QuadPart;

    // Real wall-clock 60s ± a few ms. Sandboxes that skip Sleep return in < 5s.
    return elapsed_ms < 50000.0;
}

cTight-loop instrumentation detector

// CPUID has fixed retired-instruction cost; a single-step or trace-driven
// sandbox blows past the expected wall-clock by orders of magnitude.
#include <windows.h>
#include <intrin.h>

BOOL stepped_under_debugger(void) {
    LARGE_INTEGER freq, t0, t1;
    QueryPerformanceFrequency(&freq);
    int regs[4];
    QueryPerformanceCounter(&t0);
    for (int i = 0; i < 100000; ++i) __cpuid(regs, 0);
    QueryPerformanceCounter(&t1);
    double us = (double)(t1.QuadPart - t0.QuadPart) * 1e6 / (double)freq.QuadPart;
    // Native: ~5-50 ms. Hypervisor-traced or single-stepped: seconds.
    return us > 1000000.0; // > 1s for 100k cpuid is wildly anomalous
}

asmx64 direct stub (SSN 0x31, all builds)

; NtQueryPerformanceCounter has used SSN 0x31 from Win10 1507 through Win11 24H2.
; The Win32 wrapper QueryPerformanceCounter usually services the request from
; KUSER_SHARED_DATA without invoking this syscall at all.
NtQueryPerformanceCounter PROC
    mov  r10, rcx
    mov  eax, 31h
    syscall
    ret
NtQueryPerformanceCounter ENDP

MITRE ATT&CK-Mappings

Last verified: 2026-05-20