Vulnerability
Incident response

Memory fluctuation artifacts

Perinazzo Hugo
Analyste - OWN-CERT
12/3/2026
This article focuses on the obfuscate-and-sleep mechanism inspired by Cobalt Strike's "sleep mask", as well as a simple shellcode fluctuation. It aims to raise awareness and highlight practical indicators for SOC analysts, threat hunters, DFIR experts, malware researchers, and EDR developers.OWN Security

Since the first malware appearance in 1971, Creeper, defenders have continuously adapted; beginning with Reaper, the first antivirus. This marked the start of a long “cat-and-mouse” dynamic between security vendors and malware authors.

Nowadays, attackers rarely drop an unprotected payload on the disk. The payload is either recognizable and easily flagged as it contains known signatures, or it is custom-made to avoid (or slow down) reverse-engineering and exposure of infrastructure. A payload is therefore typically stored on disk in its encrypted form and decrypted only in memory through process-injection techniques used by loaders, packers and crypters.

However, evasion does not end at avoiding on-disk detection. Defenders can also scan memory for indicators, although memory scanning is expensive and cannot be performed continuously. Furthermore, the processes’ memory is volatile, changing, and there is not just one process to monitor on an endpoint. Hence why modern defenses mechanisms rely on behavioral triggers to decide when to perform deeper memory inspection.

Scanning the memory of a process with: yara ./rule.yara <PID>

Windows API is very permissive, endowing developers with significant capabilities. As a result, there are many ways to execute a payload in memory, whether in the stager address space or in one of another process. The payload can take several forms;

If it is an executable, it will require its own implementation of a Loader, perform its image base relocations, load imported libraries, and resolve its IAT.  

If it is a library, similarly to an executable, it will require an implementation of a ReflectiveLoader and perform RDI / sRDI.  

Finaly, if it is a shellcode, the only requirement is executable memory allocation, to write the payload and start a thread.

As an evasion example, some attackers tried to unpatch EDR hooks in ntdll.dll by manually loading the library from the disk, but EDR started to monitor the module's integrity.

Attackers then tried to bypass ntdll.dll hooks by using syswhispers and direct syscalls. But the EDR once again adapted and added static detections for syswhispers headers which were embedded in the malware.

This led to the Hell’s Gate, Halo’s Gate, Tartarus’ Gate and FreshyCalls saga, where all these techniques tried to resolve the syscall numbers dynamically.

Defender adapted again by ensuring that if a syscall is called, the return address must point to an address in ntdll.dll. In response, attackers started doing indirect syscalls and Stack Spoofing.

Restricting telemetry (as ETW / AMSI patching, ntdll.dll hooks unpatch) may seem like a good idea to slightly reduce the vision capabilities of the deployed defensive solution. However, this can also introduce new indicators of compromise. It should also be kept in mind that you can only hinder the telemetry sent to the defensive solution, not block it entirely: some telemetry types are unalterable, particularly those originating from the kernel land, such as kernel callbacks or kernel ETW (TI).

This ongoing evolution has shifted focus toward evasion within memory itself. Techniques such as Cobalt Strike’s Sleep Mask Kit and newer approaches like SleepyCrypt, Foliage, Ekko, DeepSleep and Cronos encrypt or modify payloads while dormant to evade memory scans. A related method, memory fluctuation, changes page protections (e.g., flipping from RX to RW permissions while idle and vice versa) and optionally encrypts content during sleep phases.

During a forensic investigation, while analyzing memory, this technique can also help evade Volatility’s malfind plugin, which is designed to detect hidden or injected code and DLLs in user-mode memory based on characteristics such as VAD tags and page permissions.

The concept of memory fluctuation first emerged in 2017 with the work of Gargoyle, which introduced a self-aware, self-fluctuating shellcode model by leveraging APC timers and ROP chains that invoke VirtualProtect. In 2021, Mariusz Banach published the ShellcodeFluctuation repository, delivering a clear and pedagogical implementation of fluctuation mechanisms. This article relies heavily on it.

