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
#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:
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_detectedOn 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
GetTickCountcalls. - Sandboxes should accelerate the system clock or stub
GetTickCountto return plausible incremented values. - YARA: flag binaries that reference the literal address
0x7FFE0320or import bothGetTickCountand a conditional exit early in execution.