驱动开发——HelloWDM驱动

news/2024/11/16 7:51:51/

友链

hellowdm.h

// 保证头文件只被编译一次
// 因为在实际项目中,一个头文件可能会被另一个头文件包含
// 比如b.h中包含了a.h
// 然后在c.c中有如下代码:
/*
#include<a.h>
#include<b.h>
*/
// 这样一来,a.h就被包含了两次
// 而#pragma once可以保证a.h只被编译一次
// 从而提高编译效率
#pragma once// 下面的这段条件编译在C++项目中是非常常见的
// 它使得我们可以在C++项目中使用C中的头文件
#ifdef __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#ifdef __cplusplus
}
#endif// 分页标记、非分页标记和初始化内存块
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
// INIT标志指明函数只是在加载的时候需要载入内存
// 在驱动程序成功加载之后,函数可以从内存中卸载掉
#define INITCODE code_seg("INIT")#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")#define arraysize(p)    (sizeof(p)/sizeof((p)[0]))// 定义机构体 _DEVICE_EXTENSION ,并起别名
// 设备扩展结构体
// 该结构体广泛应用于驱动程序中
// 根据不同程序的需要,用于补充定义设备的相关信息
typedef struct _DEVICE_EXTENSION {PDEVICE_OBJECT fdo; PDEVICE_OBJECT NextStackDevice;UNICODE_STRING ustrDeviceName;      // 设备名称UNICODE_STRING ustrSymLinkName;     // 符号链接名称
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;// 函数声明
// 这个IN关键字可能是用来说明参数是传入参数
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT pDriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject);
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,IN PIRP Irp);
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,IN PIRP Irp);
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject);extern "C"
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath);

HelloWDM.cp

