> For the complete documentation index, see [llms.txt](https://www.221bluestreet.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.221bluestreet.com/internals-reversing/windows-kernel-drivers-101-creating-a-simple-driver.md).

# Windows Kernel Drivers 101 - Creating a Simple Driver

{% hint style="info" %}
If you're only interested in the Driver template and Client template you can skip to

\#driver-code-template-breakdown
{% endhint %}

## Windows Architecture Refresher

Before you are charts that were taken from "Windows Kernel Development" by Pavel Yosifovich, presented here for a quick representation of the general architecture of Windows OS and the general flow of a System Service Request (Syscall).&#x20;

<figure><img src="/files/ACAxPeWFfMCq3nH1hDJ4" alt=""><figcaption><p>Windows Architecture - Taken from "Windows Kernel Programming" by Pavel Yosifovich</p></figcaption></figure>

<figure><img src="/files/myj1gU5o7Rvr9ufcvsNX" alt=""><figcaption><p>System Service Request - Taken from "Windows Kernel Programming" by Pavel Yosifovich</p></figcaption></figure>

## Getting Started with Driver Development

When developing a Driver, you should have remote machine in which you can test the driver, or at the very least have a VM with a snapshot since there is a good chance to crash the system if you're not careful.&#x20;

{% hint style="warning" %}
Modern Windows machines have security mechanisms that only allows trusted drivers to load and execute such as Driver Signature Enforcement (DSE).&#x20;

To conveniently test drivers on modern system we can disable this mechanism using the native tool bcdedit.exe
{% endhint %}

### Requirements

* [ ] Visual Studio 2019 with C++ Workload installed.&#x20;
* [ ] Windows 10 SDK (Software Development Kit)
* [ ] Windows 10 WDK (Windows Driver Kit)

### Frameworks

Common Frameworks for driver development are [WDM ](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-wdm)and [KMDF](https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/writing-a-kmdf-driver-based-on-a-template), I will use WDM in this project.&#x20;

### Type of Drivers

Drivers are divided into two types, User-Mode drivers and Kernel-Mode drivers. Within kernel-mode drivers are additional types of drivers in which you can read more about [here](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/types-of-windows-drivers)

The driver template in this page is a simple software driver.&#x20;

### Enable Test Mode&#x20;

To allow unsigned or self-signed driver to load and execute, use bcdedit.exe

```
bcdedit.exe /set testsigning on 
```

After a successful prompt you will need to reboot the machine.&#x20;

## General Flow of Driver Execution

### **Driver Initialization**

**DriverEntry** is the Entry point of a Driver, it's the equivalent of a main() of user-mode programs. Most software drivers will be required to do the following actions before the driver can receive requests from clients.&#x20;

1. Set unload routine
2. Set dispatch routines the driver support (e.g. IRP\_MJ\_CREATE for example)
3. Create device object -> IoCreateDevice()
4. Create a symbolic link to the device object -> IoCreateSymbolicLink()

<pre class="language-cpp"><code class="lang-cpp">extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {

	UNREFERENCED_PARAMETER(DriverObject);
	UNREFERENCED_PARAMETER(RegistryPath);
	// 1. Setting the DriverUnload (which hasn't been defined yet)
	DriverObject->DriverUnload = UnloadTestDriver;
	
	// 2. Setting Dispatch routine
	DriverObject->MajorFunction[IRP_MJ_CREATE] = BlueDriverCreateClose;
	
	// 3. Create Driver's DeviceObject; This object is accessible from user-mode and allows communication to the driver 
	UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\BlueStreetDriver");
<strong>	PDEVICE_OBJECT DeviceObject;
</strong>	NTSTATUS status = IoCreateDevice(DriverObject, 0, &#x26;devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &#x26;DeviceObject);

	if (!NT_SUCCESS(status)) {
		TRACE("Failed to create device object (0x%08X)\n", status);
		return status;
	}

	// 4. Create Symbolic link for the Device which is accessible from user-mode
	UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\BlueStreetDriver");
	status = IoCreateSymbolicLink(&#x26;symLink, &#x26;devName);
	if (!NT_SUCCESS(status)) {
		TRACE("Failed to create symbolic link (0x%08X)\n", status);
		IoDeleteDevice(DeviceObject);
		return status;
	}

	return STATUS_SUCCESS;
}
</code></pre>

### Communication between User-Mode & Kernel-Mode

On a basic level, for a user-mode client to communicate with a driver it needs to:

1. Open a handle to the device's object -> **CreateFile**()
2. Send IO Control Requests -> **DeviceIoControl**()

{% hint style="info" %}
The handle we open to the Device object is a handle to it's symbolic link which the Driver created, only using the symbolic link can we get a handle and communicate with the driver.&#x20;
{% endhint %}

## Driver & Client Template Breakdown&#x20;

### Template

{% tabs %}
{% tab title="Driver.cpp" %}

```cpp
#include <ntifs.h>
#include "BasicDriverCommon.h"
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define TRACE(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, __VA_ARGS__)

NTSTATUS BlueDriverCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp);
NTSTATUS BlueDriverDeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp);

// MJ Function Create & Close
NTSTATUS BlueDriverCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
	UNREFERENCED_PARAMETER(DeviceObject);

	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// MJ Function Control Device
NTSTATUS BlueDriverDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {

	UNREFERENCED_PARAMETER(DeviceObject);
	TRACE("Entered Device Control MJ Function\n");

	// get our IO_STACK_LOCATION
	auto stack = IoGetCurrentIrpStackLocation(Irp); // IOC_STACK_LOCATION*
	auto status = STATUS_SUCCESS;

	switch (stack->Parameters.DeviceIoControl.IoControlCode) {

	case IOCTL_BLUESTREET_SEND_DATA:
	{
		TRACE("BLUESTREET_SEND_DATA Control Activated\n");

		// Data from IRP can be accessed via stack->Parameters.DeviceIoControl.Type3InputBuffer
		auto BufferContent = (UINT32*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
		int bufferSize = sizeof(*BufferContent);

		TRACE("BufferSize: %i\n", bufferSize);
		TRACE("BufferContent: %u\n", *BufferContent);
		break;
	}

	default:
		status = STATUS_INVALID_DEVICE_REQUEST;
		break;
	}
	Irp->IoStatus.Status = status;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return status;
}



void UnloadTestDriver(_In_ PDRIVER_OBJECT DriverObject) {

	TRACE("Sample Driver Unload called\n");
	UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\BlueStreetDriver");
	// Delete symbolic link
	IoDeleteSymbolicLink(&symLink);

	// Delete Device object
	IoDeleteDevice(DriverObject->DeviceObject);
}

extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {

	UNREFERENCED_PARAMETER(DriverObject);
	UNREFERENCED_PARAMETER(RegistryPath);
	TRACE("[+] Entered DriverEntry\n");

	// Configuring our Driver's MAJOR Functions 
	DriverObject->DriverUnload = UnloadTestDriver;
	DriverObject->MajorFunction[IRP_MJ_CREATE] = BlueDriverCreateClose;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = BlueDriverCreateClose;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = BlueDriverDeviceControl;

	// Create Driver's DeviceObject; This object is accessible from user-mode and allows communication to the driver 
	UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\BlueStreetDriver");
	PDEVICE_OBJECT DeviceObject;
	NTSTATUS status = IoCreateDevice(DriverObject, 0, &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);

	if (!NT_SUCCESS(status)) {
		TRACE("Failed to create device object (0x%08X)\n", status);
		return status;
	}

	// Create Symbolic link for the Device which is accessible from user-mode
	UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\BlueStreetDriver");
	status = IoCreateSymbolicLink(&symLink, &devName);
	if (!NT_SUCCESS(status)) {
		TRACE("Failed to create symbolic link (0x%08X)\n", status);
		IoDeleteDevice(DeviceObject);
		return status;
	}

	return STATUS_SUCCESS;
}

```

{% endtab %}

{% tab title="Client.cpp" %}

```cpp
#include <Windows.h>
#include <stdio.h>
#include "..\BasicDriver\BasicDriverCommon.h"

int Error(const char* message) {
    printf("%s (error=%d)\n", message, GetLastError());
    return 1;
}


int main(int argc, const char* argv[])
{
    unsigned int data = 1234;

    // Get handle to device object n;
    HANDLE hDevice = CreateFile(L"\\\\.\\BlueStreetDriver", \
        GENERIC_WRITE, FILE_SHARE_WRITE, \
        nullptr, OPEN_EXISTING, 0, nullptr);

    if (hDevice == INVALID_HANDLE_VALUE) {
        return Error("[!] Failed to open device");
    }

    DWORD returned;
    BOOL success = DeviceIoControl(hDevice,
        IOCTL_BLUESTREET_SEND_DATA,
        &data,
        sizeof(data),
        nullptr, 0,
        &returned, nullptr);

    if (success)
        printf("[+] IOCTL was successfully sent\n");
    else
        Error("[!] Failed sending IOCTL\n");

    CloseHandle(hDevice);
}
```

{% endtab %}

{% tab title="BasicDriverCommon.h" %}

```cpp
#pragma once 
#define BLUESTREET_DEVICE 0x8000 
#define IOCTL_BLUESTREET_SEND_DATA CTL_CODE(BLUESTREET_DEVICE,
0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
```

{% endtab %}
{% endtabs %}

### Breakdown

PLACEHOLDER

## Testing the Driver

1. Installing the Driver as a service with sc.exe

<figure><img src="/files/ClFCbUYypeGY0QhQ3NNE" alt=""><figcaption></figcaption></figure>

2\. Starting the service and using Winobj.exe (sysinternals) to verify the symbolic link to the Device was created successfully.&#x20;

<figure><img src="/files/u8Qw7o9vNxZKt3lbpyOG" alt=""><figcaption></figcaption></figure>

3\. Executing the client process that initiates a DeviceIoControl() to the driver and using DebugView\.exe (sysinternals) to observe the TRACE messages and verify the data from user-mode passed to the driver.&#x20;

<figure><img src="/files/owb7qj89CqoqTNgnlSks" alt=""><figcaption></figcaption></figure>

## Generic Notes about Drivers

PLACEHOLDER

## References and Additional Read

For actual driver / kernel development I recommend these sources for start:

* <https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/>
* <https://www.amazon.com/Windows-Kernel-Programming-Pavel-Yosifovich/dp/1977593372>
