Skip to content

QueryPerformanceCounter Timing

A timing-based anti-analysis check that uses the high-resolution performance counter to detect single-stepping or breakpoint-induced execution delays.

QueryPerformanceCounter (QPC) returns a monotonic, high-resolution timestamp from a hardware or hypervisor-backed counter. Malware brackets a short block of code with two QPC reads and measures the elapsed ticks. Under normal execution the delta is tiny; when an analyst single-steps the block or trips a software breakpoint, the delta explodes, revealing the debugger.

Unlike flag-based checks such as IsDebuggerPresent, a timing check leaves no artifact to patch in memory — it observes a side effect of analysis itself.

How it works

The sample records the counter, runs a few instructions, records it again, and compares the difference against a threshold. A human stepping through, or a debugger servicing exceptions, inflates the measured interval by orders of magnitude.

c
#include <windows.h>

LARGE_INTEGER t1, t2;
QueryPerformanceCounter(&t1);
// ... small block of work the analyst might single-step ...
QueryPerformanceCounter(&t2);

if ((t2.QuadPart - t1.QuadPart) > THRESHOLD) {
    // Execution was too slow — likely being debugged.
    ExitProcess(0);
}

The same idea appears with rdtsc, GetTickCount, and timeGetTime; QPC is favored because it is high-resolution and stable across the cores a hypervisor exposes.

Detection & analysis

When reverse engineering a sample that uses this check:

  • Static analysis: flag paired imports of QueryPerformanceCounter (and QueryPerformanceFrequency) where the two results feed a subtraction and a comparison branch. Look for the same pattern around decryption or unpacking stubs.
  • Dynamic analysis: hook QueryPerformanceCounter and return monotonically increasing values with small, consistent deltas so timed blocks always appear fast. ScyllaHide's timing patches and time-warp plugins automate this.
  • Detection rule hint: in YARA, match the imports QueryPerformanceCounter plus QueryPerformanceFrequency co-occurring with a conditional ExitProcess or jump; in behavioral logs, alert on two QPC calls separated by very few instructions followed by an early exit.
Votes

Comments(0)