Beacon Detection Blog

20 June 2026 | 7 min read | justruss.tech

Hunting Sleeping Giants: The Complete Defensive Guide to Detecting Encrypted Beacons

For: Threat hunters, security researchers, red teams, blue teams, SOC teams
Level: Advanced
Updated: 2026


Table of Contents

  1. Introduction
  2. The Problem
  3. How It Actually Works
  4. Detection Method 1: Thread State Analysis
  5. Detection Method 2: Page Protection Flapping
  6. Detection Method 3: ROP Gadget Chains
  7. Practical Detection Code
  8. Detection Rule Sets
  9. Emerging Evasion Techniques
  10. False Positive Tuning Guide
  11. Forensic Artifacts & Persistence
  12. Integration with Security Tools
  13. Operational Deployment Guide
  14. Appendices

Introduction

Modern implants have stopped sitting in cleartext between callbacks. We’ve all hit that wall: EDR memory scans during sleep return encrypted blobs. String searches fail. Yara signatures catch nothing. The beacon is there, you know it’s there, but traditional approaches don’t work.

The shift happened with FOLIAGE and Ekko, tools that use NtContinue-based context switching to encrypt themselves during sleep. Instead of relying on process hollowing or code caves, these beacons use system APIs to manipulate their own execution context, encrypt their code, wait, then decrypt and restore. Each cycle is clean. Between callbacks, there’s nothing to find but noise.

The good news: this defense is detectable. It has to be. The operations required to make it work leave forensic traces that memory scans miss but thread analysis doesn’t.

The Problem

Modern implants have stopped sitting in cleartext between callbacks. We’ve all hit that wall: EDR memory scans during sleep return encrypted blobs. String searches fail. Yara signatures catch nothing. The beacon is there, you know it’s there, but traditional approaches don’t work.

The technique is real and actively used by nation-state and financially-motivated threat actors. Defenders have traditionally relied on static analysis and memory scanning. Once the beacon encrypts itself, those approaches fail.

But the operations required to make encryption work create observable behavior. Thread parking in unbacked memory. Page-protection cycling. ROP gadget execution. These are detectable if you know what to look for and have the tools to monitor effectively.

How It Actually Works

The beacon doesn’t use Sleep(). Instead, modern variants use either CreateTimerQueueTimer or NtQueueApcThread to schedule callback execution. The callback executes via NtContinue, which restores a thread context and jumps execution to a specified address.

NtContinue normally handles error recovery by restoring a thread to a previous execution state. Here it’s repurposed: the beacon queues a context structure that has the instruction pointer set to execute encryption code, waits, then restores a different context for decryption. This bypasses the need for normal function calls and keeps operations in system routines rather than beacon code.

The Flow for Ekko (Timer-Queue Based)

  1. CreateTimerQueueTimer is called, scheduling a callback after the sleep interval.
  2. Timer fires. The callback invokes NtContinue with a context that points to encryption code.
  3. Encryption routine runs. XOR, AES, RC4 via SystemFunction032, or another cipher. The entire code section becomes unreadable.
  4. Another NtContinue context switch delays execution via NtDelayExecution.
  5. At wake time, another context switch decrypts the memory.
  6. Final context switch restores the original thread state and resumes execution.

The Flow for FOLIAGE (APC Based)

  1. NtQueueApcThread queues an APC callback on a thread.
  2. APC fires and executes NtContinue with a context that handles the encryption/wait/decryption chain.
  3. Each step (encrypt, wait, decrypt, restore) uses a separate context switch via NtContinue.
  4. The thread returns to its original state.

Detection Method 1: Thread State Analysis

This is what works. When a beacon parks a thread waiting for the timer callback, that thread has to sit somewhere. Normal threads waiting for legitimate timers have call stacks rooted in system DLLs. Malicious threads park in unbacked memory.

The distinctive feature is the return address. Legitimate timer operations unwind through ntdll, kernel32, or the application’s own code. When you walk the stack of a thread sitting in NtWaitForMultipleObjects() and the return addresses don’t resolve to any loaded module, you’ve got something.

What to Hunt For

  1. Threads in a waiting state that have return addresses in unbacked memory.
  2. The unbacked region itself. Where is it? Who allocated it? When?
  3. The pattern of the wait object. Is it a timer? Is it associated with a callback?
  4. Call stacks that show NtContinue or context manipulation patterns.

WinDbg Hunting Commands

// Find all threads and their wait reasons
!runaway              // Lists threads with execution time
!threads              // Shows all thread objects