Interest in these techniques broadened significantly in 2023. In May, John Uhlmann presented at Black Hat Asia an overview of memory-resident evasion and detection techniques. Later in October, Maldev Academy released a PEFluctuation module teaching students to implement a full PE loader and to apply fluctuation to an in-memory PE. In November, John Hammond release a video on the subject, he is a well-known influencer in the field of cybersecurity, which has resulted in raising awareness of the technique among the public.

As adoption of these techniques has grown, so has the need for effective detection. Tools like pe-sieve and moneta form the foundation for the majority of heuristics also implemented in commercial endpoint security memory scanners, while more specialized projects (e.g., TickTock, Patriot, Hunt-Sleeping-Beacons) attempt to detect specific behaviors; though some have been bypassed by newer evasions (e.g., BusySleepBeacon)

This article focuses on obfuscate-and-sleep mechanisms inspired by Cobalt Strike’s sleep mask, as well as simple shellcode fluctuation. Because defenders currently lack clear guidance on detecting these methods, this work aims to raise awareness and highlight practical indicators for SOC analysts, threat hunters, DFIR practitioners, malware researchers, and EDR developers.

Developing the loader

To understand the fluctuation technique, this section will detail the implementation of a loader, focusing on the x86_64 architecture.

For testing purposes, a lab environment has been set up, consisting of a C2 server, a SIEM, and an EDR (Elastic), with Sysmon deployed on a Windows machine serving as the victim. Further details on setting up this infrastructure can be found in the appendices of this article.

For the malware development process, nix flakes are used to create a reproducible and self-documenting development environment. Using llvm-mingw  as a custom toolchain, allows the use of modern LLVM backend and easily perform C++ while cross-compiling towards Windows.

Note that a lot of the code in the below snippets are derived from mgeeky’s source code.

Hooking sleep

The first goal is to display a message every time the sleep function is called. This can be achieved by defining a main function as follows:

The hookSleep() function is responsible for hooking the Sleep function. In the code below, the address of the Sleep function in the NTDLL module is retrieved. Similarly, the address of the MySleep function, which will replace the legitimate Sleep, is obtained. These addresses are then passed to fastTrampoline(), which handles placing and removing the hook.

The fastTrampoline() function is relatively large and is divided into several parts for clarity.

First, a trampoline template is defined, which moves an address into the r10 register and performs a jump to that address. The placeholder zero values in the template are later replaced with the addressToHook.

When installing the hook, the original Sleep function code is first backed up for later restoration. The function body is then overwritten with the trampoline stub.

To remove the hook, the original code is simply restored:

Finally, applications must call FlushInstructionCache if they generate or modify code in memory. The processor cannot detect the modification and may execute the old code that it has cached. It is also important to restore the correct memory protections afterward.

The last function not yet discussed is MySleep(), which is the destination of the trampoline stub when the program calls kernel32!Sleep.

First, some code is executed (currently a simple print to stdout), which will later be replaced by the payload encryption routine. The legitimate sleep code is then restored to remove potential indicators of compromise associated with the hook. After that, the original function is executed, and the hook is reinstalled for the next cycle.

When the program is executed, the hook operates as intended; however, the behavior is detected by the EDR.

This raised a critical alert on the console:

How elastic is detecting Sleep API hooking

The main advantage (and disadvantage) of Elastic is that it is an open-source solution. This means that SOC analysts, cybersecurity researchers, but also red teamers, and attackers can find out exactly why an alert was triggered.

Protection-artifacts is the package deployed on endpoints. It contains all detection rules like YARA, behavior, etc … A GitHub search on this repository can be used to identify the detection rule being applied.

Réf. : https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_evasion_via_sleep_api_hooking.toml

The rule shown above indicates that an alert is triggered if any of the following WinAPI functions WriteProcessMemory, VirtualProtect, or VirtualProtectEx target the address of kernel32.dll!Sleep. At this point, a malware developer could potentially evade this rule by using direct / indirect syscalls, by writing slightly before the kernel32!Sleep address, or by leveraging an alternative function that produces a similar side effect.

