In today’s rapidly evolving threat landscape, attackers are constantly innovating to bypass endpoint defenses. One such sophisticated method involves tampering with process creation notifications at the kernel level, leveraging Windows’ internal APIs. In particular, the `PsSetCreateProcessNotifyRoutine` function has become a tool of choice for advanced attackers aiming to disable Endpoint Detection and Response (EDR) systems. This blog explores how rootkits, such as Dark-kill, exploit this mechanism to blindside security defenses.
How EDRs Detect Processes
nt!PspCallProcessNotifyRoutines is a global callback list in “nt!PspCallProcessNotifyRoutines[]”. This is an array of function pointers to registered callbacks (from security tools, antivirus, drivers, etc.).
- The basic Windows Defender (Microsoft Defender Antivirus) file system filter driver is called “WdFilter.sys”. It is located at “C:\Windows\System32\drivers\WdFilter.sys”. It may have a registered callback in nt!PspCallProcessNotifyRoutines, allowing it to investigate newly created processes such as virus.exe.
- WdFilter.sys and mssecflt.sys are EDR drivers for Windows Defender and Microsoft Defender for Endpoint (MDE), respectively. These EDR-specific drivers add callbacks to the global callback list in “nt!PspCallProcessNotifyRoutines[]” to track and respond to process formation events.
When a process creation event happens, these callbacks are activated, allowing the EDR system to analyze the process’s details such as command line arguments, executable image, memory use, and other pertinent information. Using these callbacks, EDR systems can detect and respond to possible threats in real time. This approach enables them to conduct extensive inspections and take appropriate steps, such as halting harmful activities, informing security administrators, or gathering forensic data for further study.
For example, when a suspicious process such as `virus.exe` is launched (say with PID 4543), functions like `CreateProcessA/W` and `NtCreateProcess` initiate a system call that passes through this callback array. Each registered callback gets a chance to inspect the process’s attributes—command line, memory, path, etc., allowing the EDR to log, alert, or block based on threat indicators.
These callbacks are crucial detection mechanisms, and tampering with them can leave the system blind to malicious activity.
Understanding PspCreateProcessNotifyRoutine
The `PspCreateProcessNotifyRoutine` array contains all process creation callbacks from drivers, such as antivirus and EDRs, as well as tools like Sysmon. Security software registers its callbacks using functions like:
`PsSetCreateProcessNotifyRoutine`
`PsSetCreateProcessNotifyRoutineEx`
`PsSetCreateProcessNotifyRoutineEx2`
Each entry in this array represents a kernel-mode callback routine from drivers such as `WdFilter.sys` or `mssecflt.sys`. These routines are executed on every new process creation, ensuring real-time monitoring.
Sample array values
[] Process Callbacks array
[00] exfffff80358635500 (cng.sys + ex55ee)
[01] 0xfffff803590bb7b0 (WdFilter.sys+ 0x7b7b0)
[02] 0xfffff8035846c820 (ksecdd.sysex1c820)
[03] 0xfffff8035967a420 (tcpip.sys + ex5a420)
[04] exfffff80359c3d980 (iorate.sys exd930)
[05] 0xfffff803591237f0 (mssecflt.sys + ex437f0)
These drivers have registered a callback with the nt!PspCallProcessNotify Routines function to give a system call/syscall to these drivers (who will notify their respective applications) when a new process/exe is created. By removing or modifying entries from this array, attackers can disable EDR visibility, effectively preventing it from detecting or responding to new threats.
Registering a Malicious Custom Callback to Block EDR Processes
Attackers can register their custom callback named ‘blockEDR’ using `PsSetCreateProcessNotifyRoutineEx`, via a driver, say `prevent.sys`. This attacker-defined custom callback named `blockEDR` is then called every time a process is created.
Registering a Process Creation Kernel Callback to Prevent the EDR Process from Starting:
NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(blockEDR, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrintEx(0, 0, "[ProcessBlocker] Failed to set process notify routine. Status: 0x%08X\n", status);
return status;
}
- PsSetCreateProcessNotifyRoutineEx(blockEDR, FALSE);
- This function registers a callback routine (blockEDR) that the OS will call every time a process is created or destroyed.
- The second parameter is FALSE, which means adding the callback (not removing it).
- NT_SUCCESS(status);
- Check whether the API call was successful.
- If the registration succeeds, your blockEDR() function gets called on every new process (e.g., malware.exe, notepad.exe).
- If something goes wrong (e.g., too many callbacks registered), it prints a debug message.
Before vs. After the Custom Callback Registration
Before the prevent.sys is loaded/registered in the PspCreateProcessNotifyRoutine array, the PspCreateProcessNotifyRoutine array contains a list of callbacks registered by various security drivers [drivers such as WdFilter.sys (Windows Defender), mssecflt.sys (Microsoft Security)]. By having their callbacks registered, these drivers can inspect the details of newly created processes, analyze their behavior, and take appropriate actions to prevent malicious activities.
After `prevent.sys` is loaded (early at boot):
Once the ‘prevent.sys’ is registered in the PspCreateProcessNotifyRoutine array, the `blockEDR` callback starts operating. This callback checks if the process being created matches a list of targeted EDR processes (like `MsSense.exe`).
If it does, the callback blocks the process creation, effectively stopping the EDR from launching.
Result: The system appears clean, but EDR protections are disabled silently. Attackers can then operate undetected, deploy payloads, and persist for an extended period.
Conclusion: A Silent War at the Kernel Level
The misuse of `PsSetCreateProcessNotifyRoutineEx` exposes a critical vulnerability in the Windows security model. By registering a custom kernel callback, attackers can disable EDR systems before they even start. Rootkits like `prevent.sys` show that kernel-level control over process creation is a powerful and dangerous weapon.
This highlights the urgent need for:
- Secure Boot and driver signing enforcement
- Integrity validation of callback arrays
- Behavioral detection of suspicious callback registrations
- Memory protection of kernel structures like `PspCreateProcessNotifyRoutine[]`
As defenders, it should be an alert that Modern attackers no longer just run malware—they prevent your defenses from even starting. Because once your EDR is blind, your defenses are already down.
References: