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)))))