在派遣函数中可以调用IoCompleteRequest函数来结束IRP的处理或者调用IoMarkIrpPending来暂时挂起IRP,将IRP进行异步处理。 一般的IRP_MJ_CLOSE用来关闭内核创建的内核对象,对应用层来说也就是句柄,而IRP_MJ_CLEANUP用来处理被挂起的IRP,所以在这我们需要对CLEANUP的IRP进行处理,在处理它时,我们从链表中依次取出 在处理IRP时除了调用IoCompleteRequest结束之外还可以调用IoCancelIrp来取消IRP请求。 IoReleaseCancelSpinLock(Irp->CancelIrql); } //IRP_MJ_READ 处理 case IRP_MJ_READ: { Irp->IoStatus.Information IRP对应的IRQL,所以这里直接传入Irp->CancelIrql
目录 IRP 派遣函数 与通信方式 一丶IRP 1.1 IRP介绍 理论知识 1.2 IRP的类型 1.3 派遣函数 1.4 设备对象 与符号链接 1.5 IRP堆栈介绍 1.6 派遣函数中的IRP处理 1.2 IRP的类型 当应用层调用 ReadFile WriteFile CreateFile CloseHandle 等WINAPI 函数 则会产生对应的IRP类型的的IRP 也就是 IRP_MJ_CREATE IRP_MJ_WRITE IRP_MJ_READ IRP_MJ_CLOSE 并且传送到驱动的中的派遣函数中。 IRP类型 来源 IRP_MJ_CREATE CreateFile/ZwCreateFile IRP_MJ_READ ReadFile/ZwReadFile IRP_MJ_WRITE WriteFile 否则操作系统就会将IRP再转发到设备栈的下一层设备进行处理。如果设备依旧不能处理,那么继续往下发。 因此IRP会被转发多次。为了记录IRP在每层设备中的操作,IRP会有一个堆栈数组。
在Ring-3与Ring-0通讯方面,操作系统为每一个用户请求打包成一个IRP(IO Request Packet)结构,将其发送至驱动程序并通过识别IRP中的PDO来识别是发送给哪一个设备的。 2.3 IRP处理 I/O请求包IRP是驱动程序操作的中心,IRP是一个内核对象,它是预先定义好的数据结构,带有一组对它进行操作的I/O管理器例程,I/O管理器接受一个I/O请求,然后将它传送到合适的驱动程序栈中的最高驱动程序之前 ,分配并处始化一个IRP,每个I/O请求有主功能代码 2.4 IRP参数比如一个写的I/O请求转换成一个IRP时,I/O管理器填写主要的IRP首部,并构造第一个个栈单元,对写请求来讲,首部包含用户缓冲区信息 ,IRP由IRP首部和一系列的栈单元组成,每个栈单元是一个IO_STACK_LOCATION结构,首部和栈单元指出要作的动作 ,栈中有主要的重要参数如MajorFunction和MinorFunction 2.7 即插即用驱动必须有AddDevice例程并处理各种PnP IRP:IRP_MN_START_DEVICE分配资源并启动一个设备。
并记录当前挂起的IRP,然后在IRP_MJ_CLEARN请求中来完成挂起的IRP请求. 此时因为操作系统自身的机制.会设置ring3初始化的事件. 这点和 IRP 同步与异步一篇中讲解的 IRP超时处理是一样的,唯一不同的就是超时处理是取消IRP. 而我们这里是完成IRP. 其中这里只提供DriverB的两个核心处理函数. 三丶高级驱动程序调用IRP方式 3.1 设备调用方式-同步调用 3.1.1 IRP方式调用简介 所谓IRP方式就是自己申请IRP 然后发送IRP请求去调用DriverB程序 也就是说让我们自己实现 ZwCreateFile IRP_MJ_WRITE IRP_MJ_PNP IRP_MJ_FLUSH_BUFFERS IRP_MJ_SHUTDOWN 通过这些功能号也就知道变相的等于一个函数顶替了几个函数. ( [in] PIRP Irp ); 通过IRP返回他的下一层的堆栈.因为要模拟调用.所以我们要必须填写IRP的结构.
: case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE : case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: case IRP_MN_STOP_DEVICE : case IRP_MN_QUERY_DEVICE_RELATIONS: case IRP_MN_QUERY_INTERFACE: case IRP_MN_QUERY_CAPABILITIES : case IRP_MN_READ_CONFIG: case IRP_MN_WRITE_CONFIG: case IRP_MN_EJECT: case IRP_MN_SET_LOCK : case IRP_MN_READ_CONFIG: case IRP_MN_WRITE_CONFIG: case IRP_MN_EJECT: case IRP_MN_SET_LOCK
串口设备接收到的都是IRP请求.所以我们需要对IRP请求做过滤即可. 而串口过滤的时候我们只关心两种请求, 1.读请求. 2.写请求. 而过滤IRP请求则要关心他的功能号. IRP请求的有主功能号跟次功能号. 相应的在IRP栈空间中.会有一个字节保存了这些功能号. ( IN PIRP Irp ); 参数.IRP结构体 通过IRP结构.获取当前IRP堆栈空间. 在IRP结构中有三个缓冲区. 1.irp->MdlAddress 2.irp->UserBuffer 3.irp->AssociatedIrp.SystemBuffer; 关于IRP结构中的是哪个成员我们可以做一次解析 以及获取IRP堆栈. 获取IRP Buffer空间.那么我们则可以进行写代码了.
IRP与派遣函数 IRP IRP(I/O Request Package)输入输出请求包,IRP的两个最基本的结构是MajorFunction和MinorFunction,分别记录IRP的主要类型和子类型 ZwCreateFile会产生IRP_MJ_CREATE类型的IRP。 下面是不同操作所对应产生的IRP请求列表 IRP类型 来源 IRP_MJ_CREATE 创建设备,CreateFile会产生此IRP IRP_MJ_CLOSE 关闭设备,CloseHandle会产生此IRP IRP_MJ_CLEANUP 清除工作,CloseHandle会产生此IRP IRP_MJ_DEVICE_CONTROL DeviceIoControl函数会产生此IRP IRP_MJ_PNP 即插即用消息 会产生此IRP IRP_MJ_READ 读取设备内容,ReadFile会产生此IRP IRP_MJ_SET_INFORMATION 设置文件长度,SetFileSize IRP_MJ_SHUTDOWN
暴力删除就是这里所讲的 IRP删除.给底层发送IRP即可进行删除文件. 1.步骤 步骤很简单.基本上说完就可以自己写代码做出 1.打开文件.获取文件句柄 (IoCreateFile) 2.根据文件句柄, ) 4.申请IRP(IoAllocateIrp) 5.初始化你申请的IRP 6.获取IRP的下层堆栈,并且初始化信息. (IoGetNextIrpStackLocation) 7.设置回调.系统完成IRP之后会调用你这个回调.所以你需要设置事件同步跟你自己同步,才知道IRP已经发送完了. , PVOID Context ) { //操作系统会设置这个回调 Irp->UserIosb->Status = Irp->IoStatus.Status; Irp (PFILE_OBJECT pFileObj) { /* 1.申请IRP,初始化IRP 2.初始化同步事件,以及设置回调. 3.设置文件属性为默认 4.发送IRP
传输类型 位置 METHOD_IN_DIRECT irp->AssociatedIrp.SystemBuffer METHOD_OUT_DIRECT irp->AssociatedIrp.SystemBuffer 传输类型 位置 METHOD_IN_DIRECT irp->MdlAddress METHOD_OUT_DIRECT irp->MdlAddress METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer RequestorMode = KernelMode, the Irp->AssociatedIrp.SystemBuffer and Irp->UserBuffer fields do not contain ) { Irp->IoStatus.Information = 0; Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; break; } } IoCompleteRequest(Irp
串口设备接收到的都是IRP请求.所以我们需要对IRP请求做过滤即可. 而串口过滤的时候我们只关心两种请求, 1.读请求. 2.写请求. 而过滤IRP请求则要关心他的功能号. IRP请求的有主功能号跟次功能号. 相应的在IRP栈空间中.会有一个字节保存了这些功能号. ( IN PIRP Irp ); 参数.IRP结构体 通过IRP结构.获取当前IRP堆栈空间. 在IRP结构中有三个缓冲区. 1.irp->MdlAddress 2.irp->UserBuffer 3.irp->AssociatedIrp.SystemBuffer; 关于IRP结构中的是哪个成员我们可以做一次解析 以及获取IRP堆栈. 获取IRP Buffer空间.那么我们则可以进行写代码了.
在完成例程中解析穿回来的IRP就可得到对应键盘的信息。 IRP数量都需要减一 g_KeyCount--; if(Irp->PendingReturned) { IoMarkIrpPending( Irp ); } return Irp->IoStatus.Status; } NTSTATUS c2cReadDispathc( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) { PIO_STACK_LOCATION pIroStack; DbgPrint("Hook By Me! ,它注册的完成函数没有被调用,我也不知道为什么 pIroStack = IoGetCurrentIrpStackLocation(Irp); pIroStack->Control =
IRP请求会发送给设备对象.然后驱动对象会捕获.通过分发函数进行处理. 一个驱动对象可以有多个设备对象. 在内核中. 有驱动对象.设备对象. 以及IRP请求. This IRP is an associated IRP. ; 其中IRP注意的事项. 因为IRP发送给设备对象请求.所以有多个设备对象.而IRP结构体是固定的.所以在操作的时候.会有一些变动的参数.为了存储这些变动的参数. 所以IRP结构体中保存了当前IRP堆栈.如果获取这些变动的参数.可以通过IRP堆栈来操作. 其中: CurrentLocation表示的当前是哪一个IRP堆栈.
3.1 IoCancelIrp函数IoCancelIrp函数用于取消一个已经发送到设备驱动程序的IRP(I/O请求包)。 当Wait-Wake操作被取消时,与该操作关联的IRP应该被取消,以确保设备不会在待机期间被唤醒。 3.2 IoSetCancelRoutine函数IoSetCancelRoutine函数用于在IRP中设置一个取消处理函数。当系统尝试取消该IRP时,会调用这个处理函数。 ); // 设置IRP的取消处理函数 Irp->Cancel = TRUE; Irp->CancelRoutine = MyCancelRoutine; // 完成IRP Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT)
比如你对IRP堆栈进行了操作. 在IRP派遣函数中我们不想处理的数据可以通过记录在设备扩展中的目标设备对象,转发IRP进行执行. 在驱动卸载的时候也要进行解除附加. 要给那个IRP设置完成例程(也就是给IRP的子域CompletionRoutine设置) 参数2: 完成例程的回调函数. 参数4: 指定是否IRP被成功完成后进入完成例程 参数5: 指定是否IRP被错误完成后进入的完成例程 参数6: 是否是IRP被取消后... 因此完成例程可以作为通知IRP完成的标志. 并且可以很清楚的知道IRP的完成情况. 4.3 完成例程的巧用 完成例程可以知道IRP的完成情况.
函数会产生此IRP),MinorFunction 为自己定义的控制码的IRP,系统就调用相应的处理IRP_MJ_DEVICE_CONTROL的派遣函数,你在派遣函数中判断MinorFunction ,是自定义的控制码你就进行相应的处理 ) { Irp->IoStatus.Status = STATUS_SUCCESS; //LastError() Irp->IoStatus.Information = 0; //ReturnLength IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器 return = Irp->AssociatedIrp.SystemBuffer; OutputData = Irp->AssociatedIrp.SystemBuffer; InputDataLength ; IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器 return Status;
->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT 决定下发这个IRP或者结束这个IRP 3. = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); 如果我们要打开对应的驱动中的设备对象,在驱动层需要提供IRP_MJ_CREATE的处理函数,将返回给I/O管理器的值填入到IRP的IoStatus这个结构中。 类似的,如果我们通过类似WriteFile、ReadFile的函数发送的相应的IRP,即使我们不需要对这个IRP进行特殊的处理,我们也需要为这些操作提供一个默认的处理函数,至少要向I/0管理器返回一个成功
; ///设置状态为STATUS_SUCCESS 即成功 p_Irp^.IoStatus.Information := 0; IofCompleteRequest(p_Irp, IO_NO_INCREMENT ); ///调用IoCompleteRequest完成IRP Result := STATUS_SUCCESS; end; function KernelTerminateThreadRoutine( ); {取IRP的stack location的指针} dwIoControlCode := psl^.Parameters.DeviceIoControl.IoControlCode; ///取控制码 ^.IoStatus.Status := status; p_Irp^.IoStatus.Information := dwBytesReturned; IofCompleteRequest(p_Irp ] := @DispatchCreateClose; ///这里把IRP_MJ_CREATE IRP_MJ_CLOSE设置到一个函数上 pDriverObject^.MajorFunction[IRP_MJ_CLOSE
首先是我们的3环API API -> 封装数据跟命令 ->调用kerner32或者ntdll的函数 ->进行封装,传送给IRP结构体 ->调用驱动 这里接触了一个新的概念.IRP .IRP结构体其实是3 我们的数据都存放在 IRP中.我们如果要完成例程,那么就设置IRP中的. DispatchCreate IRP_MJ_CREATE DispatchRead IRP_MJ_READ DispatchWrite IRP_MJ_WRITE DisPatchchClose IRP_MJ_CLOSE FileObject内核对象 DispatchClean IRP_MJ_CLEANUP HANDLE为句柄 DisPatchControl irp_mj_device_control 堆栈.我们说过3环调用0环.需要封装在IRP结构中.windows是分层驱动.所以IRP头部是共用的.其余的是栈传递.
; DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL KDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { DbgPrint("create or close happen \n"); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, Irp->IoStatus.Information = 0; // Complete the I/O Request Irp->IoStatus.Status = STATUS_SUCCESS 派遣函数中IRP中的pIrp->MdlAddress记录DeviceIoControl指定的输出缓冲区。
与 IRP_MJ_WRITE这两个派遣函数,在派遣函数内则可以对收到的数据进行各类处理。 首先需要实现初始化各类派遣函数这么一个案例,如下代码则是通用的一种初始化派遣函数的基本框架,分别处理了IRP_MJ_CREATE创建派遣,以及IRP_MJ_CLOSE关闭的派遣,此外函数DriverDefaultHandle ] = DispatchCreate; // 创建派遣函数pDriver->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; // 关闭派遣函数/ / 增加派遣处理pDriver->MajorFunction[IRP_MJ_READ] = DispatchRead; // 读取派遣函数pDriver->MajorFunction[IRP_MJ_WRITE *Irp){NTSTATUS Status = STATUS_SUCCESS;PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);