NT驱动开发学习笔记003

来源:互联网 发布:淘宝店库存管理 编辑:程序博客网 时间:2024/05/19 18:40

 

题外话:

坚持写笔记,慢慢形成习惯,劳逸结合,GoGoGo


NT驱动开发学习笔记三2011.05.05


NT驱动的卸载例程DriverUnload

A driver's Unload routine, if supplied, should be named XxxUnload, where Xxx is a driver-specific prefix. The driver's DriverEntry routine must store the Unload routine's address in DriverObject->DriverUnload.


如上所说:

pDriverObject->DriverUnload = myDDKUnload;//注意命名

VOID myDDKUnload(INPDRIVER_OBJECTpDriverObject);


myDDKUnload里的传入参数是一个驱动对象的指针。其中根据DRIVER_OBJECT的结构,里面有个成员 PDEVICE_OBJECT DeviceObject指向这个驱动的第一个设备,让后在DEVICE_OBJECT里面有个成员DEVICE_OBJECT *NextDevice; 指向下一个设备对象,然后就可以遍历链表这样遍历设备对象了。

之前在创建设备时候把设备对象指针设备名称设备的符号链接都保存在DeviceExtension 这个结构里了,只要获取这个结构的信息后,删除设备就变得容易了。


pDeviceExtension = (PDEVICE_EXTENSION)pNextDevObj->DeviceExtension;

IoDeleteSymbolicLink(&(pDeviceExtension->szSymLinkName));

pNextDevObj = pNextDevObj->NextDevice;

IoDeleteDevice(pDeviceExtension->pDeviceObject);


NT驱动的派遣例程MajorFunction/MinorFunction

IRP

I/O Request Package,输入输出请求包。这是一个数据结构,上层应用程序与底层驱动程序通讯的时候,应用程序会发出I/O请求,操作系统把I/O请求转化为相应的IRP数据,然后根据类型传递到不同的派遣函数里面来处理。


MajorFunction/MinorFunction

两个函数指针数组,分别记录了IRP主类型副类型,可以IRP类型和派遣函数关联起来。注意:对于一些没有设置的IRP类型,系统默认这些IRP类型 和 函数_IopInvalidDeviceRequest 关联。就是在DriverEntry 之前,操作系统会用_IopInvalidDeviceRequest 的地址来填满整个MajorFunction 数组


下面是派遣函数的原型,返回一个NT状态:

NTSTATUS (*PDRIVER_DISPATCH)(

    IN PDEVICE_OBJECT pDeviceObject,

    IN PIRP pIrp);

由于这是初探,所以就对派遣函数做一个简单的处理吧:


//设置IRP完成状态

pIrp->IoStatus.Status = ntStatus;

//设置IRP操作了多少字节

pIrp->IoStatus.Information = 0;

//处理IRP,这里指明了线程的恢复优先级不增加优先级IO_NO_INCREMENT

IoCompleteRequest(pIrp, IO_NO_INCREMENT);


优先级的概念的理解

ReadFile这个WIN32 API函数为例,看看它的内部操作:

ReadFile 调用 ntdll 中的 NtReadFile。(其中ReadFile 是 WIN32 APIntdll中的NtReadFile 是 Native API)。

ntdll 中的 NtReadFile 进入到 Kernel Model(内核模式),调用 SSDT(系统服务描述表)中指出的 NtReadFile

③系统服务函数 NtReadFile 创建了IRP_MJ_WRITE 类型的IRP,然后把这个IRP发到驱动程序的派遣函数中。

④在派遣函数中一般会用IoCompleteRequestIRP请求结束,而在IoCompleteRequest里会设置等待事件,“睡眠”的线程恢复运行。

理解:

这里一开始会很难理解,想想ReadFile在读一个很大很大的文件(或设备)的时候,ReadFile是不能立刻返回的,而是会去等待一段时间,这段时间就称为睡眠的时间,当IRP请求结束的时候,这个“睡眠”的线程会被唤醒。

所以优先级可以让一些阻塞的线程优先恢复运行,这是很重要的,例如键盘、鼠标等设备,你在读文件的时候,他们不是也能很快的响应么?实际比这样想更复杂。


NT驱动中代码中断级的基本理解

平时在用户模式下,所有的程序都是在同一个中断级,所以一般人都对这个概念没有什么感觉。

Windows把中断级扩展成了32个中断级别IRQL)。其中0~2级别,即是PASSIVE_LEVEL 到 DISPATCH_LEVEL 级别为软中断级3~31级别硬件中断级。优先级0~31,级别逐渐升高。

对于软中断级,一般的线程都是运行在PASSIVE_LEVEL下的,负责调度线程的内核代码是运行在DISPATCH_LEVEL下的。就是说,DISPATCH_LEVEL下的线程是一种不会被切换的线程,想线程不被切换,就要从PASSIVE_LEVEL 提高到 DISPATCH_LEVEL。硬件的中断级都比软件的高,所以运行的线程都会被硬件中断(DIRQL所打断,这就是为什么OllyDbg这些调试软件下的硬件中断可以过驱动保护下断了。


由于函数如果在一些不对应的中断级运行,就会出现错误,所以在调用内核API之前,都要查WDK文档来了解一个内核API的中断级,要养成这个习惯。

DISPATCH_LEVEL 线程使用的内存空间不能出现缺页中断的,所以说 DISPATCH_LEVEL的调用就是不能出现PAGE Section 中的,所以在预编译的时候要特别注意了。也可以在DISPATCH_LEVEL 代码中添加 PAGED_CODE() 宏来检测。