It is also worth noting that malapi.io can be used to identify WinAPI functions with behaviors of interest to reverse engineers and malware developers.

Side note: By attaching a debugger to the loader and jumping to the kernel32!Sleep symbol address, the presence of the hook can be observed. Placing a breakpoint at this location is sufficient to trigger the alert.

This behavior is likely explained by the debugger’s use of software breakpoints. Setting a breakpoint at this address writes an INT 3 opcode into the process memory, Hence, having the same effect as a code injection.

Identifying an EDR bypass is beyond the scope of this article. However, analyzing Elastic provides valuable insight into EDR internals and yields several potential detection indicators.

Basic sleep encryption and fluctuation

A self-injection routine can now be performed, for example by using the classic VirtualAlloc + memcpy + CreateThread sequence followed by code execution. Although this approach still produces strong indicators of compromise, more discreet in-memory execution methods exist with some creativity. For illustration, the CreateThread call is replaced here with a simple JMP.

The shellcode address and size are stored in global variables to reference the buffer while the memory is encrypted. A hardcoded encryption key is used for simplicity; in a production context, a randomly generated key would be required for OPSEC. These metadata are then used by the toggleEncryption() function, which applies a simple xor operation with the provided key to the target address.

To encrypt the memory region, its protection must first be changed to writable. Using VirtualProtect, the permissions are switched from RX to RW, allowing the buffer to be encrypted. Decryption follows the reverse process: since the region is writable, the decrypted payload can be written back, and VirtualProtect is then used again to restore permissions from RW to RX.
As a result, this implementation of sleep encryption also introduces basic fluctuation.

Because this approach relies heavily on VirtualProtect, it produces clear indicators associated with fluctuating malware. Detection opportunities related to this behavior will be discussed later.


The toggleEncryption() function can now be invoked inside MySleep, where execution is redirected from the hooked kernel32!Sleep.

The hook will have the following life cycle:

Decrypt -> Unhook -> Sleep -> Encrypt -> Hook -> Return

Fluctuation via Vector Exception Handler

A Vector Exception Handler (VEH) is a Windows mechanism that allows developers to register custom exception handlers directly with the operating system. Unlike Structured Exception Handling (SEH), which is stack-based and scoped to a specific function or module, VEH handlers are global and receive all exceptions before SEH handlers. This makes VEH particularly useful for debugging, instrumentation, anti-exploitation techniques, and custom crash handling, as it provides low-level control over how exceptions are processed system wide.

This approach adds a VEH and alternates memory page protection between RX and PAGE_NOACCESS. When the instruction pointer (RIP) tries to execute code on a page currently set to NOACCESS, it raises an exception caught by the VEH that unprotects the payload and proceeds to execution. This variant is based on work by ORCA666 presented in his 0x41 injector; unfortunately, the original GitHub repository is no longer available.

Before executing the payload, a VEH is registered using AddVectoredExceptionHandler. The handler function is defined as VectoredExceptionHandler.

When the payload is protected, its page permissions are set to PAGE_NOACCESS instead of RW. Any attempt to execute this region triggers an Access Violation (0xC0000005), which is intercepted by the VEH. The handler then restores the protection to RX, allowing execution to proceed. The sleep hook’s role is to reapply PAGE_NOACCESS to the payload just before invoking ::Sleep().

A note about the Sliver C2 framework

During testing, the Sliver C2 framework was initially used. However, the sleep hooks were not triggered. On Windows, a Sliver beacon does not invoke kernel32!Sleep directly. It relies on Go’s time.Sleep and time.After functions to manage delays between C2 check-ins.  

The Go runtime abstracts platform-specific waiting mechanisms and uses internal primitives; such as waitable timers or SleepEx, instead of calling kernel32!Sleep.