#include "HelloWDM.h"/*
初始化驱动程序,定位和申请硬件资源,创建内核对象
参数列表pDriverObject:从IO管理器中传进来的驱动对象pRegistryPath:驱动程序在注册表中的路径
返回值:返回初始化驱动状态
*/
// 使用extern "C"对该函数进行修饰,这样在编译的时候会编译成_DriverEntry@8
// 如果没有这个修饰符的话,编译器会按照C++的符号名进行编译,链接的时候会报错
// 指明函数是加载到INIT内存区域中
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{// 运行在内核中的程序是没有console的,所以只能使用KdPrint宏来输出调试信息// 这个宏只在调试版本中有作用(Free)// 在发行版本中不执行任何操作(Checked)KdPrint(("Enter DriverEntry\n"));// 注册其他驱动调用函数入口// 将我们自己定义的函数的地址传送给操作系统// 操作系统会在合适的时候调用这些函数// AddDevice回调函数只出现在WDM驱动程序中,在NT式的驱动程序中是没有这个回调函数的// 该函数的作用就是创建设备对象并由PNP(即插即用)管理器调用pDriverObject->DriverExtension->AddDevice               = HelloWDMAddDevice;// 对PNP的IRP处理,IRP会在后面介绍,这也是NT式和WDM式驱动的重大区别之一pDriverObject->MajorFunction[IRP_MJ_PNP]                = HelloWDMPnp;// 这里只是演示,因此全部指向了一个简单的函数HelloWDMDispatchRoutinepDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]     =pDriverObject->MajorFunction[IRP_MJ_CREATE]             =pDriverObject->MajorFunction[IRP_MJ_READ]               = pDriverObject->MajorFunction[IRP_MJ_WRITE]              = HelloWDMDispatchRoutine;// 注册卸载函数pDriverObject->DriverUnload                             = HelloWDMUnload;KdPrint(("Leave DriverEntry\n"));return STATUS_SUCCESS;
}// 定义IRP默认处理函数
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{// 确保该例程处于APC_LEVEL之下PAGED_CODE();KdPrint(("Enter DefaultPnPHandler\n"));// 略过当前堆栈IoSkipCurrentIrpStackLocation(Irp);KdPrint(("Leave DefaultPnpHandler\n"));// 使用下层堆栈的驱动设备对象处理此IRPreturn IoCallDriver(pdx->NextStackDevice, Irp);
}#pragma PAGEDCODE
// 该函数类似于NT式驱动中的卸载例程,在WDM式的驱动中,卸载历程几乎不用做处理
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{PAGED_CODE();KdPrint(("Enter HandleRemoveDevice\n"));Irp->IoStatus.Status = STATUS_SUCCESS;// 调用默认的IRP处理函数NTSTATUS status = DefaultPnpHandler(pdx, Irp);// 删除符号链接IoDeleteSymbolicLink(&pdx->ustrSymLinkName);// 调用IoDetachDevice函数将fdo从设备堆栈中脱离if(pdx->NextStackDevice) IoDetachDevice(pdx->NextStackDevice);// 删除fdoIoDeleteDevice(pdx->fdo);KdPrint(("Leave HandleRemoveDevice\n"));return status;
}// 在WDM式的驱动程序中,创建设备的工作不再由DriverEntry承担,而是通过AddDevice回调函数
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(/* pDriverObject是由PNP管理器传递进来的驱动对象,和DriverEntry函数中的参数是一致的 */IN PDRIVER_OBJECT pDriverObject,/* PhysicalDeviceObject是由PNP管理器传递进来的底层驱动设备对象,在NT式的驱动中没有这个概念 */IN PDEVICE_OBJECT PhysicalDeviceObject)
{// 这是一个宏,如果该例程所在的中断请求级超过APC_LEVEL时,会产生一个断言,该断言会使程序终止并报错出错地址// 中断请求级将会在后面介绍PAGED_CODE();KdPrint(("ENter HelloWDMAddDevice\n"));NTSTATUS            status;PDEVICE_OBJECT      fdo;// 设备名称// 构造Unicode字符串用来存储此设备对象的名称UNICODE_STRING      devName;   RtlInitUnicodeString(&devName, L"\\Device\\MyWDMDevice");// 创建设备status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&(UNICODE_STRING)devName,FILE_DEVICE_UNKNOWN,0, FALSE,&fdo);if(!NT_SUCCESS(status))return status;// 填写设备的扩展结构体PDEVICE_EXTENSION pdx    = (PDEVICE_EXTENSION)fdo->DeviceExtension;pdx->fdo                = fdo;// 将fdo(功能设备对象)挂接在设备堆栈上,并将其返回值记录到设备扩展结构中pdx->NextStackDevice    = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);pdx->ustrDeviceName     = devName;// 创建符号链接// 设备名称只在内核态可见,用户程序是看不到的// 因此需要暴露出一个符号连接,该符号链接指向真正的设备名称UNICODE_STRING symLinkName;RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");pdx->ustrSymLinkName = symLinkName;status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName, &(UNICODE_STRING)devName);// 创建成功则返回,否则调用IoDeleteDevice删除设备if(!NT_SUCCESS(status)){// 不知道为什么这里又尝试创建了一次,没有删除设备,而是删除的符号链接// 如果失败了,不应该是根本就没有创建出来符号链接?还用的着删除?// 感觉这代码写的有问题,我给他注释掉了/*IoDeleteSymbolicLink(&pdx->ustrSymLinkName);status = IoCreateSymbolicLink(&symLinkName, &devName);if(!NT_SUCCESS(status)){return status;}*/IoDeleteDevice(fdo);return status;}// 设置设备为BUFFER_IO设备,并指明驱动初始化完成fdo->Flags = fdo->Flags | DO_POWER_PAGABLE;fdo->Flags = fdo->Flags & ~DO_DEVICE_INITIALIZING;KdPrint(("Leave HelloWDMAddDevice\n"));return STATUS_SUCCESS;
}// 定义HelloWDMPnp函数,对即插即用IRP进行处理
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,IN PIRP Irp)
{PAGED_CODE();KdPrint(("Enter HelloWDMPnp\n"));NTSTATUS status = STATUS_SUCCESS;PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;// 得到当前IRP的堆栈,设备堆栈是一个非常复杂的概念,后面会讲到PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);// 对函数表进行初始化// 将对应类别的即插即用IRP调用做不同的处理static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) ={DefaultPnpHandler,      // IRP_MN_START_DEVICEDefaultPnpHandler,      // IRP_MN_QUERY_REMOVE_DEVICEHandleRemoveDevice,     // IRP_MN_REMOVE_DEVICEDefaultPnpHandler,      // IRP_MN_CANCEL_REMOVE_DEVICEDefaultPnpHandler,      // IRP_MN_STOP_DEVICEDefaultPnpHandler,      // IRP_MN_QUERY_STOP_DEVICEDefaultPnpHandler,      // IRP_MN_CANCEL_STOP_DEVICEDefaultPnpHandler,      // IRP_MN_QUERY_DEVICE_RELATIONSDefaultPnpHandler,      // IRP_MN_QUERY_INTERFACEDefaultPnpHandler,      // IRP_MN_QUERY_CAPABILITIESDefaultPnpHandler,      // IRP_MN_QUERY_RESOURCESDefaultPnpHandler,      // IRP_MN_QUERY_RESOURCE_REQUIREMENTSDefaultPnpHandler,      // IRP_MN_QUERY_DEVICE_TEXTDefaultPnpHandler,      // IRP_MN_FILTER_RESOURCE_REQUIREMENTSDefaultPnpHandler,      // DefaultPnpHandler,      // IRP_MN_READ_CONFIGDefaultPnpHandler,      // IRP_MN_WRITE_CONFIGDefaultPnpHandler,      // IRP_MN_EJECTDefaultPnpHandler,      // IRP_MN_SET_LOCKDefaultPnpHandler,      // IRP_MN_QUERY_IDDefaultPnpHandler,      // IRP_MN_QUERY_PNP_DEVICE_STATEDefaultPnpHandler,      // IRP_MN_QUERY_BUS_INFORMATIONDefaultPnpHandler,      // IRP_MN_DEVICE_USAGE_NOTIFICATIONDefaultPnpHandler,      // IRP_MN_SURPRISE_REMOVAL};ULONG fcn = stack->MinorFunction;if(fcn >= arraysize(fcntab)){// 对于未知的IRP类别,使用DefaultPnpHandler函数去处理status = DefaultPnpHandler(pdx, Irp);return status;}#if DBGstatic char* fcnname[] = {"IRP_MN_START_DEVICE","IRP_MN_QUERY_REMOVE_DEVICE","IRP_MN_REMOVE_DEVICE","IRP_MN_CANCEL_REMOVE_DEVICE","IRP_MN_STOP_DEVICE","IRP_MN_QUERY_STOP_DEVICE","IRP_MN_CANCEL_STOP_DEVICE","IRP_MN_QUERY_DEVICE_RELATIONS","IRP_MN_QUERY_INTERFACE","IRP_MN_QUERY_CAPABILITIES","IRP_MN_QUERY_RESOURCES","IRP_MN_QUERY_RESOURCE_REQUIREMENTS","IRP_MN_QUERY_DEVICE_TEXT","IRP_MN_FILTER_RESOURCE_REQUIREMENTS","","IRP_MN_READ_CONFIG","IRP_MN_WRITE_CONFIG","IRP_MN_EJECT","IRP_MN_SET_LOCK","IRP_MN_QUERY_ID","IRP_MN_QUERY_PNP_DEVICE_STATE","IRP_MN_QUERY_BUS_INFORMATION","IRP_MN_DEVICE_USAGE_NOTIFICATION","IRP_MN_SURPRISE_REMOVAL"};KdPrint(("PNP Request (%s)\n", fcnname[fcn]));
#endifstatus = (*fcntab[fcn])(pdx, Irp);KdPrint(("Leave HelloWDMPnp\n"));return status;
}#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,IN PIRP Irp)
{// 和之前的HelloDDK代码几乎一致:https://blog.csdn.net/ma_de_hao_mei_le/article/details/125596301PAGED_CODE();KdPrint(("Enter HelloWDMDispatchRoutine\n"));Irp->IoStatus.Status = STATUS_SUCCESS;Irp->IoStatus.Information = 0;IoCompleteRequest(Irp, IO_NO_INCREMENT);KdPrint(("Leave HelloWDMDispatchRoutine\n"));return STATUS_SUCCESS;
}#pragma PAGEDCODE
// 负责驱动程序的卸载
// 由于驱动的卸载任务放到了IRP_MN_REMOVE_DEVICE处理函数中
// 所以没什么需要HelloWDMUnload去做的,打印两行调试信息就行了
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{PAGED_CODE();KdPrint(("Enter HelloWDMUnload\n"));KdPrint(("Leave HelloWDMUnload\n"));
}

