Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
BYOVD-DriverKiller — Driver Reverse & Exploitation | Kitploit
Tools/GitHubGitHub/alex3o/byovd-driverkiller
ExploitationReverse EngineeringPost-ExploitationBinary AnalysisLearning & EducationRed Teaming
GitHubalex3o/byovd-driverkiller

BYOVD-DriverKiller

Driver Reverse & Exploitation

View Repository
831431 year agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

BYOVD-DriverKiller

This README (EN) may contain errors !

⚠️ Disclaimer: This project is strictly educational and demonstrative. It is not intended for malicious use.
The goal is to learn reverse engineering methodology and the exploitation steps of a Windows driver.


Here I explain the approach I followed to solve the exercise proposed by d1rk (SaadAhla) https://github.com/SaadAhla, consisting of performing reverse engineering and exploitation on a legitimate, signed driver, not present in blocklists (HVCI, LOLBIN...).
A C program allowing to terminate any active process on the system via this Kernel-mode Driver is available, I detail its operation below.

POC-BYOD

📃 Usage:

root@kitploit:~
DriverKiller.exe <process_name.exe> [-d]

Option -d: Removes the service and the Driver from the system after exploitation.

Testsigning mode must be enabled on the target machine because the Driver’s certificate has expired.


Part 1 - Reverse Engineering

The exercise provides a .sys file, named with its SHA-256 hash.
The first step is to open this file with IDA.
IDA is available for free. You just need to go to the Hex-Rays website to generate a license and download the software.

We start by listing the IAT (Import Address Table) of the Driver and searching for the API call we are interested in: ZwTerminateProcess.

Download Tool
screen1-git

Double-clicking on ZwTerminateProcess redirects us to the compiled code of this function. By selecting the entry and displaying the cross-references, we obtain the list of Driver functions that call it.

screen2-git

We see that the function sub_12EF4, at offset 1CE, uses ZwTerminateProcess. After double-clicking, IDA displays its compiled code.

screen11-git

The decompiled code reveals calls to ZwOpenProcess (which opens a handle to the target process) and to ZwTerminateProcess (which terminates the process via this handle).

Looking at the documentation of ZwOpenProcess (https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwopenprocess), we see that the parameter ClientID corresponds to a pointer indicating the PID of the target process.

On the line above, ClientId.UniqueProcess is initialized with variable v22. This is defined just above:

root@kitploit:~
v22 = (void *)(*(_QWORD *)i + 10);

To understand this assignment, we must identify variable i and the field +10.

screen3-git

Earlier in this function, we see a call to ZwQuerySystemInformation with parameter SYSTEM_PROCESS_INFORMATION. We also see that i is the iterator over the entries of this structure with variable v6.

According to the documentation of ZwQuerySystemInformation: https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation, this function returns an array containing one entry per active process on the system.

The structure SYSTEM_PROCESS_INFORMATION is described here: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation

root@kitploit:~
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    PVOID Reserved2;
    ULONG HandleCount;
    ULONG SessionId;
    PVOID Reserved3;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG Reserved4;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    PVOID Reserved5;
    SIZE_T QuotaPagedPoolUsage;
    PVOID Reserved6;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION;

Reminder: sizes of some types on Windows x64:

  • ULONG = 4 bytes
  • USHORT = 2 bytes
  • HANDLE = 8 bytes
  • PWSTR = 8 bytes
  • KPRIORITY (typedef of LONG) = 4 bytes
  • UNICODE_STRING = 16 bytes, since its structure is:
root@kitploit:~
typedef struct _UNICODE_STRING {
    USHORT Length;        -> 2      
    USHORT MaximumLength; -> + 2 = 4
    PWSTR  Buffer;        -> + 8 = 12 (12 is not a multiple of 8 so 4 bytes of padding are added before Buffer) = 16
} UNICODE_STRING;

Offset calculation of UniqueProcessId:

root@kitploit:~
    ULONG NextEntryOffset;        -> 4
    ULONG NumberOfThreads;        -> + 4 = 8
    BYTE Reserved1[48];           -> + 48 = 56
    UNICODE_STRING ImageName;     -> + 16 = 72
    KPRIORITY BasePriority;       -> + 4 = 76 (76 is not a multiple of 8 so 4 bytes of padding are added) = 80
    HANDLE UniqueProcessId;       -> + 8 = 88

So UniqueProcessId is at offset 0x50 (80 in decimal).

Looking at the assignment of variable v22, we see that i is cast as a pointer QWORD (8 bytes):

root@kitploit:~
v22 = (void *)*((_QWORD *)i + 10);

