WDM与KMDF
WDM是微软一开始发布的驱动开发模型,全都是底层内容。在开发时,既要处理硬件,也要处理驱动程序与操作系统内核的交互。
WDF模型,为了降低驱动的开发难度,WDF中对WDM进行了多次封装,比如实现了对象,事件,自动排队等等,同时封装了一下驱动程序中的共同行为。不用再关心IRP分发,调度等问题
KMDF和UMDF是基于WDF的模型,一个是内核模式,一个是用户模式。
选择
虽然标题是开发,但是实际上是逆向,中断,服务例程之类的东西还是需要学的,所以这里还是用WDM开发
之前那篇博客疑似是用KMDF写的,所以从这里开始重来吧。
推荐教程
https://space.bilibili.com/348612384
基本框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| #include <wdm.h>
#define DEVICENAME L"\\DEVICE\\DeviceTest" #define SYMNAME L"\\??\\SymboilcLinkTest"
void DrvUnload(PDRIVER_OBJECT pdriver) {
if (pdriver->DeviceObject) { IoDeleteDevice(pdriver->DeviceObject); UNICODE_STRING symname = {}; RtlInitUnicodeString(&symname, SYMNAME); IoDeleteSymbolicLink(&symname); }
DbgPrint("Driver Unloaded\n"); }
NTSTATUS myCreate(PDEVICE_OBJECT pdevice, PIRP pirp) { NTSTATUS status = STATUS_SUCCESS;
DbgPrint("my divice has be opened\n");
pirp->IoStatus.Status = status; pirp->IoStatus.Information = 0; IoCompleteRequest(pirp, IO_NO_INCREMENT);
return status; } NTSTATUS myClose(PDEVICE_OBJECT pdevice, PIRP pirp) { NTSTATUS status = STATUS_SUCCESS;
DbgPrint("my divice has be closed\n");
pirp->IoStatus.Status = status; pirp->IoStatus.Information = 0; IoCompleteRequest(pirp, IO_NO_INCREMENT);
return status; } NTSTATUS myCleanup(PDEVICE_OBJECT pdevice, PIRP pirp) { NTSTATUS status = STATUS_SUCCESS;
DbgPrint("my divice has be cleaned\n");
pirp->IoStatus.Status = status; pirp->IoStatus.Information = 0; IoCompleteRequest(pirp, IO_NO_INCREMENT);
return status; }
NTSTATUS DriverEntry( PDRIVER_OBJECT driver, PUNICODE_STRING reg_path ) { driver->DriverUnload = DrvUnload;
UNICODE_STRING deviceName = { 0 }; RtlInitUnicodeString(&deviceName, DEVICENAME); PDEVICE_OBJECT pdevice = NULL; NTSTATUS status = IoCreateDevice(driver, 0, &deviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pdevice); if (!NT_SUCCESS(status)) { DbgPrint("Create Device fail:%x\n", status); return status; }
UNICODE_STRING symname = { 0 };
RtlInitUnicodeString(&symname, SYMNAME); status = IoCreateSymbolicLink(&symname, &deviceName);
if (!NT_SUCCESS(status)) { DbgPrint("Create symbolicLink fail:%x\n", status); IoDeleteDevice(pdevice); return status; }
driver->MajorFunction[IRP_MJ_CREATE] = myCreate; driver->MajorFunction[IRP_MJ_CLOSE] = myClose; driver->MajorFunction[IRP_MJ_CLEANUP] = myCleanup;
return 0; }
|
补充之前的驱动开发入门这篇博客,这里需要说明一下对于驱动,设备对象,以及符号链接这三个东西的概念。
驱动:运行在操作系统OS内存中,作为补充操作系统功能的软件。类似于R3里的程序与DLL的关系。一般分为两种,一种是硬件驱动:用来实现接收硬件的数字信号并进行处理,或者实现操作系统对硬件的控制。这里的硬件可以是虚拟硬件,因为驱动是中间层,操作系统只会发送消息,让它执行某些操作,系统并不关心驱动如何完成这些任务。因此,虚拟硬盘(类似将内存当作硬盘用)驱动可以在系统要读取其控制的数据时,将地址转化为内存中的读取等等。另一种是和硬件无关的驱动,只是借助驱动的权限高的特点,来实现R3层不好实现的内容,一般叫它内核模块。
设备对象:操作系统控制硬件时,不是直接和硬件交互,而是中间间隔了硬件抽象层(hal.dll),也就是虚拟设备。虚拟设备需要处理系统的irp请求,比如文件打开,文件关闭等等。这些内容都应该是函数/过程,r0也只能写在都写sys文件(驱动)里面。因此,驱动需要生成并绑定设备对象,之后所有的irp回调,都是由驱动来定义的,系统收到对这个对象的请求,都会先给驱动,在驱动中找到对应的irp回调并触发。
符号链接:操作系统需要一个指明设备对象的句柄才可以知道找哪个设备对象,同时,为了让R3也可以使用不同的硬件,设备对象需要绑定一个全局的符号链接,通过使用这个链接,表示了指向这个设备对象,符号链接是一个字符串。比如调用上面例子的设备,在R3需要使用
1
| CreateFile("\\\\.\\SymboilcLinkTest",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
|