IRP结构中的IRP!StackCount--IRP!CurrentLocation--IRP!CurrentStackLocation三个字段关系错综,仅以此文已做备忘。
//IRP结构后面接一个IO_STACK_LOCATION数组
typedef struct _IRP {
CSHORT Type;
USHORT Size;
struct _MDL *MdlAddress;
ULONG Flags;
union {
struct _IRP *MasterIrp;
volatile LONG IrpCount;
PVOID SystemBuffer;
} AssociatedIrp;
LIST_ENTRY ThreadListEntry;
//返回值
IO_STATUS_BLOCK IoStatus;
KPROCESSOR_MODE RequestorMode;
BOOLEAN PendingReturned;
//整个设备栈中从顶层到底层的栈深度(IO_STACK_LOCATION数组的长度)
CHAR StackCount;
//当前设备栈深度,从1开始计数
/*
另外,IofCallDriver函数中一段代码:
Irp->CurrentLocation--;
if (Irp->CurrentLocation <= 0)
{
KeBugCheckEx(NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR)Irp, 0, 0, 0);
}
在调用下一层设备之前,先对Irp->CurrentLocation减一,如果Irp->CurrentLocation==0就bugcheck了,
从这里可以确定,CurrentLocation是从1开始计数的
CurrentLocation就如同日常口语中第一层(对应数组元素0)第二层(数组元素1)的概念
*/
CHAR CurrentLocation;
BOOLEAN Cancel;
KIRQL CancelIrql;
CCHAR ApcEnvironment;
UCHAR AllocationFlags;
PIO_STATUS_BLOCK UserIosb;
//同步对象
PKEVENT UserEvent;
union {
struct {
PIO_APC_ROUTINE UserApcRoutine;
PVOID UserApcContext;
} AsynchronousParameters;
LARGE_INTEGER AllocationSize;
} Overlay;
volatile PDRIVER_CANCEL CancelRoutine;
PVOID UserBuffer;
union {
struct {
_ANONYMOUS_UNION union {
KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
_ANONYMOUS_STRUCT struct {
PVOID DriverContext[4];
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
//发出IRP的线程
PETHREAD Thread;
PCHAR AuxiliaryBuffer;
_ANONYMOUS_STRUCT struct {
LIST_ENTRY ListEntry;
_ANONYMOUS_UNION union {
//当前使用的堆栈数组元素指针
/*
StackCount/CurrentLocation/CurrentStackLocation三者的关系还是得从IoSizeOfIrp和IoInitializeIrp确立
*/
struct _IO_STACK_LOCATION *CurrentStackLocation;
ULONG PacketType;
} DUMMYUNIONNAME;
} DUMMYSTRUCTNAME;
//设备对象所关联的文件对象
struct _FILE_OBJECT *OriginalFileObject;
} Overlay;
//整个IRP异步返回时用到的APC对象
KAPC Apc;
PVOID CompletionKey;
} Tail;
} IRP;
如同注释中说,
StackCount/CurrentLocation/CurrentStackLocation三者的关系还是得从IoSizeOfIrp和IoInitializeIrp确立
接下来看下IoSizeOfIrp和IoInitializeIrp对IRP中这些字段的操作
VOID
NTAPI
IoInitializeIrp(IN PIRP Irp,
IN USHORT PacketSize,
IN CCHAR StackSize)
{
/* Clear it */
IOTRACE(IO_IRP_DEBUG,
"%s - Initializing IRP %p\n",
__FUNCTION__,
Irp);
RtlZeroMemory(Irp, PacketSize);
/* Set the Header and other data */
Irp->Type = IO_TYPE_IRP;
Irp->Size = PacketSize;
Irp->StackCount = StackSize;
//初始状态时当前堆栈深度为实际深度(StackSize)+1
//因为此时的IRP还没有进入设备对象堆栈
Irp->CurrentLocation = StackSize + 1;
Irp->ApcEnvironment = KeGetCurrentThread()->ApcStateIndex;
//IO_STACK_LOCATION数组紧贴IRP后面,数组中一共只有StackSize个元素,
//下标从0到StackSize-1,所以指针已经越界一个元素。
//最重要的,调用IoCallDriver时会调用一次IoGetNextIrpStackLocation,把数组
//指针拨动到正确的位置,然后再把irp传递下去
/*
还是以老式设备StackSize=1为例:
Irp->CurrentLocation=2,设备栈中第二个设备(很可惜堆栈上一共只有1个设备),
Irp->Tail.Overlay.CurrentStackLocation=&IO_STACK_LOCATION[1].数组中只有元素0,因此还是越界
状态.
调用IofCallDriver时,IoSetNextIrpStackLocation(Irp),因此Irp->CurrentLocation=1,意为取堆栈中
第一个元素,Irp->Tail.Overlay.CurrentStackLocation--堆栈指针指向&IO_STACK_LOCATION[0],取
堆栈元素0
*/
Irp->Tail.Overlay.CurrentStackLocation = (PIO_STACK_LOCATION)(Irp + 1) + StackSize;
/* Initialize the Thread List */
InitializeListHead(&Irp->ThreadListEntry);
}
/*
StackSize是设备栈中从顶层到底层设备数量,以老式设备为例StackSize=1.
因此这个宏将在池中获得sizeof(IRP)和只有一个IO_STACK_LOCATION元素的数组,
下标为0
*/
#define IoSizeOfIrp(_StackSize) \
((USHORT) (sizeof(IRP) + ((_StackSize) * (sizeof(IO_STACK_LOCATION)))))