> Windows Syscalls
ntoskrnl.exeT1497T1497.003T1106

NtQuerySystemTime

Retourne l'heure système actuelle en intervalles de 100 ns depuis 1601-01-01 UTC.

Prototype

NTSTATUS NtQuerySystemTime(
  PLARGE_INTEGER SystemTime
);

Arguments

NameTypeDirDescription
SystemTimePLARGE_INTEGERoutReçoit l'heure UTC système actuelle en unités de 100 ns depuis 1601-01-01 (époque FILETIME).

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x5Awin10-1507
Win10 16070x5Awin10-1607
Win10 17030x5Awin10-1703
Win10 17090x5Awin10-1709
Win10 18030x5Awin10-1803
Win10 18090x5Awin10-1809
Win10 19030x5Awin10-1903
Win10 19090x5Awin10-1909
Win10 20040x5Awin10-2004
Win10 20H20x5Awin10-20h2
Win10 21H10x5Awin10-21h1
Win10 21H20x5Awin10-21h2
Win10 22H20x5Awin10-22h2
Win11 21H20x5Awin11-21h2
Win11 22H20x5Awin11-22h2
Win11 23H20x5Awin11-23h2
Win11 24H20x5Awin11-24h2
Server 20160x5Awinserver-2016
Server 20190x5Awinserver-2019
Server 20220x5Awinserver-2022
Server 20250x5Awinserver-2025

Module noyau

ntoskrnl.exeNtQuerySystemTime

APIs liées

GetSystemTimeAsFileTimeGetSystemTimeGetTickCount64QueryPerformanceCounterNtQueryPerformanceCounterNtQuerySystemTimePrecise

Stub du syscall

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

Notes non documentées

La plupart du code moderne lit directement `KUSER_SHARED_DATA.SystemTime` (`0x7FFE0014`) sans syscall — `GetSystemTimeAsFileTime` fait exactement cela en user mode. `NtQuerySystemTime` reste utile aux appelants nécessitant une valeur faisant autorité issue du noyau (horodatage cryptographique, débogueurs, tests passant par le dispatcher). Le SSN est resté `0x5A` sur toutes les builds Win10/11 publiées.

Usage courant par les malwares

Usage classique : détection d'accélération de sandbox — associer `NtQuerySystemTime` à `NtDelayExecution` pour vérifier que la progression de l'horloge murale correspond au sleep demandé. Beaucoup de sandboxes automatisées hookent ou sautent `Sleep` pour finir l'analyse plus vite — mais elles falsifient rarement `KeQuerySystemTime`, donc l'écart trahit la fraude. Du malware extra-prudent lit aussi `KUSER_SHARED_DATA.SystemTime` *et* appelle `NtQuerySystemTime` ; un écart suggère une page partagée trafiquée. Sert également de source d'entropie d'apparence anodine pour les RNG des stagers.

Opportunités de détection

Difficile à alerter isolément — c'est l'une des Native API les plus appelées dans le logiciel normal. Le motif intéressant est la *séquence* : `NtDelayExecution` court suivi immédiatement de `NtQuerySystemTime`, en boucle, depuis un processus sans UI. Les traces ETW `Microsoft-Windows-Kernel-Process` combinées au stack-walking peuvent le révéler. Lire `KUSER_SHARED_DATA.SystemTime` en user mode est invisible aux outils de syscall tracing, ce qui explique que le malware évasif préfère cette voie quand une horloge approximative suffit.

Exemples de syscalls directs

asmx64 direct stub

; Direct syscall stub for NtQuerySystemTime (SSN 0x5A, all Win10/11 builds)
NtQuerySystemTime PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 5Ah          ; SSN
    syscall
    ret
NtQuerySystemTime ENDP

cCross-check shared page vs syscall

// If KUSER_SHARED_DATA.SystemTime and NtQuerySystemTime disagree by
// more than a few seconds, the analysis environment has tampered
// with the shared page or with the syscall path.
#include <windows.h>
#include <winternl.h>

extern "C" NTSTATUS NTAPI NtQuerySystemTime(PLARGE_INTEGER);

BOOL SharedPageTamperedWith(void) {
    LARGE_INTEGER shared, kernel;
    // KUSER_SHARED_DATA.SystemTime is a tick-incrementing 8-byte field
    shared.QuadPart = *(volatile LONGLONG*)0x7FFE0014;
    NtQuerySystemTime(&kernel);
    LONGLONG deltaSec = (kernel.QuadPart - shared.QuadPart) / 10000000LL;
    return deltaSec > 5 || deltaSec < -5;
}

rustAnti-sandbox sleep delta

// Pair NtQuerySystemTime with NtDelayExecution to detect sandbox-accelerated sleeps.
use std::arch::asm;

#[unsafe(naked)]
unsafe extern "system" fn nt_query_system_time(_out: *mut i64) -> i32 {
    asm!("mov r10, rcx", "mov eax, 0x5A", "syscall", "ret", options(noreturn));
}
#[unsafe(naked)]
unsafe extern "system" fn nt_delay_execution(_a: u8, _i: *const i64) -> i32 {
    asm!("mov r10, rcx", "mov eax, 0x34", "syscall", "ret", options(noreturn));
}

pub fn sleep_was_skipped(seconds: i64) -> bool {
    let (mut t0, mut t1): (i64, i64) = (0, 0);
    let interval = -seconds * 10_000_000;
    unsafe {
        nt_query_system_time(&mut t0);
        nt_delay_execution(0, &interval);
        nt_query_system_time(&mut t1);
    }
    ((t1 - t0) / 10_000_000) < seconds - 1
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20