IRP请求处理及完成机制

虚拟串口IRP请求处理流程图解说明 深入解析虚拟串口中IRP请求的完整处理流程,通过图解方式清晰展示数据传递与状态转换,帮助开发者更好理解虚拟串口的工作机制和调试方法。 阅读详情

 

近来学习 Windows 内核方面的东西,觉得对 I/O 处理过程没有一个总体的概念。于是,就花了很长的时间搜集了很多这方面的知识总结了一下。

在 Windows 内核中的请求基本上是通过 I/O Request Packet 完成的。前面说过,设备对象是唯一可以接受请求的实体。下面,我就来详细地说下 IRP 请求是怎么样一步一步完成的。

首先,我们就需要知道 IRP 是怎么产生。 IRP 是由 I/O 管理器发出的, I/O 管理器是用户态与内核态之间的桥梁,当用户态进程发出 I/O 请求时, I/O 管理器就捕获这些请求,将其转换为 IRP 请求,发送给驱动程序。 I/O 管理器无疑是非常重要的,具有核心地位。它负责所有 I/O 请求的调度和管理工作,根据请求的不同内容,选择相应的驱动程序对象,设备对象,并生成、发送、释放各种不同的 IRP 。整个 I/O 处理流程是在它的指挥下完成的。

一个 IRP 是从非分页内存中分配的可变大小的结构,它包括两部分: IRP 首部和辅助请求参数数组,如图 1 所示。这两部分都是由 I/O 管理器建立的。

 

 

 

IRP简单结构图

图 1 IRP 简单结构图

IRP 首部中包含了指向 IRP 输入输出缓冲区指针、当前拥有 IRP 的驱动指针等。

紧接着首部的是一个 IO_STACK_LOCATION 结构的数组。它的大小由设备栈中的设备数确定(设备栈的概念会在下文中阐述)。 IO_STACK_LOCATION 结构中保存了一个 I/O 请求的参数及代码、请求当前对应的设备指针、完成函数指针( IoCompletion )等。

那么,由 I/O 管理器产生的 IRP 请求发送到哪去了呢?这里我们就要来说说设备栈的概念了,操作系统用设备对象( device object )表示物理设备,每一个物理设备都有一个或多个设备对象与之相关联,设备对象提供了在设备上的所有操作。也有一些设备对象并不表示物理设备。一个唯软件驱动程序( software-only driver ,处理 I/O 请求,但是不把这些请求传递给硬件)也必须创建表示它的操作的设备对象。设备常常由多个设备对象所表示,每一个设备对象在驱动程序栈( driver stack )中对应一个驱动程序来管理设备的 I/O 请求。一个设备的所有设备对象被组织成一个设备栈( device stack )。而且, IO_STACK_LOCATION 数组中的每个元素和设备栈中的每个设备是一一对应的,一般情况下,只允许层次结构中的每个设备对象访问它自己对应的 IO_STACK_LOCATION 。无论何时,一个请求操作都在一个设备上被完成, I/O 管理器把 IRP 请求传递给设备栈中顶部设备的驱动程序( IRP 是传递给设备对象的,通过设备对象的 DriverObject 成员找到驱动程序)。驱动程序访问它对应的设备对象在 IRP 中 IO_STACK_LOCATION 数组中的元素检查参数,以决定要进行什么操作(通过检查结构中的 MajorFunction 字段,确定执行什么操作及如何解释 Parameters 共用体字段的内容)。驱动程序可以根据 IO_STACK_LOCATION 结构中的 MajorFunction 字段进行处理。每一个驱动或者处理 IRP ,或者把它传递给设备栈中下一个设备对象的驱动程序。

 

传递 IRP 请求到底层设备的驱动程序需要经过下面几个步骤:

1.       为下一个 IO_STACK_LOCATION 结构设置参数。可以有以下两种方式:

·  调用 IoGetNextIrpStackLocation 函数获得下个结构的指针,再对参数进行赋值;

·  调用 IoCopyCurrentIrpStackLocationToNext 函数(如果第 2 步中驱动设置了 IoCompletion 函数 ),或者调用 IoSkipCurrentIrpStackLocation 函数(如果第 2 步中驱动没有设置 IoCompletion 函数 )把当前的参数传递给下一个。

2.       如果需要的话,调用 IoSetCompletionRoutine 函数设置 IoCompletion 函数进行后续处理。

3.       调用 IoCallDriver 函数将 IRP 请求传递给下一层驱动。这个函数会自动调整 IRP 栈指针,并且执行下一层驱动的派遣函数。

当驱动程序把 IRP 请求传递给下一层驱动之后,它就不再拥有对该请求的访问权,强行访问会导致系统崩溃。如果驱动程序在传递完之后还想再访问该请求,就必须要设置 IoCompletion 函数。 IRP 请求可以再其他驱动程序或者其他线程中完成或取消。

 

