派遣函数(1) - 初识派遣函数

转载自;http://mzf2008.blog.163.com/blog/static/3559978620101114103859212/



IRP与派遣函数< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />

IRP的处理机制类似Windows应用程序的“消息处理”机制,驱动程序接收到不同类型的IRP后,会进入不同的派遣函数,在派遣函数中IRP得到处理。

 

IRP

Windows内核中,有一种数据结构叫做IPR(I/O Request Package),即输入输出请求包。它是与输入输出相关的重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求包。操作系统将I/O请求转化为IRP数据,不用类型的IRP会根据类型传递到不同的派遣函数中。

         IRP是一个很复杂的数据结构。其中两个很重要的属性MajorFunctionMinorFunction,分别记录IRP的主类型和子类型。操作系统根据MajorFunctionIRP“派遣”到不同的派遣函数中,在派遣函数中还可以继续判断这个IRP属于哪种MinorFunction

         一般来说,NT式驱动程序和WDM驱动程序都是在DriverEntry中注册IRP的派遣函数。

 

如下代码:

#pragma INITCODE

extern "C" NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObj,

IN PUNICODE_STRING pRegisterPath )

{

         KdPrint(("Entry DriverEntry!\n"));

         //设置卸载函数

         pDriverObj->DriverUnload = HelloDDKUnload;

         //设置派遣函数

         pDriverObj->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;

         pDriverObj->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;

         pDriverObj->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;

         pDriverObj->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;

 

         NTSTATUS status;

         //创建设备对象

         status = CreateDevice(pDriverObj);

 

         KdPrint(("Leave DriverEntry!\n"));

         return STATUS_SUCCESS;

}

DriverEntry的驱动对象pDriverObj中,有个函数指针数组MajorFunction,这个数据的每个元素都记录着一个函数的地址。通过设置这个数组,可以将IRP的类型和派遣函数关联起来。

在上面的例子中,只设置了4种类型的IRP派遣函数。对于其他没有设置的IRP类型,系统默认这些IRP类型与_IopInvalidDeviceRequest函数关联。

在进入DriverEntry之前,操作系统会将_IopInvalidDeviceRequest的地址填满整个MajorFunction数组。

 

 

IRP类型

IRP类型

来源

IRP_MJ_CREATE

创建设备,CreateFile会产生此IRP

IRP_MJ_CLOSE 

关闭设备,CloseHandle会产生此IRP

IRP_MJ_CLEARUP

清除工作,CloseHandle会产生此IRP

IRP_MJ_DEVICE_CONTROL 

DeviceIoControl函数会产生此IRP

IRP_MJ_PNP

即插即用消息,只有WDM才支持此种IRP

IRP_MJ_PNP_POWER

在操作系统处理电源消息时产生此IRP

IRP_MJ_QUERY _INFORMATION

获取文件长度,GetFileSize会产生此IRP

IRP_MJ_READ

读取设备内容,ReadFIle会产生此IRP

IRP_MJ_SET_INFORMATION 

设置文件长度,GetFileSize会产生此IRP

IRP_MJ_SHUTDOWN

关闭系统前会产生此IRP

IRP_MJ_SYSTEM_CONTROL

系统内部产生的控制信息

IRP_MJ_WRITE

对设备进行WriteFile时产生此IRP

 

 

对派遣函数的简单处理

如下代码:

#pragma PAGEDCODE

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,

                                                                            IN PIRP pIrp)

{

         KdPrint(("Enter HelloDDKDispatchRoutine\n"));

         NTSTATUS status = STATUS_SUCCESS;

         // 设置IRP完成状态为STATUS_SUCCESS,这样,发起I/O请求的API函数(WriteFile)

         //将会返回TRUE;

         pIrp->IoStatus.Status = status;

         //设置IRP操作了多少字节,如果是ReadFile产生的IRP,这个字节数代表从设备中读

//了多少字节。

         pIrp->IoStatus.Information = 0;       // bytes xfered

         // IoCompleteRequest

         IoCompleteRequest( pIrp, IO_NO_INCREMENT );

         KdPrint(("Leave HelloDDKDispatchRoutine\n"));

         return status;

}

 

 

应用程序通过符号链接打开设备

在应用程序中,设备可以通过符号链接进行访问。在驱动程序中,我们通过函数IoCreateSymbolicLink来创建符号链接。加入我们定义的符号链接为\??\HelloDevice,那么我们在应用程序中应该把前面的\??\改写成\\.\,即\\.\HelloDevice,写成C语言字符串形式则为\\\\.\\HelloDevice

 

示例代码:(注:每个IRP对应着自己相应的派遣函数)

int main(void)

