# 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>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.221bluestreet.com/internals-reversing/windows-kernel-drivers-101-creating-a-simple-driver.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