So v22 corresponds to address of i + 10 * 8 = 80 bytes. This variable thus contains the PID retrieved from the SYSTEM_PROCESS_INFORMATION structure.
To know which PID will be passed to ZwTerminateProcess, we need to analyze the condition surrounding this assignment.

screen4-git

We can see that the process image name is first retrieved:

root@kitploit:~
v9 = (wchar_t *)*((_QWORD *)i + 8);

Because v9 = address of i + 8 × 8 = 64 bytes. This corresponds to the Buffer of the ImageName member, since this member is located at offset 56 + 2 (USHORT) + 2 (USHORT) + 4 (padding) = 64.

Given the manipulations and the loops below, we can hypothesize that a comparison is made between the process name passed as argument (a2) and the active processes on the system (v9/String):

root@kitploit:~
 sub_1C078(String, v9, (int)v13); v17 = strupr(a2); v18 = strupr(String); 

Thus, the parameter a2 is expected to contain the name of the process to be terminated via ZwTerminateProcess. We can see that a2 is a parameter of the function sub_12EF4. To go further, we need to examine the references to this function (I renamed it ZwTerminateProcessCaller for better readability).

screen5-git

We can see that ZwTerminateProcessCaller is called by the function sub_13624 at offset 61A.

screen6-git

Before analyzing this decompiled code, I check the references of function sub_13624 (renamed ZwTerminateProcessCallerCaller) to make sure this code is indeed used after an API call to DeviceIoControl from UserMode.

screen§-git

We can see that ZwTerminateProcessCallerCaller is called by the function sub_14130 (renamed ZwTerminateProcessCallerCallerCaller ... fortunately for us, this is the last one before the entry point 😅).

screen7-git

We can see that ZwTerminateProcessCallerCallerCaller is called by the function sub_1A4A8 at offset 306.

screen8-git

We find the assignment of the function ZwTerminateProcessCallerCallerCaller:

root@kitploit:~
memset64(DriverObject->MajorFunction, (unsigned __int64)ZwTerminateProcessCallerCallerCaller, 0x1Cu);

Which means this function is assigned to all entries of the MajorFunction table (0x1B = 27, and there are 28 major IRPs).

screen9-git

Before returning to function sub_13624 (aka ZwTerminateProcessCallerCaller), we retrieve the Symbolic Name and Device Name (identical here): Viragtlt.

screen12-git

Going back to ZwTerminateProcessCallerCaller, we notice that its second parameter (thus a2) corresponds to MasterIrp->AssociatedIrp.SystemBuffer.

screen13-git

Just above the call to ZwTerminateProcessCaller we find the IOCTL code: -2106392528 (in hexadecimal: 0x82730030).

With this information, we can deduce that to exploit this Driver, one must send a DeviceIoControl API call to the Driver with the name of the process to be terminated in the SystemBuffer.


🔷 Information recovered thanks to reverse engineering:

  • IOCTLCode: 0x82730030
  • Device Name: Viragtlt
  • Symbolic Name: Viragtlt
  • SystemBuffer must contain the target process name

Part 2 - Exploitation

To exploit this Driver (if installed and active on the target machine), it is necessary to open a handle to it, then make a DeviceIoControl API call with a Buffer containing the name of the process to terminate.

For this exercise, I developed a C project that:

  • Checks if the Driver is present and active on the system (with a specific service name):
    • If yes, the program exploits the Driver with a DeviceIoControl API call.
    • If no, the program extracts the driver from its resources, deploys it on the user’s desktop, creates an active service, then exploits the Driver with a DeviceIoControl API call. (Requires admin rights since a service is created.)
  • If the Driver is present on the system but the service is not started, the program attempts to start the service then exploits it with a DeviceIoControl API call.

I also added a -d option that allows to remove the service and the Driver from the system after exploitation.

Here is the behavior of the C program in its full execution cycle:

git


AV/EDR Evasion

In this case, DriverKiller.exe is not detected by Microsoft Defender, neither statically nor dynamically.
Evasion does not really make sense here because the exploited Driver has an expired certificate, making its use in real-world scenarios unlikely.
But for better stealth, one could have implemented:

  • Hiding some API calls in the IAT via custom implementations of GetProcAddress and GetModuleHandle
  • A closer approach to the Kernel for executing API calls (Direct/Indirect Syscalls)
  • Anti-VM / Anti-Debug techniques

⚠️ This project was carried out in a learning context. It may contain inaccuracies or errors. Any suggestion, correction, or discussion is welcome! 😃
Thanks to d1rk (SaadAhla): https://github.com/SaadAhla