> Windows Syscalls
ntoskrnl.exeT1497T1497.003T1106

NtQuerySystemTime

Devuelve la hora del sistema actual como un contador de intervalos de 100 ns desde 1601-01-01 UTC.

Prototipo

NTSTATUS NtQuerySystemTime(
  PLARGE_INTEGER SystemTime
);

Argumentos

NameTypeDirDescription
SystemTimePLARGE_INTEGERoutRecibe la hora UTC actual del sistema en unidades de 100 ns desde 1601-01-01 (época FILETIME).

IDs de syscalls por versión de Windows

Versión 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

Módulo del kernel

ntoskrnl.exeNtQuerySystemTime

APIs relacionadas

GetSystemTimeAsFileTimeGetSystemTimeGetTickCount64QueryPerformanceCounterNtQueryPerformanceCounterNtQuerySystemTimePrecise

Stub del 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

Notas no documentadas

El código moderno lee `KUSER_SHARED_DATA.SystemTime` (`0x7FFE0014`) directamente sin syscall — `GetSystemTimeAsFileTime` hace exactamente eso en user mode. `NtQuerySystemTime` sigue existiendo para llamantes que necesitan un valor autoritativo del kernel (sellado criptográfico de tiempo, depuradores, tests que deben pasar por el dispatcher). El SSN ha sido `0x5A` en cada build publicado de Win10/11.

Uso común por malware

Uso clásico: detección de aceleración de sandbox — emparejar `NtQuerySystemTime` con `NtDelayExecution` para verificar que el avance del reloj de pared coincida con el sleep solicitado. Muchas sandboxes automatizadas hookean o saltan `Sleep` para terminar el análisis más rápido — pero rara vez falsifican `KeQuerySystemTime`, así que la diferencia revela la manipulación. Malware extra defensivo lee también `KUSER_SHARED_DATA.SystemTime` *y* llama a `NtQuerySystemTime`; una discrepancia indica una página compartida adulterada. También sirve como fuente de entropía aparentemente inocua para semillas de RNG en stagers.

Oportunidades de detección

Difícil alertar aisladamente — es una de las Native API más invocadas en software normal. El patrón interesante es la *secuencia*: `NtDelayExecution` corto seguido inmediatamente por `NtQuerySystemTime`, repetido en bucle, desde un proceso sin UI. Trazas ETW `Microsoft-Windows-Kernel-Process` con stack-walking pueden mostrarlo. Leer `KUSER_SHARED_DATA.SystemTime` desde user mode es invisible para herramientas de syscall tracing, por lo que el malware evasivo prefiere esa vía cuando le basta un reloj aproximado.

Ejemplos de syscalls directos

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
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20