当某一驱动程序调用 IoCompleteRequest 函数时, I/O 操作就完成了。这个函数使得 IRP 的堆栈指针向上移动一个位置,如图 2 所示:


IRP完成时栈指针的移动

 

图 2 IRP 完成时栈指针的移动

图 2 所示的当 C 驱动程序调用完 IoCompleteRequest 函数后 I/O 栈的情况。左边的实线箭头表明栈指针现在指向驱动 B 的参数和回调函数;虚线箭头是之前的情况。右边的空心箭头指明了 IoCompletion 函数被调用的顺序。

如果驱动程序把 IRP 请求传递给设备栈中的下层设备之前设置了 IoCompletion 函数,当 I/O 栈指针再次指回到该驱动程序时, I/O 管理器就将调用该 IoCompletion 函数。

IoCompletion 函数的返回值有两种:

( 1 ) STATUS_CONTINUE_COMPLETION :告诉 I/O 管理器继续执行上层驱动程序的 IoCompletion 函数。

( 2 ) STATUS_MORE_PROCESSING_REQUIRED :告诉 I/O 管理器停止执行上层驱动程序,并将栈指针停在当前位置。在当前驱动程序调用 IoCompleteRequest 函数后再继续执行上层驱动的 IoCompletion 函数。

当所有驱动都完成了它们相应的子请求时, I/O 请求就结束了。 I/O 管理器从 Irp‑>IoStatus.Status 成员更新状态信息,从 Irp‑>IoStatus.Information 成员更新传送字节数。

 

IRP 是 I/O request packet 的缩写,即 I/O 请求包。驱动与驱动之间通过 IRP 进行通信。而使用驱动的应用层调用的 CreatFile,ReadFile,WriteFile,DeviceIoControl 等函数,说到底也是使用 IRP 和驱动进行通信。

一个 IRP 由两部分组成。首先是头部或者叫包的固定部分,是一个 IRP 结构。紧跟在这个头部之后的是 I/O stack locations ,这是一个 IO_STACK_LOCATION 结构的数组,这个数组中元素的个数是根据情况而定的,由 IoAllocateIrp( IN CCHAR StackSize , IN BOOLEAN ChargeQuota ) 时的参数 StackSize 决定。而 StackSize 通常由 IRP 发往的目标 DEVICE_OBJECT 的 +30 char StackSize 决定。而这个 StackSize 是由设备对象连入所在的设备栈时,根据在设备栈中位置决定的。我们先看看 IRP 结构和 IO_STACK_LOCATION 结构的定义。

IRP 结构定义如下

struct _IRP (sizeof=112)
+00 int16 Type
+02 uint16 Size
+04 struct _MDL *MdlAddress
+08 uint32 Flags
+0c union __unnamed14 AssociatedIrp
+0c struct _IRP *MasterIrp
+0c int32 IrpCount
+0c void *SystemBuffer
+10 struct _LIST_ENTRY ThreadListEntry
+10 struct _LIST_ENTRY *Flink
+14 struct _LIST_ENTRY *Blink
+18 struct _IO_STATUS_BLOCK IoStatus
+18 int32 Status
+18 void *Pointer
+1c uint32 Information
+20 char RequestorMode
+21 byte PendingReturned
+22 char StackCount
+23 char CurrentLocation
+24 byte Cancel
+25 byte CancelIrql
+26 char ApcEnvironment
+27 byte AllocationFlags
+28 struct _IO_STATUS_BLOCK *UserIosb
+2c struct _KEVENT *UserEvent
+30 union __unnamed15 Overlay
+30 struct __unnamed16 AsynchronousParameters
+30 function *UserApcRoutine
+34 void *UserApcContext
+30 union _LARGE_INTEGER AllocationSize
+30 uint32 LowPart
+34 int32 HighPart
+30 struct __unnamed3 u
+30 uint32 LowPart
+34 int32 HighPart
+30 int64 QuadPart
+38 function *CancelRoutine
+3c void *UserBuffer
+40 union __unnamed17 Tail
+40 struct __unnamed18 Overlay
+40 struct _KDEVICE_QUEUE_ENTRY DeviceQueueEntry
+40 struct _LIST_ENTRY DeviceListEntry
+40 struct _LIST_ENTRY *Flink
+44 struct _LIST_ENTRY *Blink
+48 uint32 SortKey
+4c byte Inserted
+40 void *DriverContext[4]
+50 struct _ETHREAD *Thread
+54 char *AuxiliaryBuffer
+58 struct _LIST_ENTRY ListEntry
+58 struct _LIST_ENTRY *Flink
+5c struct _LIST_ENTRY *Blink
+60 struct _IO_STACK_LOCATION *CurrentStackLocation
+60 uint32 PacketType
+64 struct _FILE_OBJECT *OriginalFileObject
+40 struct _KAPC Apc
+40 int16 Type
+42 int16 Size
+44 uint32 Spare0
+48 struct _KTHREAD *Thread
+4c struct _LIST_ENTRY ApcListEntry
+4c struct _LIST_ENTRY *Flink
+50 struct _LIST_ENTRY *Blink
+54 function *KernelRoutine
+58 function *RundownRoutine
+5c function *NormalRoutine
+60 void *NormalContext
+64 void *SystemArgument1
+68 void *SystemArgument2
+6c char ApcStateIndex
+6d char ApcMode
+6e byte Inserted
+40 void *CompletionKey