{

         HANDLE hDevice;

         //打开设备句柄,触发IRP_MJ_CREATE

         hDevice = CreateFile("\\\\.\\HelloDDK",GENERIC_READ | GENERIC_WRITE,

                   FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

         if (hDevice == INVALID_HANDLE_VALUE)

         {

                   DWORD dwError = GetLastError();

                   printf("%d\n", dwError);

         }

         //关闭设备句柄,触发IRP_MJ_CLEANUP IRP_MJ_CLOSE

         CloseHandle(hDevice);

         return 1;

}

派遣函数(1) - 初识派遣函数 - Fly - 从C开始

 

 

编写一个更通用的派遣函数

首先介绍一个重要的数据结构IO_STACK_LOCATION,I/O堆栈,这个数据结构跟IRP紧密相连。

我们知道,驱动对象会创建一个个的设备对象,并将这些设备对象叠成一个垂直结构。这种垂直的结构很像栈,因此被称为“设备栈”。

IRP会被操作系统发送到设备栈的顶层,如果顶层的设备对象的派遣函数结束了IRP的请求,则这次I/O请求结束。如果没有将IRP的请求结束,那么操作系统会将IRP转发到设备栈的下一层设备进行处理。如果这个设备的派遣函数依然不能结束IRP请求,那么则会继续向下层设备转发。

因此,一个IRP可能会被转发多次。为了记录IRP在每层设备中做的操作。IRP会有一个IO_STACK_LOCATION数组。数组 的元素数应该大于IRP穿越的设备数。每个IO_STACK_LACTION元素记录着对应设备中做的操作。对于本层设备对应 IO_STACK_LOCATION,可以通过IoGetCurrentIrpStackLocation函数得到。

 

PIO_STACK_LOCATION 
  IoGetCurrentIrpStackLocation(
    IN PIRP
  Irp
    );

 

注意:IO_STACK_LOCATION结构中的MajorFunction子域会记录当前IRP的类型。

 

下面的代码演示了派遣函数如何获得当前的IO_STACK_LOCATION,以及如何获得IRP的类型。

 

示例代码:(注:每个IRP对应着同一个派遣函数)

#pragma PAGEDCODE

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDeviceObj, IN PIRP pIrp)

{

         KdPrint(("进入派遣函数中!\n"));

 

         //获取当前设备中的IO_STACK_LOCATION结构

         PIO_STACK_LOCATION pIos;

         pIos = IoGetCurrentIrpStackLocation(pIrp);

 

         //建立一个字符串数组,与IRP类型对应起来

         static CHAR* irpName[] = {

                   "IRP_MJ_CREATE",

                   "IRP_MJ_CREATE_NAMED_PIPE",

                   "IRP_MJ_CLOSE",    

                   "IRP_MJ_READ",     

                   "IRP_MJ_WRITE",    

                   "IRP_MJ_QUERY_INFORMATION",  

                   "IRP_MJ_SET_INFORMATION",  

                   "IRP_MJ_QUERY_EA",

                   "IRP_MJ_SET_EA",

                   "IRP_MJ_FLUSH_BUFFERS",

                   "IRP_MJ_QUERY_VOLUME_INFORMATION",

                   "IRP_MJ_SET_VOLUME_INFORMATION",

                   "IRP_MJ_DIRECTORY_CONTROL",

                   "IRP_MJ_FILE_SYSTEM_CONTROL",

                   "IRP_MJ_DEVICE_CONTROL", 

                   "IRP_MJ_INTERNAL_DEVICE_CONTROL", 

                   "IRP_MJ_SHUTDOWN",

                   "IRP_MJ_LOCK_CONTROL",

                   "IRP_MJ_CLEANUP",

                   "IRP_MJ_CREATE_MAILSLOT",

                   "IRP_MJ_QUERY_SECURITY",

                   "IRP_MJ_SET_SECURITY",

                   "IRP_MJ_POWER",

                   "IRP_MJ_SYSTEM_CONTROL",

                   "IRP_MJ_DEVICE_CHANGE",

                   "IRP_MJ_QUERY_QUOTA",

                   "IRP_MJ_SET_QUOTA",

                   "IRP_MJ_PNP"

         };

        

         UCHAR irpType = pIos->MajorFunction;

         if (irpType >= arraysize(irpName))

         {

                   KdPrint(("不明真相的类型,类型为:%x", irpType));

         }

         else

         {

                   KdPrint(("%s\n", irpName[irpType]));

         }

 

         NTSTATUS status = STATUS_SUCCESS;

         pIrp->IoStatus.Status = status;

         pIrp->IoStatus.Information = 0;

         IoCompleteRequest(pIrp, IO_NO_INCREMENT);

 

         KdPrint(("离开派遣函数!\n"));

         return status;

}

派遣函数(1) - 初识派遣函数 - Fly - 从C开始

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值