Skip to content

GetTickCount Timing

Malware measures elapsed wall-clock time with GetTickCount to detect the artificial slowdown caused by single-stepping or software breakpoints in a debugger.

GetTickCount reads the millisecond uptime counter directly from the read-only KUSER_SHARED_DATA page shared between the kernel and user mode, making it impossible to hook at the API level without kernel access. Malware takes a timestamp before and after a section of code; if the delta exceeds a threshold (typically a few hundred milliseconds), the code was single-stepped or a breakpoint was hit.

This is conceptually similar to the RDTSC timing check but uses a lower-resolution, wall-clock source that is unaffected by CPU frequency scaling — making it complementary to RDTSC-based checks.

How it works

c
#include <windows.h>

BOOL IsDebugged_TickCount(DWORD dwThresholdMs)
{
    DWORD dwStart = GetTickCount();

    // Decoy work the analyst is likely to step through
    volatile DWORD x = 0;
    for (int i = 0; i < 1000; i++) x += i;

    DWORD dwElapsed = GetTickCount() - dwStart;
    return dwElapsed > dwThresholdMs;   // TRUE = debugger suspected
}

Because GetTickCount maps to a single mov eax, [KUSER_SHARED_DATA+0x320] instruction on x86, malware often inlines the read to avoid the import:

asm
mov  eax, [7FFE0320h]   ; KUSER_SHARED_DATA.TickCount (low dword, x86)
; ... work ...
mov  ecx, [7FFE0320h]
sub  ecx, eax
cmp  ecx, 190h          ; 400 ms threshold
ja   debugger_detected

On x64 the address is 0x7FFE0320 in the 32-bit view or read via GetTickCount64 for larger values.

Detection & analysis

During debugging:

  • Use run (F9) rather than single-step (F7/F8) to keep elapsed time within the threshold.
  • Patch the threshold constant to 0xFFFFFFFF, or NOP the conditional jump.
  • ScyllaHide "GetTickCount" spoofing replaces the returned value with a constant to defeat timing checks.
  • Set a breakpoint after the comparison rather than inside the timed block.

Static / automated detection:

  • CAPA rule U0125 / B0001.032: flags functions containing two or more sequential GetTickCount calls.
  • Sandboxes should accelerate the system clock or stub GetTickCount to return plausible incremented values.
  • YARA: flag binaries that reference the literal address 0x7FFE0320 or import both GetTickCount and a conditional exit early in execution.
Votes

Comments(0)