Skip to content

Guard Page Anti-Debug

Arming memory with PAGE_GUARD turns the first access into a one-shot STATUS_GUARD_PAGE_VIOLATION, exposing single-steppers and memory-scanning tools.

A guard page is memory marked with PAGE_GUARD. The first access — read, write, or execute — raises a one-shot STATUS_GUARD_PAGE_VIOLATION (0x80000001) and then automatically clears the guard bit. Malware abuses this as a tripwire: it arms a page, accesses it through its own controlled path, and verifies the exception arrives exactly when expected.

If a debugger has placed a software breakpoint on the page, or a memory-scanning tool reads it first, the guard fires out of order — or the analyst's exception handler swallows the violation — and the sample detects the interference.

How it works

The malware allocates or re-protects a page with PAGE_GUARD, installs a vectored/SEH handler, then deliberately touches the page. On a clean machine the handler runs exactly once; the handler is also where the real (often decrypted) code lives, so naively skipping the fault breaks execution.

c
DWORD old;
BYTE *page = VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);
VirtualProtect(page, 0x1000, PAGE_READWRITE | PAGE_GUARD, &old);

__try {
    volatile BYTE x = page[0];   // triggers STATUS_GUARD_PAGE_VIOLATION
    (void)x;
    // If we reach here without the exception, something cleared the guard.
    debugger_detected();
} __except (GetExceptionCode() == STATUS_GUARD_PAGE_VIOLATION
            ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
    // Expected on a clean run — continue real logic here.
}

Single-stepping interacts badly with guard pages: combining PAGE_GUARD with an instruction that the debugger steps over can desynchronise the expected exception count, which the sample tallies.

Detection & analysis

Static analysis: Look for VirtualAlloc/VirtualProtect with the PAGE_GUARD flag (0x100) ORed into the protection constant, paired with an SEH/VEH handler that explicitly checks for STATUS_GUARD_PAGE_VIOLATION (0x80000001). The deliberate self-access immediately after arming is the tell.

Dynamic analysis: Configure your debugger to pass STATUS_GUARD_PAGE_VIOLATION straight to the application's handler rather than catching it. Avoid placing software breakpoints inside guarded pages; use hardware breakpoints outside them. Trace the exception-count logic so you do not perturb the sample's expected sequence.

Detection rule hint: Flag re-protection of recently-written memory to PAGE_READWRITE | PAGE_GUARD followed within a few instructions by a read of that same region, plus a handler keyed on exception code 0x80000001.

Votes

Comments(0)