IO_STACK_LOCATION 结构定义如下

struct _IO_STACK_LOCATION (sizeof=36)
+00 byte MajorFunction
+01 byte MinorFunction
+02 byte Flags
+03 byte Control
+04 union __unnamed19 Parameters
+04 struct __unnamed20 Create
+04 struct _IO_SECURITY_CONTEXT *SecurityContext
+08 uint32 Options
+0c uint16 FileAttributes
+0e uint16 ShareAccess
+10 uint32 EaLength
+04 struct __unnamed21 CreatePipe
+04 struct _IO_SECURITY_CONTEXT *SecurityContext
+08 uint32 Options
+0c uint16 Reserved
+0e uint16 ShareAccess
+10 struct _NAMED_PIPE_CREATE_PARAMETERS *Parameters
+04 struct __unnamed22 CreateMailslot
+04 struct _IO_SECURITY_CONTEXT *SecurityContext
+08 uint32 Options
+0c uint16 Reserved
+0e uint16 ShareAccess
+10 struct _MAILSLOT_CREATE_PARAMETERS *Parameters
+04 struct __unnamed23 Read
+04 uint32 Length
+08 uint32 Key
+0c union _LARGE_INTEGER ByteOffset
+0c uint32 LowPart
+10 int32 HighPart
+0c struct __unnamed3 u
+0c uint32 LowPart
+10 int32 HighPart
+0c int64 QuadPart
+04 struct __unnamed23 Write
+04 uint32 Length
+08 uint32 Key
+0c union _LARGE_INTEGER ByteOffset
+0c uint32 LowPart
+10 int32 HighPart
+0c struct __unnamed3 u
+0c uint32 LowPart
+10 int32 HighPart
+0c int64 QuadPart
+04 struct __unnamed24 QueryDirectory
+04 uint32 Length
+08 struct _STRING *FileName
+0c int32 FileInformationClass
+10 uint32 FileIndex
+04 struct __unnamed25 NotifyDirectory
+04 uint32 Length
+08 uint32 CompletionFilter
+04 struct __unnamed26 QueryFile
+04 uint32 Length
+08 int32 FileInformationClass
+04 struct __unnamed27 SetFile
+04 uint32 Length
+08 int32 FileInformationClass
+0c struct _FILE_OBJECT *FileObject
+10 byte ReplaceIfExists
+11 byte AdvanceOnly
+10 uint32 ClusterCount
+10 void *DeleteHandle
+04 struct __unnamed28 QueryEa
+04 uint32 Length
+08 void *EaList
+0c uint32 EaListLength
+10 uint32 EaIndex
+04 struct __unnamed29 SetEa
+04 uint32 Length
+04 struct __unnamed30 QueryVolume
+04 uint32 Length
+08 int32 FsInformationClass
+04 struct __unnamed30 SetVolume
+04 uint32 Length
+08 int32 FsInformationClass
+04 struct __unnamed31 FileSystemControl
+04 uint32 OutputBufferLength
+08 uint32 InputBufferLength
+0c uint32 FsControlCode
+10 void *Type3InputBuffer
+04 struct __unnamed32 LockControl
+04 union _LARGE_INTEGER *Length
+08 uint32 Key
+0c union _LARGE_INTEGER ByteOffset
+0c uint32 LowPart
+10 int32 HighPart
+0c struct __unnamed3 u
+0c uint32 LowPart
+10 int32 HighPart
+0c int64 QuadPart
+04 struct __unnamed33 DeviceIoControl
+04 uint32 OutputBufferLength
+08 uint32 InputBufferLength
+0c uint32 IoControlCode
+10 void *Type3InputBuffer
+04 struct __unnamed34 QuerySecurity
+04 uint32 SecurityInformation
+08 uint32 Length
+04 struct __unnamed35 SetSecurity
+04 uint32 SecurityInformation
+08 void *SecurityDescriptor
+04 struct __unnamed36 MountVolume
+04 struct _VPB *Vpb
+08 struct _DEVICE_OBJECT *DeviceObject
+04 struct __unnamed36 VerifyVolume
+04 struct _VPB *Vpb
+08 struct _DEVICE_OBJECT *DeviceObject
+04 struct __unnamed37 Scsi
+04 *Srb
+04 struct __unnamed38 QueryQuota
+04 uint32 Length
+08 void *StartSid
+0c struct _FILE_GET_QUOTA_INFORMATION *SidList
+10 uint32 SidListLength
+04 struct __unnamed29 SetQuota
+04 uint32 Length
+04 struct __unnamed39 QueryDeviceRelations
+04 int32 Type
+04 struct __unnamed40 QueryInterface
+04 struct _GUID *InterfaceType
+08 uint16 Size
+0a uint16 Version
+0c struct _INTERFACE *Interface
+10 void *InterfaceSpecificData
+04 struct __unnamed41 DeviceCapabilities
+04 struct _DEVICE_CAPABILITIES *Capabilities
+04 struct __unnamed42 FilterResourceRequirements
+04 struct _IO_RESOURCE_REQUIREMENTS_LIST *IoResourceRequirementList
+04 struct __unnamed51 ReadWriteConfig
+04 uint32 WhichSpace
+08 void *Buffer
+0c uint32 Offset
+10 uint32 Length
+04 struct __unnamed52 SetLock
+04 byte Lock
+04 struct __unnamed53 QueryId
+04 int32 IdType
+04 struct __unnamed54 QueryDeviceText
+04 int32 DeviceTextType
+08 uint32 LocaleId
+04 struct __unnamed55 UsageNotification
+04 byte InPath
+05 byte Reserved[3]
+08 int32 Type
+04 struct __unnamed56 WaitWake
+04 int32 PowerState
+04 struct __unnamed57 PowerSequence
+04 struct _POWER_SEQUENCE *PowerSequence
+04 struct __unnamed58 Power
+04 uint32 SystemContext
+08 int32 Type
+0c union _POWER_STATE State
+0c int32 SystemState
+0c int32 DeviceState
+10 int32 ShutdownType
+04 struct __unnamed59 StartDevice
+04 struct _CM_RESOURCE_LIST *AllocatedResources
+08 struct _CM_RESOURCE_LIST *AllocatedResourcesTranslated
+04 struct __unnamed60 WMI
+04 uint32 ProviderId
+08 void *DataPath
+0c uint32 BufferSize
+10 void *Buffer
+04 struct __unnamed61 Others
+04 void *Argument1
+08 void *Argument2
+0c void *Argument3
+10 void *Argument4
+14 struct _DEVICE_OBJECT *DeviceObject
+18 struct _FILE_OBJECT *FileObject
+1c function *CompletionRoutine
+20 void *Context

