Windows 驱动开发 新手入门(一)

Windows 驱动开发 新手入门(一)

  • 引言
  • 驱动介绍
    • Win10 WDK
  • 建立一个驱动项目
    • 建立MyDriver.cpp
    • 理论知识
      • 驱动入口
      • 派遣函数 MajorFunction
      • Device和SymbolicLink
      • DeviceExtension
      • IRP

引言

首先祝朋友们新年快乐,然后呢,因为无聊,写2篇文章打发时间,而且太久没弄过windows的东西了,算是回顾了,本文是对Windows下的驱动开发有一个简单的介绍,我尽可能写的小白文一些,因为大多数的驱动开发书籍对新手来说还是过于难理解。

本篇文章通过WDM进行介绍,具体NT式驱动例子看这:
Windows 驱动开发 新手入门(二)

驱动介绍

在介绍驱动开发之前,先了解一下基础知识驱动是什么的?

驱动这个词是由Driver直译的,这和平常开发中的测试驱动开发(TDD)中的驱动并不是个意思。
驱动是在内核下工作的,如果你了解过Windows的一些内核对象(如Event Mutex等),你也许会认为在Windows用户层也可以随意获取到内核对象。实际上虽然我们在用户层可以获取到,但这只是通过微软公开的API获取的。

Win10 WDK

Windows Driver Kit 用于开发、测试和部署 Windows 驱动程序(官网原话)

由于我的电脑在去年中旬做过一次系统,所以索性装了VS2019,所以请挑选对应版本的WDK进行安装。
Windows 10 版本 2004 VS 2019 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk

其他版本 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/other-wdk-downloads

建立一个驱动项目

在安装完WDK后,新建项目选择Driver你会看见如下这些类型,VS2019中无法快速建立一个NT式驱动。
Windows 驱动开发 新手入门(一)_第1张图片
所以我们选择WDM驱动,我想先从WDM驱动介绍开始
在这里插入图片描述

可以清楚的看见只有一个inf文件,这个文件是之后驱动安装的文件,我们先跳过。
Windows 驱动开发 新手入门(一)_第2张图片

建立MyDriver.cpp

Source Files目录建立MyDriver.cpp,并且将下面的代码拷贝进去,不要急,代码我会挑出关键部分单讲,并且我尽可能写了每行代码的注释。

//由于我们建立的是CPP文件,所以引入头文件和入口函数需要extern "C"
extern "C" {
     
#include 
}

#define DEVICE_NAME L"\\Device\\MyWdmDevice" //定义设备名称
#define LINK_NAME L"\\??\\MyWdmLink" //定义符号链接,??代表以前的DosDevices
NTSTATUS DriverAddDevice(PDRIVER_OBJECT pDriverObj, PDEVICE_OBJECT pPhysicalDeviceObject);
NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDeviceObj, IN PIRP pIrp);
NTSTATUS WDMPNP(IN PDEVICE_OBJECT pDeviceObj, IN PIRP pIrp);
void DriverUnload(PDRIVER_OBJECT pDriverObj);

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
     

	UNREFERENCED_PARAMETER(pRegistryString); //不用将编译警告等级设为3,不使用 就Unreferenced即可
	DbgPrint("驱动被加载\n");

	//这里和nt驱动的区别是,我们不要去直接给MajorFunction 派遣 IRP_MJ_CREATE之类的
	//这里我们需要用到扩展
	//在WDM驱动程序中DriverEntry不再负责创建设备,而是交由AddDevice例程去创建设备。
	pDriverObj->DriverExtension->AddDevice = DriverAddDevice;
	pDriverObj->DriverUnload = DriverUnload; //这里不是真实的卸载
	//IRP全部派遣到DisPatchRoutine
	for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
     
		pDriverObj->MajorFunction[i] = DispatchRoutine;
	}
	//重写PNP派遣
	pDriverObj->MajorFunction[IRP_MJ_PNP] = WDMPNP; //即插即用服务
	return STATUS_SUCCESS;
}

typedef struct _DEVICE_EXTENSION
{
     
	PDEVICE_OBJECT PDeviceObject; ///< 设备对象
	PDEVICE_OBJECT PNextStackDevice; ///< 下层设备对象指针
	UNICODE_STRING DeviceName; ///< 设备名称
	UNICODE_STRING SymLinkName; ///< 符号链接名

}DEVICE_EXTENSION, * PDEVICE_EXTENSION;

NTSTATUS DriverAddDevice(PDRIVER_OBJECT pDriverObj, PDEVICE_OBJECT pPhysicalDeviceObject)
{
     
	DbgPrint("AddDevice\n");

	NTSTATUS status = STATUS_SUCCESS;

	UNICODE_STRING deviceName;
	UNICODE_STRING linkName;
	PDEVICE_OBJECT pDeviceObj = NULL; //创建设备对象
	PDEVICE_EXTENSION pDeviceExt = NULL; //设备扩展对象
	RtlInitUnicodeString(&deviceName, DEVICE_NAME); //初始化Unicode字符串 设备名称
	RtlInitUnicodeString(&linkName, LINK_NAME);//初始化Unicode字符串 符号链接名称

	//创建设备
	status = IoCreateDevice(pDriverObj, sizeof(PDEVICE_EXTENSION), &deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObj); 
	if (!NT_SUCCESS(status))
	{
     
		return status;
	}
	//让扩展对象指向刚刚创建的设备的扩展
	pDeviceExt = (PDEVICE_EXTENSION)(pDeviceObj->DeviceExtension);
	pDeviceExt->PDeviceObject = pDeviceObj;
	pDeviceExt->DeviceName = deviceName;
	pDeviceExt->SymLinkName = linkName;

	// 将设备对象挂接在设备堆栈上
	pDeviceExt->PNextStackDevice = IoAttachDeviceToDeviceStack(pDeviceObj, pPhysicalDeviceObject);

	//创建符号链接
	status = IoCreateSymbolicLink(&linkName, &deviceName);
	if (!NT_SUCCESS(status))
	{
     
		IoDeleteSymbolicLink(&pDeviceExt->SymLinkName);
		status = IoCreateSymbolicLink(&linkName, &deviceName);
		if (!NT_SUCCESS(status))
		{
     
			return status;
		}
	}

	pDeviceObj->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
	pDeviceObj->Flags &= ~DO_DEVICE_INITIALIZING;

	DbgPrint("Leave AddDevice\n");

	return status;
}

