近来学习 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 管理器建立的。
图 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 所示:
图 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 外,第二重要的函数,这个函数又大又复杂,牵涉到其他的一些机制,对自己,还是下不了这个毒手,所以就不说了,用一用也就有个大概的概念。