Exploitation of Windows DEP to Implement Stealth Breakpoints

Note: The method described in this post only applies to 32-bit targets.


The ability to live debug is a key to reverse engineering a binary sample. However, most malware implement measures to detect debuggers and the breakpoints that they use.

While analyzing a sample, I ran into this problem. The sample contained various methods to eliminate the use of virtually all types of breakpoints that I could find. I was able to implement breakpoints using data execution prevention, a security feature in modern processors.

Software breakpoints are usually implemented by placing an interrupt opcode (0xCC) at the location a debugger wants to breakpoint. When executed, this throws an exception that the debugger is able to catch and handle. The debugger can then replace the interrupt with the instructions that were present before the breakpoint was placed. This method of creating breakpoints often fails on malware, because samples are often sensitive to changes in their memory, scanning and running a CRC every so often.

Hardware breakpoints use special registers inside the CPU. Basically, there are four debug registers that can be filled with addresses on which to breakpoint. There are associated registers for specifying the breakpoint type (on execute/on write/on read). I was hopeful that this method would go undetected, but the malware sample seemed to overwrite the debug registers constantly, making them useless.

I then began looking for alternative methods, when I found a paper describing a method for implementing stealth breakpoints. The authors use a technique that takes advantage of how memory pages work. By setting a page to not-present, an access violation exception will be thrown when it is accessed. This happens on execution, read, and write. This method can be achieved in Windows by using VirtualProtect:

VirtualProtect(target, length, PAGE_NOACCESS, &oldAccess);

Using this method, I was able to get breakpoints when the code was accessed, but it also caused the malware’s CRC to fail. I needed a method that still allowed code to be read, but not executed.


VirtualProtect is able to set the protection level on memory pages as specified by the memory protection constants. In normal 32-bit executables, there is no difference between PAGE_READWRITE and PAGE_EXECUTE_READWRITE. The difference comes into play when data execution prevention (DEP) is enabled. This is a feature of modern processors that allows only specified memory pages to be executed. It helps protect against various types of attacks, such as buffer overflows.

In recent versions of Visual Studio’s compiler, it is enabled by default. In older versions (my target was compiled using an old version), it was not. However, it can easily be enabled at any point in the process’s life, using SetProcessDEPPolicy. If the code was not compiled for DEP, it is necessary to first apply some fix-ups, or all code will trigger an access violation.

One method for this is to patch code on the fly. When an EXCEPTION_ACCESS_VIOLATION is caught, the ExceptionRecord contains the address it failed to access. If this address matches EIP, it is likely that it failed because of DEP. An example for checking this is as follows:

LONG CALLBACK exceptionHandler(
) {
    if (pExceptionInfo->ExceptionRecord->ExceptionCode
        if (pExceptionInfo->ExceptionRecord->ExceptionInformation[1]
        == pExceptionInfo->ContextRecord->Eip) {
            // This instruction is probably blocked
            // because it is not executable
            DWORD oldProtection;
            16, PAGE_EXECUTE_READWRITE, &oldProtection);

A caveat to this is that you need to keep your breakpoint target as PAGE_READWRITE (not executable!).

Another method to patch code for DEP-compatibility is to patch all code at once by enumerating all memory pages using VirtualQuery. You simply loop through all pages and set all of them to PAGE_EXECUTE_READWRITE, except for the one containing code you need to breakpoint.

DWORD oldProtect;
for (LPVOID base = NULL;
VirtualQuery(base, info, sizeof(*info));
base += info.RegionSize) {
    VirtualProtect(base, info.RegionSize, PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect(breakpointTarget, 1, PAGE_READWRITE, &oldProtect);

This method works unless there is code loaded later that will not be set to executable, or if the sample uses crazy polymorphic techniques. The first method worked best in my testing.

A basic outline of the method is as follows:

1. Set up a breakpoint handler using AddVectoredExceptionHandler

2. Turn on process DEP using SetProcessDEPPolicy

3. Set all code pages to PAGE_EXECUTABLE_READWRITE

4. Set the code page you need to breakpoint to PAGE_READWRITE

5. Catch the exception when the code is executed and handle it appropriately.

By doing this, you can access all registers and view the stack when the exception hit using the arguments to your exception handler, which is what I intended to do. If you need to continue executing code, you can use VirtualProtect to set the code to be executable and then continue.


The major problem with this technique is that memory pages are large. Setting your target code to PAGE_READWRITE may cause other code (code near it in memory) to also cause access violations. In my sample, this was not a problem, but a possible solution would be quickly setting the protection to include EXECUTE and then changing it back. This is not a very elegant thing to do, but I am not sure how else to combat the problem.

This method also does not work on 64-bit code, because DEP is always-on.

Example Code

An example of this technique can be found on GitHub. The DEP breakpoint method is used here to hook a call going to a DLL. It is recommended you compile the example and then run it outside of Visual Studio, so that it doesn’t catch the exception for you.


Now read this

Creating an Extensible Packet Manipulation System for an Online Game

Introduction Many reverse engineers get their first taste of reverse engineering by tinkering with video games. Games make fun targets because they have such a wide range of possible exploits and modification opportunities. The most... Continue →