IO_STACK_LOCATION 结构的大小是固定的,+04 到 +14 之间的16个字节是个公用体。

我们大略的看一个例子,或许能让你对 IO_STACK_LOCATION 有点概念

一个驱动程序的应用层程序,调用 DeviceIoControl ,让驱动程序完成一个任务。DeviceIoControl 中会申请一个Irp,初始化它,然后用这个 Irp,调用 IoCallDriver 发往设备栈的最高层。

IoCallDriver 会根据Irp中的当前 IO_STACK_LOCATION 中的 +00 byte MajorFunction,调用发往设备对象所属驱动中的相应函数。

我们看看被传入到设备栈最高层的 irp,

kd> !irp fe403968
Irp is active with 6 stacks 6 is current (= 0xfe403a8c)
No Mdl System buffer = fe3d6068 Thread fe427960: Irp stack trace. 
cmd flg cl Device File Completion-Context
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
>[ e, 0] 0 0 fe4f5df0 fe426688 00000000-00000000 
/Driver/Kbdclass
Args: 00000000 00000004 000b0008 00000000

WinDbg 的 !irp 命令,会显示一个 Irp 的 IO_STACK_LOCATION 数组的一些格式化信息

可以看到传入的这个 Irp,它的最后一个 IO_STACK_LOCATION 是当前的 IO_STACK_LOCATION,里面有传入的一些参数。

>[ e, 0] 0 0 fe4f5df0 fe426688 00000000-00000000 
/Driver/Kbdclass
Args: 00000000 00000004 000b0008 00000000

kd> !strct io_stack_location fe403a8c
struct _IO_STACK_LOCATION (sizeof=36)
+00 byte MajorFunction = 0e .
+01 byte MinorFunction = 00 .
+02 byte Flags = 00 .
+03 byte Control = 00 .
+04 union __unnamed19 Parameters
+04 struct __unnamed33 DeviceIoControl
+04 uint32 OutputBufferLength = 00000000
+08 uint32 InputBufferLength = 00000004
+0c uint32 IoControlCode = 000b0008
+10 void *Type3InputBuffer = 00000000
+14 struct _DEVICE_OBJECT *DeviceObject = FE4F5DF0
+18 struct _FILE_OBJECT *FileObject = FE426688
+1c function *CompletionRoutine = 00000000
+20 void *Context = 00000000

这些就是当前 IO_STACK_LOCATION,里面有参数什么的,这样本层驱动就可以根据 IRP 和当前的 IO_STACK_LOCATION 中的内容,知道该干些什么。本例中,本层驱动根据这个 Irp 做了些处理,发现还需要下层驱动做更多处理,就设置了下一层驱动要使用的 IO_STACK_LOCATION ,然后调用 IoCallDriver ,把这个 Irp 发到了下一层。

