DEP bypass
TL;DR: Data Execution Prevention marks the stack and heap NX, so attackers chain ROP gadgets to call
VirtualProtect/VirtualAlloc/WriteProcessMemoryand turn an existing buffer back into RX before jumping to shellcode.
What it is
DEP (NX on the CPU side) prevents instruction fetch from pages without the executable bit. With DEP on, dropping shellcode into the stack and jumping to it crashes with STATUS_ACCESS_VIOLATION. The standard bypass is a return-orientated programming chain that re-marks attacker-controlled memory executable using a documented Win32 API, then transfers control to the shellcode placed there.
Preconditions / where it applies
- Process opted into DEP (
AlwaysOnpolicy, all 64-bit, most 32-bit since Win7) - A control-of-stack primitive — saved-RIP / SEH overwrite / stack pivot
- Knowledge of (or leak of) at least one module base so gadget addresses are usable; if ASLR is enabled see aslr-bypass
Technique
The canonical chain calls VirtualProtect to flip an existing RW page (often the stack or a heap buffer holding shellcode) to RWX:
1
2
3
4
5
6
7
8
9
[ pop_pad gadgets to set up arguments per __stdcall (x86) ]
ROP layout (x86):
&VirtualProtect ; ret-to
&return_to_shellcode ; return address after VirtualProtect
lpAddress ; pointer to shellcode page
dwSize ; e.g. 0x1000
flNewProtect = 0x40 ; PAGE_EXECUTE_READWRITE
&writable_dword ; lpflOldProtect
x86_64 follows fastcall: rcx/rdx/r8/r9 → use pop rcx ; ret, pop rdx ; ret, etc. mona.py automates chain generation:
1
2
!mona rop -m *.dll -cp nonull -n
!mona modules
Alternative API targets:
VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE)thenmemcpyshellcodeWriteProcessMemoryon the current process to copy shellcode into a known RX pageNtSetInformationProcesswithProcessExecuteFlagsto disable DEP for the whole process (only works if the process isDEP-OptOut)SetProcessDEPPolicy(0)— legacy 32-bit only
Once the chain returns into the now-executable page, the shellcode runs normally. On modern Windows pair with aslr-bypass, control-flow-guard, and cet-shadow-stack consideration — naive ret-based chains die against CET.
Detection and defence
- DEP
AlwaysOn(default since Win8 x64) blocks per-process opt-out APIs - CFG (control-flow-guard) breaks indirect-call gadget reuse; CET (cet-shadow-stack) breaks the
retchain itself - ACG (Arbitrary Code Guard) blocks
VirtualProtectfrom making pages executable in protected processes (Edge content processes) - EDR telemetry on
VirtualProtect/VirtualAllocwith executable flag from non-image regions is a high-signal hunt
References
- Corelan: Exploit writing tutorial 10 — DEP bypass via ROP — the canonical walkthrough
- Microsoft: DEP — policy modes and APIs
- mona.py docs — chain generation cookbook
Related: rop-chains, aslr-bypass, mona-py, stack-buffer-overflow