The following snippet shows how the thread is paused between server interactions:  

Réf. : https://github.com/BishopFox/sliver/blob/master/implant/sliver/sliver.go

he time.After(duration) branch pauses the goroutine between C2 interactions, with the delay fully managed by Go’s internal timer system.

For the remainder of this article, the Adaptix C2 framework was used instead, as its beacon agents are implemented in C++.

With Adaptix, the hook operated as expected:

Let’s hunt for indicators

Memory scanners

Now that the loader implements the fluctuation technique using both RW and NOACCESS, and a suitable lab environment is available, the next step is to evaluate the previously mentioned tools and observe their capabilities when analyzing fluctuation.

Moneta

Moneta is a live usermode memory analysis tool for Windows with the capability to detect malware IOCs, Its mainly based on anomalies detections.

In the scan results shown below, the loader process was analyzed using both the RW and NOACCESS fluctuation approaches. The scanner still identified abnormal behavior and flagged FluctuateLdr.exe as suspicious.

PE-sieve

Like Moneta, PE‑sieve scans a target process for potentially malicious implants or patches. When such modifications are identified, it extracts the suspicious or modified PE. During testing, it proved highly effective: it detected the injection and successfully extracted the shellcode in its unprotected form. This capability is particularly useful for analysts seeking to recover a payload from a loader.

Hunt-sleeping-beacons

HSB successfully detected FluctuateLdr.exe due to multiple calls to Sleep. Since EDRs often behave similarly to a “blue team C2”, sleeping and communicating with a server, they can trigger HSB more frequently than the malware itself, resulting in a significant number of false positives.

Hunting through the logs

Before performing SIEM research, EVTX logs were collected, and rapid forensic tools were used, simulating typical DFIR analysis, to identify any notable artifacts.

Using chainsaw and hayabusa, no memory fluctuation artifacts were detected with the default hunting rules.

Since the technique operates entirely in memory, limited visibility is expected on the SIEM, even with Sysmon installed. Logs from Application, System, Security, and Sysmon were collected from the endpoint.

No conclusive artifacts were identified, aside from C2 network interactions, which provide insufficient information to classify as a reliable indicator. At this stage, analysis should focus on the attacker’s objectives and the behaviors exhibited after initial access.

Note: Unrelated to fluctuation, the PE “original filename” header is absent, likely due to compilation with llvm-mingw. While not inherently malicious, this may indicate unusual activity on the system.

For this test, EDR alerts and related telemetry were intentionally disregarded. The objective is to identify new indicators rather than rely on existing Elastic detections.

A note about volatility

For completeness, the target machine’s memory was also examined. A full memory dump was acquired using WinPmem and analyzed with the Volatility3 framework. Although one might expect limited value from memory forensics against a technique designed to evade in‑memory inspection, the analysis was performed for verification.

As expected, the malfind plugin did not identify the embedded payload: the loader stores it in pages marked RW or NOACCESS, which prevents straightforward detection. Attempts to identify the Sleep hook were also unsuccessful, as the hook is intentionally removed just before calling Sleep() to avoid leaving that artifact, as presented during the development phase.

In this context, Volatility provided no actionable indicators. It only confirmed that the loader was active at the time of acquisition, implying that the payload remained within the loader’s address space rather than being injected into another process.

VirtualProtect as a strong indicator

As outlined in John Uhlmann’s presentation, all fluctuation techniques ultimately rely on VirtualProtect to modify memory protections, which internally invokes the undocumented NtProtectVirtualMemory function.
Because ETW TI kernel telemetry cannot be tampered with in the same way as user‑mode NTDLL hooks, the Microsoft‑Windows‑Threat‑Intelligence provider, particularly the THREATINT_PROTECTVM_LOCAL event identified in the TelemetrySource mapping, offers a reliable source of detection.

However, this approach primarily benefits endpoint security developers. Subscribing to this provider requires a process running with Protected Process Light (PPL) privileges at the “Anti‑Malware” level or higher. Legitimate access to this level requires a Microsoft‑co‑signed driver, which limits its availability.