在下一层的处理程序中,我们看看收到的 Irp

kd> !irp fe403968
Irp is active with 6 stacks 5 is current (= 0xfe403a68)
No Mdl System buffer = fe3d6068 Thread fe427960: Irp stack trace. 
cmd flg cl Device File Completion-Context
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000 

Args: 00000000 00000000 00000000 00000000
>[ f, 0] 0 0 fe4f5020 fe426688 00000000-00000000 
/Driver/i8042prt
Args: 00000000 00000004 000b0008 00000000
[ e, 0] 0 0 fe4f5df0 fe426688 00000000-00000000 
/Driver/Kbdclass
Args: 00000000 00000004 000b0008 00000000

我们看到了 Irp 的当前 IO_STACK_LOCATION 变成了倒数第二个 IO_STACK_LOCATION ,里面是上一层驱动中设置的参数,告诉这一层驱动要干什么。本例中,本层驱动读写端口,操作硬件,完成了相应的功能。

IoCallDriver

首先我们看看对了解 IRP 最重要的一个函数,也是使用 IRP 进行驱动间通信所使用的函数,IoCallDriver。这个函数并不大,也不复杂,我们将详细的看看它的汇编代码。

NTSTATUS 
IoCallDriver(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
);
Irp 是发往设备对象的。参数 DeviceObject 指明设备目标设备对象。

nt!IoCallDriver:
80421417 8b542408 mov edx,[esp+0x8]
/* [esp+0x8] 为 IN OUT PIRP Irp */
8042141b 8b4c2404 mov ecx,[esp+0x4]
/* [esp+0x4] 为 IN PDEVICE_OBJECT DeviceObject */
8042141f e82ae1ffff call nt!IofCallDriver (8041f54e)
80421424 c20800 ret 0x8

nt!IofCallDriver:
8041f54e ff25d01a4780 jmp dword ptr [nt!pIofCallDriver (80471ad0)]

nt!IopfCallDriver:
8041f516 56 push esi
8041f517 8bf2 mov esi,edx
/* edx 为 IN OUT PIRP Irp */
8041f519 57 push edi
8041f51a 8bf9 mov edi,ecx
/* ecx 为 IN PDEVICE_OBJECT DeviceObject */
8041f51c fe4e23 dec byte ptr [esi+0x23]
/* esi 为 IN OUT PIRP Irp */
/* struct _IRP (sizeof=112) */
/* +23 char CurrentLocation */
8041f51f 8a4623 mov al,[esi+0x23]
8041f522 33c9 xor ecx,ecx
8041f524 3ac1 cmp al,cl
8041f526 7f0b jg nt!IopfCallDriver+0x1d (8041f533)
/* 如果 Irp->CurrentLocation 大于0,就跳 */
8041f528 51 push ecx
8041f529 51 push ecx
8041f52a 51 push ecx
8041f52b 56 push esi
8041f52c 6a35 push 0x35
8041f52e e8e1c90000 call nt!KeBugCheckEx (8042bf14)
8041f533 8b4660 mov eax,[esi+0x60]
/* esi 为 IN OUT PIRP Irp */
/* struct _IRP (sizeof=112) */
/* +60 struct _IO_STACK_LOCATION *CurrentStackLocation */
8041f536 56 push esi
/* esi 为 IN OUT PIRP Irp */
8041f537 83e824 sub eax,0x24
/* eax 为 Irp->CurrentStackLocation */
/* 0x24 为 sizeof(IO_STACK_LOCATION) */
8041f53a 57 push edi
/* edi 为 IN PDEVICE_OBJECT DeviceObject */
8041f53b 894660 mov [esi+0x60],eax
/* [esi+0x60] 为 Irp->CurrentStackLocation */
8041f53e 897814 mov [eax+0x14],edi
/* eax 为新的当前 IO_STACK_LOCATION */
/* struct _IO_STACK_LOCATION (sizeof=36) */
/* +14 struct _DEVICE_OBJECT *DeviceObject */
/* edi 为 IN PDEVICE_OBJECT DeviceObject */
8041f541 8b4f08 mov ecx,[edi+0x8]
/* edi 为 IN PDEVICE_OBJECT DeviceObject */
/* struct _DEVICE_OBJECT (sizeof=184) */
/* +08 struct _DRIVER_OBJECT *DriverObject */
8041f544 0fb600 movzx eax,byte ptr [eax]
/* eax 为新的当前 IO_STACK_LOCATION */
/* struct _IO_STACK_LOCATION (sizeof=36) */
/* +00 byte MajorFunction */
8041f547 ff548138 call dword ptr [ecx+eax*4+0x38]
/* ecx 为 DeviceObject->DriverObject */
/* struct _DRIVER_OBJECT (sizeof=168) */
/* +38 function *MajorFunction[28] */
/* eax 为 Irp->CurrentStackLocation->MajorFunction */
/* [ecx+eax*4+0x38] 为 */
/* DeviceObject->DriverObject.MajorFunction[Irp->CurrentStackLocation->MajorFunction] */
8041f54b 5f pop edi
8041f54c 5e pop esi
8041f54d c3 ret