/// @brief 对默认IPR进行处理
NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
{
     
	UNREFERENCED_PARAMETER(pDeviceObject);

	DbgPrint("Enter WDMDispatchRoutine\n");
	NTSTATUS status = STATUS_SUCCESS;

	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = 0;

	IoCompleteRequest(pIrp, IO_NO_INCREMENT);

	DbgPrint("Leave WDMDriverDispatchRoutine\n");
	return status;
}

/// @brief PNP移除设备处理函数
/// @param[in] pDeviceExt 设备扩展对象
/// @param[in] pIrp 请求包
/// @return 状态
NTSTATUS PnpRemoveDevice(PDEVICE_EXTENSION pDeviceExt, PIRP pIrp)
{
     
	KdPrint(("Enter HandleRemoveDevice\n"));

	pIrp->IoStatus.Status = STATUS_SUCCESS;
	// 略过当前堆栈
	IoSkipCurrentIrpStackLocation(pIrp);

	// 用下层堆栈的驱动设备处理此IRP
	NTSTATUS status = IoCallDriver(pDeviceExt->PNextStackDevice, pIrp);

	// 删除符号链接
	IoDeleteSymbolicLink(&pDeviceExt->SymLinkName);

	//调用IoDetachDevice()把设备对象从设备栈中脱开:  
	if (pDeviceExt->PNextStackDevice != NULL)
		IoDetachDevice(pDeviceExt->PNextStackDevice);

	//删除设备对象:  
	IoDeleteDevice(pDeviceExt->PDeviceObject);

	DbgPrint("Leave HandleRemoveDevice\n");

	return status;
}

NTSTATUS WDMPNP(IN PDEVICE_OBJECT pDeviceObj, IN PIRP pIrp)
{
     
	PDEVICE_EXTENSION pDeviceExt = (PDEVICE_EXTENSION)pDeviceObj->DeviceExtension;
	PIO_STACK_LOCATION pStackLoc = IoGetCurrentIrpStackLocation(pIrp);
	unsigned long func = pStackLoc->MinorFunction;

	DbgPrint("PNP Request (%u)\n", func);

	NTSTATUS status = STATUS_SUCCESS;
	switch (func)
	{
     
	case IRP_MN_REMOVE_DEVICE:
		status = PnpRemoveDevice(pDeviceExt, pIrp);
		break;
	default:
		// 略过当前堆栈
		IoSkipCurrentIrpStackLocation(pIrp);

		// 用下层堆栈的驱动设备处理此IRP
		status = IoCallDriver(pDeviceExt->PNextStackDevice, pIrp);
		break;
	}

	DbgPrint("Leave HelloWDMPnp\n");

	return status;
}




/// @brief 驱动程序卸载操作
void DriverUnload(PDRIVER_OBJECT pDriverObj)
{
     
	UNREFERENCED_PARAMETER(pDriverObj);

	DbgPrint("Enter WDMUnload\n");
	DbgPrint("Leave WDMUnload\n");
}

理论知识

驱动入口

在之前写程序时,程序入口函数为main,参数有argc和argv代表着命令行的参数个数及对应的字符串指针,驱动也有入口函数为DriverEntry,其返回类型为NTSTATUS

函数的第一个参数pDriverObj是刚被初始化的驱动对象指针
函数的第二个参数pRegistryString是驱动在注册表中的键值。

派遣函数 MajorFunction

我们在驱动入口函数中看到了pDriverObj->MajorFunction数组,其作用是针对每一个的事件都有与之对应回调函数

因为WDM驱动不在DriverEntry入口中去创建设备和对应的符号链接,而是移到了DriverAddDevice函数中,所以pDriverObj->MajorFunction中我们并没有单独指定pDriverObj->MajorFunction[IRP_MJ_CREATE]pDriverObj->MajorFunction[IRP_MJ_CLOSE],而是将所有的派遣函数指向一个通用的回调函数DispatchRoutine

Device和SymbolicLink

其中设备名称在代码中被我们宏定义L"\\Device\\MyWdmDevice"符号链接被我们定义为L"\\??\\MyWdmLink"??代表着默认的路径,也就是DosDevices

Device是用来和应用层通信的,应用层程序可以通过SymbolicLink找到对应的设备。

IoCreateDevice就是创建设备的函数

  • 第一个参数为驱动对象指针
  • 第二个参数为设备扩展结构(DeviceExtension)大小
  • 第三个参数为设备名称,具体可以查看微软文档。

IoCreateSymbolicLink就是创建符号链接的函数

  • 第一个参数为符号链接名称
  • 第二个参数为设备名称

DeviceExtension

设备扩展结构是和应用层通信时,可以读取或写入的信息的结构,之后NT式驱动的例子中会用到。

IRP

在通用的派遣函数DispatchRoutine中,我们看到了一个参数pIrpIRP的含义是I/O Request Packet缩写,是在驱动中IO请求的结构体,其具体作用,我决定放在NT式驱动中细说。

你可能感兴趣的:(C/C++)