For analysts who are willing to exploit the system they should initially investigate, tools such as SealighterTI: a Sysmon-like research tool for ETW that can access Threat Intelligence ETW telemetry through a PPLdump exploit. Although this exploit has been patched since Windows 10 v21H2 (Build 19044.1826), it may still be achievable through a BYOVD scenario.

In the same category, EtwTi‑FluctuationMonitor uses ETW TI telemetry to track VirtualProtect() activity. It maintains a list of memory regions and increments counters for each change in access protection; when a counter exceeds a defined threshold, the associated region is classified as fluctuating.

A tool that does not require exploiting the host is CFG‑FindHiddenShellcode. It relies on the Control Flow Guard (CFG) bitmap, which records all memory regions that are, or have been executable. Any region that was previously executable but is no longer marked as such is flagged by the scanner. In testing, the tool consistently detected both PAGE_READWRITE and PAGE_NOACCESS transitions from ShellcodeFluctuation.exe, making it one of the most effective options for identifying fluctuating shellcode in memory.

For endpoint security developers, monitoring NtProtectVirtualMemory provides a straightforward way to detect memory fluctuation by observing repeated state changes for the same page. Modern systems rarely need to modify memory protections except for scenarios such as JIT compilation, though relying solely on this heuristic will still produce false positives.

As a threat hunter, Etw‑SyscallMonitor (used with PPLKiller) enables comparison between expected syscall behavior and actual activity based on ETW telemetry.

For details on the internals of these techniques and tools, refer to Uhlmann’s Black Hat Asia 2023 presentation.

Hardening

Finally, for organisations aiming to strengthen the security of Windows endpoints, two hardening measures are worth considering:

  • Enable Hardware Stack Protection (HSP).
    HSP maintains a read‑only shadow stack that stores legitimate return addresses. This provides two benefits: it mitigates exploitation techniques such as ROP or certain fluctuation‑based approaches (e.g., Gargoyle) and it creates a detection opportunity for security products. Techniques such as those demonstrated in CallStackSpoofer and ThreadStackSpoofer can conceal activity from thread‑stack scans but comparing the regular stack to its shadow counterpart can reveal stack spoofing.
  • Enable and leverage Arbitrary Code Guard (ACG).
    ACG prevents a process from creating executable pages or converting writable memory into executable memory, it ensures that code executed is from memory backed by a file on disk, blocking common injection and JIT‑based attack techniques such as shellcode execution, reflective DLL loading, and process hollowing. However, ACG is intentionally restrictive and may disrupt legitimate applications, which limits its practicality for broad deployment in production environments.

Conclusion

As noted in the introduction, the cat‑and‑mouse game has not stopped; far from it. It has simply moved into memory. This evolution has made memory‑resident threats a central area of interest, driving innovation in both detection and evasion techniques.

Key practical takeaways:

  • SOC analysts: EVTX logs alone provide limited value. Rely primarily on telemetry and EDR alerts and apply endpoint hardening measures such as HSP and ACG. Correlate multiple data sources rather than trusting a single signal.
  • Threat hunters: EVTX logs are often insufficient. Tools like Etw-SyscallMonitor can monitor ETW for security relevant syscalls maintaining the set called by each unique process
  • DFIR analysts: CFG‑FindHiddenShellcode offers a fast method to detect fluctuating shellcode. EVTX investigation is generally low‑value compared with targeted memory analysis.
  • Reversers: PE‑sieve can dump fluctuating shellcode for further offline analysis.
  • Security researchers: Tools such as SealighterTI and EtwTi‑FluctuationMonitor allow leveraging ETW TI channels for experimental and research purposes.
  • Endpoint security developers: Monitor sequences of consecutive VirtualProtect calls and take inspiration from Elastic’s approach to API‑hook detection (for example, monitoring hooks of kernel32!Sleep), as well as observing calls to WriteProcessMemory, VirtualProtect, and VirtualProtectEx. That said, behavioral detection alone is likely to generate false positives; combine signals from multiple detectors and consider triggering an on‑demand memory scan when the evidence justifies it.

