Bypassing Kernel Patch Protections on Windows

Bypassing Kernel Patch Protections on Windows

Kernel Patch Protection (KPP) is part of the NT kernel introduced with consumer versions of Windows NT, and in this post we'll break it


3 min read

Kernel Patch Protection (KPP) is part of the NT kernel security system introduced with Windows XP and Windows Server 2000 SP1. In Win9x kernels, the system allowed you to freely manipulate OS kernel objects and functions.

Until KPP was deployed, it wasn’t uncommon for kernel components of various security products to aggressively set inline hooks to OS kernel functions to enhance detection. But this is also true for malware who also use the same kernel hooks to evade detection and manipulate system functions on a low-level basis . Thats why KPP is one of the major features supporting modern Windows security in the NT era.

The NT Kernel has an API called PsSetCreateProcessNotifyRoutine that can register callbacks for driver-specified routines when processes are created and deleted. A similar function is the PsSetCreateProcessNotifyRoutineEx API. The API with the Ex suffix is notified when processes are about to be created and deleted, and callback routines can refuse to load. PsSetCreateProcessNotifyRoutine only does the notification.


Due to its nature, this function is monitored by KPP because it can be a huge attack surface if it’s able to be abused. PsSetCreateProcessNotifyRoutineEx, PsSetCreateThreadNotifyRoutine and PsSetLoadImageNotifyRoutine are also protected as KPP protects key kernel objects.

KPP will immediately interrupt a software interrupt, an ISR called KiRaiseSecurityCheckFailure and it will raise bugcheck 0x139 if the function is called from an unsigned kernel address space, causing a BSOD that states a kernel check has failed.


Arg1: a39fdcd99ec06505, Reserved
Arg2: b3b6e95ff1433664, Reserved
Arg3: 0000000000000002, Failure type dependent information
Arg4: 0000000000000018, Type of corrupted region, can be

18  : Kernel notification callout modification


The NT kernel has a static array that holds registered callbacks like PspCreateProcessNotifyRoutine, and according to the documentation

For Windows Vista and later versions of Windows, the system can register up to 64 process-creation callback routines.

counter = 0i64;
while ( 1 )
    CallbackBlock=ExReferenceCallBackBlock(&PspCreatcProcessNotifyRoutine[counter]);// Acquire Rundown Protection
    _CallbackBlock = CallbackBlock;
    if (CallbackBlock)
        LODWORD(_Remove) = _Remove & 0xFFFFFFFE;
        // Try remove NotifyRoutine if matches
        if ( *((_QWORD *)CallbackBlock + 1) == _NotifyRoutine
          && *((_DWORD *)CallbackBlock + 4) == (_DWORD)_Remove
          && ExCompareExchangeCallBack(&PspCreateProcessllotifyRoutine[counter],0164,(__int64)CallbackBlock))

This array is able to be affected by DKOM (Direct Kernel Object Manipulation) attacks due to the callback being inserted directly into the array without going through the system call, thereby evading the KPP. You can get a pointer to this array by calculating the RVA (Relative Virtual Address) from the ntoskrnl base address.

// this array (sizeof 64) contains all callback blocks
PVOID* PspCreateProcessNotifyRoutine = &*( PVOID* )

Arrays have a block called Callback Block, which is generated by ExAllocateCallBack. ExAllocateCallBack is an undocumented NT kernel internal function. In the original syscall behavior, the function stores a pointer to the callback function that was actually passed to PspSetCreateProcessNotifyRoutine, and Context stores the value passed as Remove.

    EX_RUNDOWN_REF RundownProtect;
    PVOID Context;

Insert the callback block into the array with ExCompareExchangeCallBack, which is also an undocumented internal NT kernel function. If it succeeds in inserting the value into the array, it increments the value of a counter called PspCreateProcessNotifyRoutineCount which is static like the array.

// increment count
InterlockedIncrement( ( volatile LONG* )
    &*( UINT32* )

The results are that we can hitch a ride and use PsSetCreateProcessNotifyRoutine without triggering a KPP bugcheck and a BSOD.


Since Windows 10 21H2, Microsoft implemented to an update for KPP against this method, after publication of this exploit on GitHub. KPP will now check if the callback entry is within the legitimate address. But threat actors can still bypass this measure by placing callback routine stub on the legitimate RWX memory in code cave. We can also hijack existing callback routines by using inline-hooks not monitored by KPP.