IoCallDriver 将参数 Irp 的 +23 char CurrentLocation 减1,然后检查这时的 CurrentLocation 是否大于0。如果不大于0,将蓝屏。接着将参数 Irp 的 +60 struct _IO_STACK_LOCATION *CurrentStackLocation 减 0x24 。这里原来是一个 IO_STACK_LOCATION 的地址,0x24是一个 IO_STACK_LOCATION 结构的大小。这样减 0x24 就等于把 +60 struct _IO_STACK_LOCATION *CurrentStackLocation 指向了前一个 IO_STACK_LOCATION。然后将新的 CurrentStackLocation 的 +14 struct _DEVICE_OBJECT *DeviceObject 赋值为传入的参数 DeviceObject 。从传入的参数 DeviceObject 中获得该 DeviceObject 的 DriverObject,调用该 DriverObject 的新的 CurrentStackLocation 的 +00 byte MajorFunction 相应的 MajorFunction ,用传入的两个参数 IN PDEVICE_OBJECT DeviceObject,IN OUT PIRP Irp 做参数。

简单的说,把 Irp 的 CurrentLocation 减1,CurrentStackLocation 指向前一个(地址更小处) 的IO_STACK_LOCATION。然后根据现在的 CurrentStackLocation 所指的 IO_STACK_LOCATION 结构中 MajorFunction 的值,调用 DeviceObject 所属的驱动的 DriverObject 中 MajorFunction[28] 相应的函数。

DriverObject 中的 MajorFunction[28],是在 DriverEntry 中初始化的。

Irp 的产生

Irp 说到底是由 IoAllocateIrp 产生的。IoBuildDeviceIoControlRequest 这样产生 Irp 的函数,也是调用 
IoAllocateIrp 实现的。一个驱动可能需要另一层的驱动完成什么功能,而直接使用 IoBuildDeviceIoControlRequest 这样函数产生 Irp,并用这个 Irp 和其他驱动通信。更多的 Irp 是应用层,使用 ReadFile,WriteFile,DeviceIoControl 等这样的 api,来产生的。这些 api 最终也会调用 IoAllocateIrp 产生一个 Irp,然后初始化这个 Irp,发给驱动,驱动会根据 Irp ,完成相应的任务。

Irp 操作的一些函数

操作Irp的一些函数,很简单,只是一些宏。了解这些函数的具体的操作,有助于对 Irp 的了解。
下面的内容来自 ntddk.h

IoGetCurrentIrpStackLocation

#define IoGetCurrentIrpStackLocation( Irp ) ( (Irp)->Tail.Overlay.CurrentStackLocation )

IoSkipCurrentIrpStackLocation

#define IoSkipCurrentIrpStackLocation( Irp ) /
(Irp)->CurrentLocation++; /
(Irp)->Tail.Overlay.CurrentStackLocation++;

IoGetNextIrpStackLocation

#define IoGetNextIrpStackLocation( Irp ) (/
(Irp)->Tail.Overlay.CurrentStackLocation - 1 )

IoCopyCurrentIrpStackLocationToNext

#define IoCopyCurrentIrpStackLocationToNext( Irp ) { /
PIO_STACK_LOCATION irpSp; /
PIO_STACK_LOCATION nextIrpSp; /
irpSp = IoGetCurrentIrpStackLocation( (Irp) ); /
nextIrpSp = IoGetNextIrpStackLocation( (Irp) ); /
RtlCopyMemory( nextIrpSp, irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); /
nextIrpSp->Control = 0; }

IoMarkIrpPending

#define IoMarkIrpPending( Irp ) ( /
IoGetCurrentIrpStackLocation( (Irp) )->Control |= SL_PENDING_RETURNED )

Irp 的释放

使用 IoFreeIrp 释放一个 Irp

Irp 的结束

使用 IoCompleteRequest 结束一个 Irp,这个是了解如何使用 Irp 通信中,除了 IoCallDriver 外,第二重要的函数,这个函数又大又复杂,牵涉到其他的一些机制,对自己,还是下不了这个毒手,所以就不说了,用一用也就有个大概的概念。

 

IRP异步完成处理 // driver.c #include <ntddk.h> LIST_ENTRY ListHeader; // 派遣函数-常规 NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); // 派遣函数-读操作 NTSTATUS DispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); // 派遣函数-清理 NTSTATUS Dispat 阅读详情

相关推荐

手把手教你用X-AnyLabeling实现医学图像标注:从SAM模型选择到多边形优化全流程

本文详细介绍了如何使用X-AnyLabeling平台结合Med-SAM等专业模型实现医学图像的高效标注。从环境配置、模型部署到实战工作流,涵盖智能辅助标注、低对比度解决方案及团队协作方案,帮助医疗从业者大幅提升标注效率与质量。特别适合处理CT/MRI等医学影像的复杂标注需求。