Across all roles, PE‑sieve and Moneta remain valuable for analyzing advanced memory fluctuation techniques. Combining strong telemetry, layered detection, selective memory scanning, and lab‑driven research provides the best chance of maintaining situational awareness.

A special note for redteamers reading a blueteam article, even though hiding in memory is very useful for evading an AV or EPP, it is clear that all these evasion techniques rely on numerous syscalls and WinAPI functions sometimes outstanding, raising anomalies and making a lot of noise (i.e: relying on VirtualProtect for flipping memory protection leaves a lot of evidence). This approach of “living in memory” is unlikely to remain practical or sustainable long‑term when facing a mature EDR. An alternative approach is to minimize static IoCs in the implant (using ThreatCheck or avred) and execute from backed memory. It is important to emphasize that most static detection rules, such as YARA rules, depend largely on specific strings, which can be easily altered or obfuscated. Moreover, the machine code generated by compilers is highly variable, meaning that even minor changes in source code, compiler version, or compilation settings can produce significantly different binaries. This inherent variability limits the reliability of purely static detection approaches.

References

Appendix

Setting up the lab environment

To reproduce a realistic production environment and support both defensive research and adversary emulation, we deploy a dedicated lab composed of two virtual machines:

  • A Windows “victim” VM used for malware testing, shellcode fluctuation experiments, and endpoint telemetry collection.
  • A Debian‑based infrastructure VM hosting the Elastic Stack (SIEM + EDR) and a separate C2 server.

This setup provides a balanced ecosystem where offensive activity can be monitored, measured, and used to derive new detection indicators without relying on existing detection rules.

Victim Windows VM

The victim workstation runs Windows 11 25H2 (x86_64). Both Sysmon and Elastic EDR are installed to enrich the telemetry available during testing.

Handling Windows Defender During Malware Development

When reversing or developing malware, Windows Defender quickly becomes a major obstacle. Even when temporarily disabled (including cloud submission for OPSEC reasons), its self‑protection mechanisms eventually reactivate it, often without notice. Classic “disable Defender” tricks (registry edits, Group Policy tweaks, etc.) are now largely ineffective on modern Windows builds.

Fortunately, the tool windows-defender-remover currently provides a reliable, automated way to fully remove Defender for lab and research scenarios.
At the time of writing, it works flawlessly on modern Windows versions.

Sysmon for Better Telemetry

Native Windows logs lack structure and consistency for analysis. To obtain richer, more readable, and more actionable telemetry, install Sysmon using a simple one‑liner:

The Elastic Stack (Debian VM)

On the second VM (Debian Trixie), the Elastic stack is deployed. Although Elastic is often presented as heavyweight and difficult to install, it remains one of the best open‑source SIEM/EDR ecosystem available today. Using Elastic Container Project to set up a fully functional Elastic and Fleet environment and dramatically simplifies deployment thanks to docker.

Configuring the Elastic Agent Policy

Lors de la configuration de la politique déployée sur l'agent, les intégrations suivantes sont activées :

  • system –Provides basic host monitoring capabilities. It collects system metrics as well as core Windows event logs such as Application, System, and Security EVTX channels.
  • Windows – Extends Windows‑specific telemetry coverage. This integration collects additional event channels including AppLocker, Defender, and Sysmon events.
  • Elastic Defend – This is the EDR component. All prevention rules were switched from “prevent” to “detect” in order to avoid interfering with ongoing offensive testing. This configuration ensures that detections are logged for analysis without terminating processes or blocking malicious behavior during experiments.

Note that If you’re unsure what telemetry a security solution truly collects, edr-telemetry.com provides an objective overview of the capabilities of major EDR engines and Windows telemetry sources.

Partager l'article :

Your OWN cyber expert.