Microsoft Learn

Documentation for developers

Writing Your First Driver

This guide will walk you through the process of creating a basic Windows driver. We'll cover the essential steps, from setting up your environment to compiling and testing your driver.

Prerequisites

Before you begin, ensure you have the following installed:

Step 1: Set Up Your Development Environment

Follow the instructions in Preparing Your Computer for Driver Development to configure your system for driver development.

Step 2: Create a New Driver Project

Open Visual Studio and create a new project. Select the "Kernel Mode Driver" template.

  1. In Visual Studio, go to File > New > Project...
  2. Under Templates > Visual C++ > Windows Driver, select Kernel Mode Driver.
  3. Name your project (e.g., "MyFirstDriver") and click OK.

Step 3: Understand the Driver Template

The template provides a basic driver structure. The key files include:

Step 4: Implement Driver Entry and Unload Routines

The core of your driver will be the DriverEntry and DriverUnload routines. DriverEntry is called when the driver is loaded, and DriverUnload is called when it's unloaded.


#include <ntddk.h>

// Define a symbolic name for our device (for demonstration purposes)
#define MY_DEVICE_SYMBOLIC_LINK L"\\DosDevices\\MyFirstDevice"

// Forward declaration of DriverUnload
VOID
DriverUnload(
    _In_ PDRIVER_OBJECT DriverObject
);

// Entry point for the driver
NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)
{
    UNREFERENCED_PARAMETER(RegistryPath);

    NTSTATUS status = STATUS_SUCCESS;
    PDEVICE_OBJECT deviceObject = NULL;

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "MyFirstDriver: DriverEntry called.\n");

    // Set the unload routine
    DriverObject->DriverUnload = DriverUnload;

    // Create a device object
    status = IoCreateDevice(
        DriverObject,               // Pointer to driver object
        0,                          // Device extension size
        NULL,                       // Device name (NULL for function driver)
        FILE_DEVICE_UNKNOWN,        // Device type
        0,                          // Device characteristics
        FALSE,                      // Not exclusive
        &deviceObject               // Pointer to device object
    );

    if (!NT_SUCCESS(status)) {
        DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "MyFirstDriver: Failed to create device object (0x%X).\n", status);
        return status;
    }

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "MyFirstDriver: Device object created successfully.\n");

    // Set IRP dispatch routines (minimum required)
    DriverObject->MajorFunction[IRP_MJ_CREATE] = NULL; // For simplicity, not handling create yet
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = NULL;  // For simplicity, not handling close yet
    DriverObject->MajorFunction[IRP_MJ_READ] = NULL;   // For simplicity, not handling read yet
    DriverObject->MajorFunction[IRP_MJ_WRITE] = NULL;  // For simplicity, not handling write yet

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "MyFirstDriver: Driver loaded successfully.\n");

    return STATUS_SUCCESS;
}

// Driver unload routine
VOID
DriverUnload(
    _In_ PDRIVER_OBJECT DriverObject
)
{
    PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "MyFirstDriver: DriverUnload called.\n");

    // Delete the device object if it exists
    if (deviceObject) {
        IoDeleteDevice(deviceObject);
        DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "MyFirstDriver: Device object deleted.\n");
    }

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "MyFirstDriver: Driver unloaded.\n");
}
            

Step 5: Compile and Build

In Visual Studio, configure your project to build a driver. This typically involves setting the correct configuration (e.g., Win8 Release or Win10 Release) and platform (e.g., x64).

Build the solution (Build > Build Solution). This will create a .sys file in your output directory.

Step 6: Deploy and Test Your Driver

Deploying and testing a driver requires careful setup. We recommend using a virtual machine for testing.

  1. Enable Test Signing: Your driver needs to be signed for testing. You can use a test certificate.
  2. Configure Driver Load: You can load your driver manually using sc.exe or automate the process with a setup utility.
  3. Monitor Output: Use DebugView or Visual Studio's debugger to view the DbgPrintEx messages from your driver.
Important: Developing kernel-mode drivers is a complex and sensitive task. Errors in kernel mode can lead to system instability, including blue screens of death (BSODs). Always test drivers on a non-production system, preferably a virtual machine.

Next Steps

Once you have a basic driver loading, you can start implementing I/O Request Packet (IRP) handlers to respond to requests from user-mode applications and the operating system. Explore topics like:

Refer to the Kernel-Mode Drivers documentation for more advanced topics.