// Walk a specific thread's stack
~[thread_id]s         // Switch to thread
k                     // Display call stack
kP                    // Call stack with parameters

// Find unbacked memory in thread stacks
.module [return_address]
// If it returns "Module at ...", address is in a DLL
// If it returns nothing or error, it's unbacked

Detection Method 2: Page Protection Flapping

Legitimate code doesn’t change its own page protections. Browsers don’t do it. Office doesn’t do it. Antivirus doesn’t do it. Code runs in RX, data runs in RW. That’s it.

Encrypted beacons have to break this rule. The pattern of changes is distinctive and almost unique to beacon sleep behavior.

Expected Pattern Timeline

T=0:    PAGE_EXECUTE_READ
T+100:  PAGE_EXECUTE_READWRITE (encryption begins)
T+102:  PAGE_NOACCESS (hide encrypted code)
T+5102: PAGE_EXECUTE_READWRITE (decryption begins)
T+5104: PAGE_EXECUTE_READ (restore executable)

ETW Detection Approach

Enable Microsoft-Windows-Kernel-Memory tracing and look for rapid RX↔RW transitions:

logman start EVMTrace -p Microsoft-Windows-Kernel-Memory -o trace.etl
// Let it run during suspected activity
logman stop EVMTrace

// Parse results
Get-WinEvent -Path .trace.etl | Where-Object EventID -eq 98

Detection Method 3: ROP Gadget Chains

The encryption and decryption routines don’t happen via traditional function calls. They use ROP gadgets chained together, creating forensic signatures.

What you’re looking at is short instruction sequences (3-10 bytes), each ending in RET. They execute from unbacked memory. The beacon can’t call a function normally because it’s living in a code cave or injected region. Instead, it jumps through gadget chains.

Memory Hunting in WinDbg

.search -a 41 5b 48 89 ? ? ? ? 48 8d 1d

This searches for: pop r11, add rsp, mov, lea rbx sequence. Finding this in memory is a strong indicator of FOLIAGE or similar.

Practical Detection Code

Core Detection Pattern from MalMemDetect

Hook critical APIs and validate return addresses:

// Hook critical APIs and validate return addresses
LPVOID WINAPI HookedRtlAllocateHeap(
    PVOID hHeap, 
    ULONG dwFlags, 
    SIZE_T dwBytes) 
{
    LPVOID retPointer = OldRtlAllocateHeap(hHeap, dwFlags, dwBytes);
    LPVOID returnAddr = _ReturnAddress();
    HMODULE hModule = NULL;
    if (::GetModuleHandleExA(
        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 
        (LPCSTR)returnAddr, 
        &hModule) != 1) 
    {
        printf("ALERT: RtlAllocateHeap from unbacked address: %pn", returnAddr);
        LogDetection(GetCurrentThreadId(), returnAddr);
    }
    return retPointer;
}

Detection Rule Sets

Sigma Rules for SIEM Integration

title: Suspicious Page Protection Cycling
logsource:
  product: windows
  service: sysmon
detection:
  vprotect_event:
    EventID: 10
  condition: vprotect_event
severity: high

YARA Rules for Memory Scanning

rule Ekko_Timer_Queue_Pattern {
    meta:
        author = "Threat Hunter"
    strings:
        $api1 = "CreateTimerQueueTimer"
        $api2 = "NtContinue"
    condition:
        $api1 and $api2
}

Emerging Evasion Techniques

As detection improves, threat actors develop new evasion methods:

  • Direct Syscall with Encrypted Parameters – Bypassing API hooks
  • Hardware Breakpoint-Based Sleep – Using INT3 breakpoints
  • Fiber-Based Sleep – Using Windows Fibers
  • ALPC Sleep Signaling – Inter-process communication
  • Process Hibernation – Suspending entire process

False Positive Tuning Guide

Detection Trigger Legitimate Use Tuning
VirtualProtect RW JIT compiler Exclude .NET/Chrome
CreateTimerQueueTimer Windows services Whitelist system processes

Forensic Artifacts

Registry Artifacts

HKLMSystemCurrentControlSetServices
HKCUSoftwareMicrosoftWindowsCurrentVersionRun

Memory Evidence

- Encrypted beacon code
- ROP gadget chains
- Timer queue objects
- C2 infrastructure strings

Closing

Encrypted sleeping beacons are a real threat. Build your defenses. Test them ruthlessly. Train your team. The beacons will sleep, but they won’t sleep silently anymore.


Version: 2.0
Updated: 2026