weixin_29251131的博客 457

IRP 乱杂谈

IRP 乱杂谈 作者: JIURL                 主页: http://jiurl.yeah.net     IRP 是 I/O request packet 的缩写,即 I/O 请求包。驱动与驱动之间通过 IRP 进行通信。而使用驱动的应用层调用的 CreatFi

jiurl的专栏 4890

MTK pwm beeper配置记录

如果正常,系统启动后会在/dev/input下生成 一个event节点,使用方法参考标准linux使用。dts相关修改如下,全都在里面,注意一下位置。对应的配置文件中增加如下定义。

hclydao的专栏 728

IRP 处理流程

本文通过打开一个文件对象为例子,描述了一个带有两个I/O stack location的IRP的详细处理过程。当然一个IRP可以有多个I/O stack location,具体个数取决于将要处理该请求的驱动的层数。下图详细描述了驱动程序是如何通过使用I/O支持例程(IoXxx routines)来处理IRP以此来实现对相应文件的读写操作。 1:I/O管理器调用文件系统驱动

门没锁的专栏 7344

避坑指南:RK3576+Android14适配移远5G模组(RG200U)时,我遇到的3个‘巨坑’及解决方案

本文详细记录了RK3576芯片适配Android14系统与移远RG200U 5G模组过程中遇到的三大技术难题:RIL服务启动失败、HIDL兼容性矩阵配置错误及ttyUSB权限冲突。通过实战案例提供了动态库拷贝、HIDL版本链声明和SELinux策略调整等解决方案,帮助开发者高效完成5G模组集成,特别适用于基于kernel6.1的嵌入式开发场景。

weixin_30664051的博客 390

如何在 Ubuntu 24.04 或 22.04 上安装和使用 GDebi

本文介绍了在Ubuntu 24.04上安装和使用GDebi的方法。GDebi是DPKG的轻量级替代品,能够自动解析并安装.deb包的依赖项。文章详细说明了系统更新、安装GDebi的步骤,并演示了通过GUI和CLI两种方式安装.deb包的操作流程。此外,还提供了验证安装和卸载包的方法。GDebi虽然非必需,但为用户提供了处理依赖项的另一种选择,适合需要安装本地Debian包的用户。

山岚的运维笔记 1997

驱动开发程序的IRP处理过程

标准IRP的处理过程如同上图所示,整个处理由IO管理器创建的IRP开始。当然这个IO管理器在大多数情况下是系统,然而在驱动程序当中也可以创建IRP。下面四个函数可以用于创建IRP: IoBuildAsynchronousFsdRequest用于创建异步IRP,发送IRP的IO管理器不需要因为等待IRP的处理。 IoBuildSynchronousFsdRequest用于创建同步IRP,如果IR

dayenglish的专栏 1556

【人工智能时代】- 一键部署本地AI大模型

Ollama是一个开源项目,致力于简化大型语言模型(LLM)的本地部署过程。它允许用户在自己的硬件上运行和测试最新的语言模型,无需依赖云服务。Ollama提供了一个类似于OpenAI的API接口,使得开发者可以轻松地在自己的应用程序中集成大型语言模型。此外,Ollama WebUI为用户提供了一个友好的界面,用于管理和与模型进行交互。

xiaoli8748的专栏 2393

伪指令IRP和IRPC

伪指令IRP和IRPC伪指令IRP 伪指令IRP的作用是用每个参数创建一组语句,其重复次数由伪指令后面参数表中参数的个数来确定。其一般使用格式如下: IRP形式参数, <实参1, 实参2, ……, 实参n> 重复的语句组 ENDM 例9.11 把16位通用寄存器之值相加,并把结果存入寄存器AX。 解:由于16位通用寄存器名是一些不同的符号,不能用计数的方法来依次访问它们,所以,我们需要用

LiYuan199118的博客 1546

GameFramework框架详解之 Network网络框架

NetworkManager只负责维护NetworkChannel,实际的有种我们简历网络连接的也是NetworkChannel作为一个网路频道,这样管理起来比较方便,比如:一个游戏可能有多个网络连接,那么我们建立多个网络频道即可,或者当我们一个网络频道断线后,我们可以直接再新建一个,防止数据混乱。/// /// 网络管理器。/// /// 网络频道。/// 用户自定义网络错误事件。/// 检查是否存在网络频道。/// 获取网络频道。/// 获取所有网络频道。

TxNet 3367

需要Perfetto分析的anr

anr类的问题,经常会有系统cpu,内存负载不高,iowait也不高,主线程状正常的,这类问题通过常规的日志无法分析,需要抓取Perfetto来分析应用的情况。event log中也没有异常。

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 63

I/O Request Packet

第5章 I/O Request Packet 5.1 数据结构 在处理 I/O 请求上,有两个重要的数据结构:IRP(I/O request packet)和 IO_STACK_LOCATION 5.1.1 IRP 的结构 下面是在 windbg 里得到的 IRP 结构: nt!_IRP +0x000 Type : Int2B +0x002 Size