source

TARGETNAME=HelloWDM
TARGETTYPE=DRIVER
DRIVERTYPE=WDM
TARGETPATH=OBJINCLUDES=$(BASEDIR)\inc;\$(BASEDIR)\inc\ddk;\SOURCES=HelloWDM.cpp\

http://www.ppmy.cn/news/734959.html

相关文章

windows内核驱动开发

友链 hellodriver 使用VS2019 项目地址 代码 // I dont like NTSTATUS, it like shit typedef NTSTATUS _nt;/* your driver must implement two basic event callback fucnions at least which is:- DRIVER_INITIALIZE DriverEntry- EVT_WDF_DRIVER_DEVICE_ADD KmdfHello…

数据标注:数字病理学中的人工智能

在数字病理学中&#xff0c;组织样本的显微图像被传输到计算机&#xff0c;并使用先进的图像处理技术和计算机视觉对其进行分析。 医学图像和诊断的数字化为病理学中的人工智能开辟了途径。病理学家可以使用机器学习模型来进行增强分析并提高结果准确性。此外&#xff0c;病理学…

基于FreeRTOS的嵌入式设备管理关键技术研究及实现(学习二)

嵌入式操作系统FreeRTOS FreeRTOS是一个专门为轻量级嵌入式应用设计的迷你操作系统&#xff0c;它的主要功能由IPC、时钟管理、内存管理、任务通知以及任务调度等部分构成。 FreeRTOS的代码可以分解为三个主要区块&#xff1a;任务调度、通讯、硬件库。 任务调度&#xff1a;F…

