Skip to main content

Overview

Sleep Encryption (inspired by Ekko by C5pider) is a memory obfuscation technique that encrypts your process’s .text section during sleep periods, making it invisible to memory scanners. When the sleep timer expires, memory is automatically decrypted before execution resumes.
Use case: Defeats periodic memory scans by AV/EDR that look for malicious signatures while your payload is idle.

The Problem: Memory Scanning

Periodic Memory Scanners

Modern EDRs periodically scan process memory for:
  • Known malware signatures (YARA rules)
  • Suspicious code patterns (syscall stubs, shellcode)
  • Unmapped executable pages (RWX regions)
  • Modified PE sections (integrity checks)
Typical scan interval: Every 5-30 seconds

Traditional Sleep is Vulnerable

Long-running C2 beacons that sleep for minutes between callbacks are especially vulnerable — EDR has plenty of time to scan.

How Sleep Encryption Works

High-Level Flow

Step-by-Step Breakdown

1

Generate Random XOR Key

Use RDTSC (CPU timestamp counter) for entropy:
Why RDTSC?
  • No API call (no ntdll hook)
  • Non-deterministic (varies per execution)
  • Fast (single instruction)
2

Locate .text Section

Find the executable code section in our own PE:
3

Change Memory Protection to RW

.text is normally RX (read+execute). We need RW to modify:
4

XOR-Encrypt .text

Encrypt the entire executable section:
Result: .text is now encrypted garbage. Signature scans will fail.
5

Create Waitable Timer

Set up a kernel timer to fire after the sleep duration:
6

Queue APC to Decrypt

Register an APC (Asynchronous Procedure Call) that will decrypt .text:
7

Enter Alertable Sleep

Sleep in alertable state so the APC can fire:
8

APC Decrypts Memory

The queued APC callback decrypts .text:
9

Execution Resumes

After APC completes, NtWaitForSingleObject returns and execution continues:

Implementation

Full C Code

Usage

Enable Sleep Encryption

In Your Code

C2 Beacon Example

Advantages

Defeats Signature Scans

Encrypted .text doesn’t match YARA rules or known patterns

Automatic Decryption

Timer + APC ensures execution resumes correctly without manual intervention

No External Keys

XOR key is generated on-the-fly via RDTSC — nothing stored on disk

Transparent

Drop-in replacement for Sleep() — no code changes needed

Limitations

1. Short Sleep Periods

If sleep duration < EDR scan interval, encryption provides minimal benefit:
Recommendation: Use only for sleeps ≥1 second.

2. Memory Writes During Encryption

If .text is modified while encrypted (impossible since we’re sleeping, but theoretically):
  • Corruption on decrypt
  • Access violations
Protection: We’re sleeping — no code execution occurs.

3. Thread Suspension

If EDR suspends our thread and scans memory:
  • .text is encrypted → scan fails ✅
  • But EDR sees non-executable .text section (suspicious)

4. Performance Overhead

Encryption/decryption cost: Impact: Negligible for typical payloads (10-100 KB).

Detection Vectors

What EDRs Can See

  1. NtProtectVirtualMemory (RX → RW → RX): Suspicious permission changes
  2. NtQueueApcThread: Self-queuing APCs are uncommon
  3. Memory scanning during sleep: .text is encrypted (good!)
  4. Timer objects: NtCreateTimer + alertable wait pattern

Telemetry Example

Analysis: Pattern is distinctive but memory scan fails.

Mitigation Strategies

1

Combine with other evasion

Stack spoofing + ETW bypass reduce telemetry:
2

Vary sleep durations

Random jitter makes pattern less predictable:
3

Don't over-use

Reserve for long sleeps (>5 seconds). Short delays use regular Sleep():

Comparison with Alternatives

Further Reading

Ekko Original Research

C5pider’s original Ekko sleep obfuscation

Anti-Debug Checks

Combine with anti-debugging for layered defense

Stack Spoofing

Hide call stack during execution

Alertable I/O

Microsoft docs on APCs