Reflective code loading lets an intruder run a full payload inside a process that already exists, without ever writing an executable to disk or starting a new process, so the controls that watch files and process launches see nothing unusual. MITRE ATT&CK v20 tracks it as T1620 Reflective Code Loading under the Stealth tactic (TA0005) on Windows, Linux and macOS, with more than 30 procedure examples ranging from commodity loaders to state groups. This page covers how the technique differs from process injection and shared modules, who uses it, which telemetry actually carries it (memory permission changes, .NET loader events, AMSI), the hardening options that raise the cost, and how to test detections safely. It is written for detection engineers, EDR owners and incident responders. For the wider picture of how attackers blind endpoint tools, see the EDR Evasion and Defence Guide; this page stays on the ATT&CK technique view.
Key takeaways
- The signal is in memory, not on disk. ATT&CK v20 detection strategy DET0300 looks for allocate, copy, change protection and execute inside one process, with no cross-process write and no backing file.
- .NET is the most common Windows route. FIN7, Kimsuky, Gamaredon Group and the SharePoint ToolShell campaign are all recorded loading assemblies from memory with System.Reflection.Assembly.Load.
- ATT&CK lists no mitigation for T1620. Prevention comes from making the loaders hard to reach: application control, constrained scripting, exploit protection and attack surface reduction, labelled here as NHI Mgmt Group recommendations.
- AMSI and .NET runtime ETW close much of the gap. AMSI sees PowerShell and script content after deobfuscation, and the .NET loader events record assemblies that never had a file.
- Memory forensics is the confirmation step. An alert on suspicious memory behaviour should trigger a memory capture before the host is rebooted, because the evidence lives only in RAM.
What T1620 is and how it differs from its neighbours
ATT&CK defines reflective loading as allocating and then executing a payload directly within the memory of a process, rather than creating a thread or process backed by a file path on disk. The payload can be a compiled binary, an anonymous file present only in RAM, or position-independent shellcode. ATT&CK gives the PowerShell route as an example: the .NET Assembly.Load() method called from PowerShell can load raw code into the running process. Because the code runs inside an existing, often trusted process, process-based detections see only the host process, and payloads can stay encrypted until the moment they execute.
| Technique | Where the code ends up | Disk artefact | Key detection difference |
|---|---|---|---|
| T1620 Reflective Code Loading | The loading process's own memory | None required | Memory allocation and protection changes within one process; no cross-process access |
| T1055 Process Injection | Another process's memory | None required | Cross-process handle opens, remote writes and remote threads (Sysmon 8 and 10) |
| T1129 Shared Modules | A module loaded through the normal OS loader | A DLL or shared object on disk | Image load events with a file path, hash and signature |
| T1027 Obfuscated Files or Information | Not an execution method; hides content | Often an encoded file or script | Often paired with T1620 so the payload is decrypted only in memory |
See the T1055 Process Injection Defensive Guide and the T1027 Obfuscated Files or Information Defensive Guide for those techniques.
Routes by platform
- Windows native code: reflective DLL loaders map a PE file into memory by hand (resolving imports and relocations themselves) and call its entry point. ATT&CK records PowerSploit reflectively loading a Windows PE into a process and Lizar using a public reflective DLL injection module.
- Windows .NET: Assembly.Load with a byte array, or hosting the Common Language Runtime inside a process to run a .NET executable from memory. ATT&CK records Cobalt Strike's execute-assembly and SILENTTRINITY doing this in a sacrificial process.
- Linux: anonymous memory regions or in-memory files. The Linux man page for memfd_create describes it as creating an anonymous file that lives in RAM and appears under /proc/self/fd with a memfd: prefix, which is why loaders favour it.
- macOS: ATT&CK records ThiefQuest and APT38 using the NSCreateObjectFileImageFromMemory API to load and link payloads held in memory.
- Shellcode converters: tools such as Donut generate position-independent code that runs VBScript, JScript, EXE, DLL and .NET payloads in memory, so one loader can carry almost any payload.
Real cases from ATT&CK v20
| Actor, software or campaign | What ATT&CK records | Detection lesson |
|---|---|---|
| SharePoint ToolShell Exploitation (C0058), 2025 | Threat actors reflectively loaded payloads using System.Reflection.Assembly.Load (citing Microsoft and Eye Security, July 2025) | Server processes such as the IIS worker (w3wp.exe) loading unusual assemblies deserve their own baseline |
| FIN7 (G0046) | Loaded a .NET assembly into the current execution context via Reflection.Assembly::Load | PowerShell plus in-memory assembly load is a core pattern for AMSI and script block logging |
| Kimsuky (G0094) | Used Invoke-Mimikatz to reflectively load a credential-stealing DLL, and also used System.Reflection.Assembly loading | Reflective loading often carries credential theft, so link alerts to LSASS access monitoring |
| 3CX Supply Chain Attack (C0057) | AppleJeus converted PE-COFF files to position-independent code with the open-source DAVESHELL project to load the payload reflectively | Signed, trusted software can be the host process; trust in the binary is not trust in its memory |
| IceApple (S1022) | Loads .NET assemblies into MSExchangeOWAAppPool on Exchange servers | Web application pools are a favourite home for memory-only frameworks |
| Uroburos (S0022) | Loads new modules directly into memory with its Load Modules Mem command (citing joint advisory AA23-129A) | Long-lived espionage implants keep modules memory-resident to avoid file scanning |
| Pikabot (S1145) and Lumma Stealer (S1213) | Reflectively load encrypted components into the current process to avoid writing content to disk | Commodity loaders use the same technique, so it is not rare enough to ignore |
The SharePoint case is a reminder that reflective loading is usually one step in a chain: in the ToolShell SharePoint exploitation, attackers also stole ASP.NET machine keys, so responders had to rotate secrets as well as clean the host.
What to detect and where
ATT&CK v20 detection strategy DET0300 has one analytic per platform. Each looks for memory being made executable inside a process that does not normally do that, without a matching file on disk.
| Platform (analytic) | Behaviour to detect | Log sources named in v20 | Tuning elements |
|---|---|---|---|
| Windows (AN0838) | Allocation, copy, protection change to executable and thread creation inside one process (for example VirtualAlloc, then VirtualProtect, then CreateThread), with no cross-process write | Sysmon 1, 7 and 10; Microsoft-Windows-DotNETRuntime ETW (Loader keyword); AMSI script content and verdicts | Parent process allow list; memory region permissions |
| Linux (AN0839) | mmap and mprotect changing memory from writable to executable, followed by execve or execveat, without a matching ELF on disk | auditd execve; auditd records of RWX memory allocation | Process name scope; RWX threshold |
| macOS (AN0840) | dlopen, dlsym or mmap with RWX in processes that do not normally load modules dynamically; anonymous executable memory in user processes | Unified log events for exec and in-memory dylib loads | Application scope; time window |
Windows telemetry in practice
| Source | Event or field | What it adds for T1620 |
|---|---|---|
| Microsoft-Windows-DotNETRuntime ETW, Loader keyword (0x8) | AssemblyLoad_V1 (event 154) and ModuleLoad_V2 (event 152) | Records every assembly load with its name and flags; ModuleILPath holds a file path for normal loads, so an assembly with no meaningful path in an unexpected process is a strong lead |
| AMSI | Script content passed to the antimalware provider | Microsoft documents AMSI integration for PowerShell (scripts, interactive use and dynamic code evaluation), Windows Script Host, JavaScript and VBScript, and Office VBA macros, so the loader script is seen after deobfuscation |
| PowerShell Operational log | Event 4104 script block logging | Captures the script that calls Assembly.Load or builds the byte array; see the PowerShell Security Guide |
| Sysmon | Event 7 image loaded | Microsoft notes it is off by default and noisy; enable it selectively, for example for clr.dll or mscoree.dll loading into processes that are not .NET applications, which suggests the runtime was hosted to run an assembly |
| Sysmon | Event 10 process access; event 25 process tampering | Separates T1620 from cross-process injection and catches hollowing variants |
| EDR memory scanning | Executable private memory not backed by an image | Where the EDR product offers it, alerting on unbacked executable regions and thread start addresses outside loaded modules is the closest equivalent to DET0300's chain |
NHI Mgmt Group recommendation: start with three high-value rules rather than a broad memory rule. First, the CLR loading into processes that are not .NET applications (Office, script hosts, browser helpers). Second, AMSI or 4104 content containing Assembly.Load with a byte array or a long Base64 blob. Third, server worker processes (w3wp.exe, Exchange application pools, Java application servers) creating threads whose start address is outside any loaded module. Each has a narrow baseline and a clear response.
Prevention and hardening
ATT&CK v20 maps no mitigation objects to T1620, because the technique uses normal operating system and runtime features. The controls below are NHI Mgmt Group recommendations that reduce the routes into reflective loading or the damage it can do.
| Control | What it does against T1620 | Trade-off |
|---|---|---|
| Application control (App Control for Business, AppLocker) with PowerShell Constrained Language Mode | Constrained Language Mode blocks the .NET type access that PowerShell loaders depend on, and application control stops unapproved loaders from launching | Needs a maintained allow list and admin exceptions; see App Control for Business (WDAC) |
| Exploit protection: Arbitrary Code Guard (ACG) | Microsoft documents that ACG makes VirtualAlloc or VirtualProtect calls with the execute flag fail, so the process cannot run dynamically generated code | Incompatible with JIT compilers and many script or intermediate-language runtimes; use per application on processes that do not need it, after audit mode |
| Exploit protection: Code Integrity Guard (CIG) | Blocks the memory manager from mapping binaries not signed by Microsoft into the process, including injected ones | Incompatible with most non-Microsoft software; suits a small set of Microsoft-only processes |
| AMSI enabled with a capable antimalware provider | Scans PowerShell and script content, including dynamically evaluated code, before it runs | Only as good as the provider and only covers integrated hosts |
| Remove PowerShell 2.0 and legacy runtimes | Removes engine versions without script block logging or AMSI | Rare legacy dependencies; see the downgrade guidance in MITRE ATT&CK T1689 Downgrade Attack |
| Linux: noexec mounts for /tmp and /dev/shm, restrictive seccomp profiles for containers | Reduces simple file-backed staging and limits syscalls such as memfd_create in workloads that do not need them | memfd and anonymous mappings still work in unconfined processes; pair with auditd |
Where identity fits
Reflective loaders are frequently the delivery vehicle for credential theft: ATT&CK records Kimsuky loading Mimikatz this way and MuddyViper loading a browser data theft tool in a new thread. On servers, the host process often runs as a service account or application pool identity with access to databases, key stores or cloud APIs, so anything loaded into it inherits that identity. NHI Mgmt Group recommendation: when a reflective loading alert fires on a server, treat the service account, its secrets and any machine keys or tokens on the host as exposed and rotate them, as the ToolShell response required. See the T1003 OS Credential Dumping page for the follow-on technique.
Response: confirm in memory before you reboot
- Contain without powering off: isolate the host through EDR network containment so the memory survives.
- Capture memory: acquire a full memory image, then the process dump of the suspected host process. Reflectively loaded code disappears on reboot. See the Memory Forensics Guide for acquisition and analysis.
- Look for unbacked executable regions: memory analysis tools flag private executable regions containing PE headers or shellcode; extract them for scanning and YARA rules.
- Trace the loader: use 4104, AMSI and process creation data to find the script, document or exploit that started the chain and any persistence that will reload it.
- Rotate exposed identities: credentials, tokens and keys reachable by the host process.
- Rebuild if the host process was a server: memory-only implants are often reinstalled by web shells or scheduled tasks; clean the persistence, not only the process.
How to test
- Pick a benign test payload: use a harmless .NET assembly that writes a marker file, loaded from a byte array by a test PowerShell script on a lab host. Open-source adversary emulation libraries include T1620 test cases; run them only in a lab or with written authorisation.
- Check each layer: confirm the AMSI provider logged the script, event 4104 captured it, the .NET runtime ETW recorded the assembly load, and the EDR raised an unbacked memory or CLR load alert.
- Test a server host: repeat inside a test web application pool to make sure server baselines do not suppress the alert.
- Test exploit protection in audit mode: apply ACG in audit mode to a target process, review audit events in Event Viewer or advanced hunting for a week, then enforce if clean.
- Roll back: remove per-process mitigations through the same policy if a business application breaks, and record the exception with an owner and review date.
Measuring coverage
- Telemetry coverage: percentage of Windows servers and workstations sending .NET runtime loader events or equivalent EDR memory telemetry (target: all servers).
- AMSI health: percentage of endpoints with a registered, working AMSI provider.
- Scripting attack surface: number of hosts with PowerShell 2.0 still installed (target: zero) and percentage of user endpoints in Constrained Language Mode.
- Test pass rate: quarterly emulation of T1620 test cases detected within the agreed time.
- Memory capture rate: share of reflective loading incidents where memory was captured before reboot.
Common failure patterns
- Relying on file scanning: antivirus that only scans files on disk never sees a payload that was decrypted in memory.
- Excluding server processes from EDR memory scanning for performance: IIS, Exchange and SharePoint worker processes are exactly where IceApple and the ToolShell payloads ran.
- Rebooting first: reimaging or restarting before memory capture destroys the only copy of the payload and its configuration.
- Leaving PowerShell 2.0 installed: attackers can start the old engine to sidestep AMSI and script block logging.
- Cleaning the host but not the identities: credentials and keys read by the loaded code remain valid after the process is killed.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| MITRE ATT&CK v20 | T1620 Reflective Code Loading (TA0005 Stealth); related T1055, T1129, T1027, T1059.001 | Technique definition, procedures and DET0300 detection strategy |
| NIST SP 800-53 Rev 5 | SI-16 Memory Protection | Exploit protection such as ACG and data execution prevention |
| NIST SP 800-53 Rev 5 | SI-3 Malicious Code Protection; SI-4 System Monitoring | AMSI, EDR memory scanning and runtime telemetry |
| NIST SP 800-53 Rev 5 | CM-7 Least Functionality | Removing legacy PowerShell and constraining script hosts |
| NIST CSF 2.0 | DE.CM-09; PR.PS-04 | Monitoring runtime environments and generating the logs needed |
For the whole tactic, see the ATT&CK Stealth and Defense Impairment Tactics Guide and the ATT&CK Mitigations hub.
Practitioner checklist
- Enable .NET runtime loader telemetry or the EDR equivalent on all servers.
- Confirm AMSI is active with a working provider on every Windows endpoint.
- Turn on PowerShell script block logging and forward event 4104.
- Remove PowerShell 2.0 and other unused legacy runtimes.
- Put user endpoints in Constrained Language Mode under application control.
- Pilot Arbitrary Code Guard in audit mode on processes that do not use JIT compilation.
- Do not exclude web and application server worker processes from memory scanning.
- Write the three narrow detections (CLR in non-.NET processes, Assembly.Load with byte arrays, unbacked threads in server processes).
- Make memory capture the first response step for in-memory alerts.
- Rotate service account credentials and keys on any host where reflective loading is confirmed.
Standards and references
- MITRE ATT&CK: T1620 Reflective Code Loading
- MITRE ATT&CK: DET0300 Detection Strategy for Reflective Code Loading
- Microsoft Learn: Antimalware Scan Interface (AMSI)
- Microsoft Learn: Loader ETW Events (.NET runtime)
- Microsoft Learn: Exploit protection reference
- Microsoft Learn: Sysmon
- Linux man-pages: memfd_create(2)
- NIST: SP 800-53 Rev 5 Security and Privacy Controls
Related NHI Mgmt Group resources: EDR Evasion and Defence Guide · Memory Forensics Guide · Defence Evasion Techniques Guide · T1059 Command and Scripting Interpreter Defensive Guide · Living off the Land Guide