蒲公英枸杞菊花可以一起泡茶喝吗?

这两年花草茶、花果茶甚是流行和风靡&#xff0c;加上人们对健康养生越来越重视&#xff0c;像蒲公英、菊花和枸杞这样组合在一起&#xff0c;人们也不陌生&#xff0c;三者不但都是传统的中药&#xff0c;更是一种食材&#xff0c;不管是泡水、煎汤、煮粥&#xff0c;都能起到…

常使用电脑的人可使用的护眼软件

我不是从事IT行业&#xff0c;是做计算模拟相关的。 因为公司网络限制先前使用电脑管家的健康小助手的一些小功能&#xff0c;但有天不能使用了&#xff0c;卸载不能自己安装。 试着找了找其他可代替的软件&#xff0c;发现了更多有用的。 定时休息 win7可以设置每隔1小时让电…

ubuntu各种实践笔记

一、精选命令 CtrlAltT&#xff1a;运行终端 CTRL Shift T&#xff1a;新建标签页。 CtrlShiftC/V&#xff1a;在终端上复制/粘贴 CtrlL&#xff1a;显示当前文件夹的地址栏&#xff0c;按ESC恢复 Tab&#xff1a;终端上自动补全命令名或文件名 CtrlH&#xff1a;单次显示当…

简化电脑操作,不让多余操作浪费你的生命

一、桌面管理 1、简单美化 以高效为宗旨&#xff0c;我向来是反对使用桌面美化软件的&#xff0c;类似于动态壁纸&#xff0c;活动小窗口&#xff0c;花哨的Dock栏&#xff0c;这些分散注意力的软件一概不用&#xff0c;我们需要高效&#xff0c;就不应该把注意力放在桌面上。…

简单聊聊程序员的健康问题

起因 为什么想聊这个话题呢&#xff0c;最近一段时间&#xff0c;加班比较严重&#xff0c;我想应该有很多朋友的状态和我差不多&#xff0c;昨天头晕眼胀的&#xff0c;而且坐久了腰还疼。 所以我们关注一下自己的健康&#xff0c;是很有必要的。但我知道的实在有限&#xff0…