zhuo_zhibin的专栏 6094

《AgentScope-Java 深入浅出教程》第10章 RAG 知识检索

RAG(Retrieval-Augmented Generation,检索增强生成)是一种将信息检索与文本生成相结合的技术架构。它通过从外部知识库中检索相关文档,将检索到的信息作为上下文注入到语言模型的提示中,从而增强模型的回答质量。│ RAG 工作流程 ││ ││ 用户问题 向量检索 上下文增强 生成回答 ││ ▼ ││ │ A │ ││ ││ 1. 接收问题 ││ 2. 将问题转换为向量,在知识库中检索相似文档 ││ 3. 将检索到的文档作为上下文添加到提示词中 │。

步子哥的博客 462

IRP的同步完成与异步完成

应用程序中对设备进行同步、异步操作,都必须得到驱动程序的支持。所有对设备的操作都会转化为IRP请求,并传递到相应的派遣函数中。可以有两种方式处理IRP请求,第一种是在派遣函数中直接结束IRP请求,即同步处理。另一种方法是在派遣函数中不结束IRP请求,而是让派遣函数直接返回。IRP在以后的某个时候再进行处理。

ingor的专栏 2701

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

IRP完成函数:IofCompleteRequest

IRP完成函数:IofCompleteRequest 一.IRP的完成 1.IRP完成后,做些什么(简单描述)? A.执行完成Routine> B.释放(有可能要复制还给“用户层”,见下面详细)缓冲区 C.删除IRP   2.怎么完成一个IRP? A.调用IoCompleteRequest,而IoCompleteRequest是一个“宏”== IofCompleteReques

qq350625238的专栏 2668

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

内核开发irp详解运行机制详解[学习]

I/O Request Packet (IRP),设备栈,IO_STACK_LOCATION,IoCompletion函数

SunShine的专栏 3322

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

完成I/O请求

完成I/O请求每个IRP都渴望被完成。在标准模型中,你至少有两种完成IRP的环境。DpcForIsr通常用于完成导致最近中断的IRP。派遣函数也可以在下面这两种情况下完成IRP: 如果请求是错误的(可以以容易的检测方式查明,例如要求打印机倒纸请求或卸载键盘请求),则派遣例程应以失败方式完成该请求并返回适当的出错代码。    如果请求要求得到的仅是派遣函数可以容易确定的信息(例如

Iqian1314的专栏 3527

IRP(I/O Request Package)详解

篇一: 简介: IRP(I/O Request Package)在windows内核中,有一种系统组件——IRP,即输入输出请求包。 当上层应用程序需要访问底层输入输出设备时,发出I/O请求,系统会把这些请求转化为IRP数 据,不同的IRP会启动I/O设备驱动中对应的派遣函数。 IRP类型 由于IRP是响应上层应用程序的。可想而知,IRP类型是与上层对底层设备的访问类型相对应。 文件...

极客神殿 8815

IRP请求的完成与返回

 IRP请求的完成与返回    每当完成了一个以IRP为代表的I/O操作请求的时候,就要执行IRP的善后操作IoCompleteRequest(),这是个宏操作,定义为函数IofCompleteRequest()。#define  IoCompleteRequest  IofCompleteRequest    如前所述,以IRP为代表的I/O操作请求的执行可以是同步的,也可以是异步的,所以一

博文视点(北京)官方博客 1940

派遣函数 - IRP与派遣函数

派遣函数 - IRP与派遣函数

wendyWJGU的博客 660

IRP说明

<br />近来学习Windows内核方面的东西,觉得对I/O处理过程没有一个总体的概念。于是,就花了一点时间搜集了很多这方面的资料并自己总结了一下。下面说说我自己的理解。<br /> 在Windows内核中的I/O请求基本上是通过 I/O Request Packet (IRP)完成的。下面,我就来详细地说下IRP请求是怎么样一步一步完成的。<br /> 首先,我们就需要知道IRP是怎么产生。IRP是由I/O管理器发出的,I/O管理器是用户态与内核态之间的桥梁,当用户态进程发出I/O请求时,I/O管理器就

Augusdi的专栏 2901

驱动开发(13)IRP 的异步完成和 CancelRoutine

https://blog.youkuaiyun.com/zuishikonghuan/article/details/51301922 在之前的博文中,我们对于IRP,都是同步完成的,但是 Windows 对异步操作很友好,我们来看看如何异步完成 IRP 。 在应用程序中异步访问设备 在开始之前,我认为有必要提一句异步访问设备。在之前的博文中,与驱动通信的代码都是采用同步访问设备的,其实所谓同步访问,是...

子曰小玖的博客 899
上一篇: 中国经济回暖与华尔街的阴谋
下一篇: VC编程中常使用的子函数
jiangdf
博客等级 码龄19年 86粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值