Cuzっ 发表于 2015-2-10 00:16:07

对IRP的一些理解

假设某过滤驱动的分层结构如下:

1 FIDO<-- 此时你在这里调用IoAllocateIrp()创建一个先的IRP往下层驱动FDO传送
2 FDO
3 PDO

这时创建出来的新IRP[假设用new_IRP来表示],那么new_IRP的StackCount只能是 >= 2,因为FIDO的下层驱动有2个分别是FDO和PDO。
此时new_IRP的IO堆栈单元为StackCount+1。 假设IO堆栈单元有3个,第3个IO堆栈单元表示new_IRP当前IO堆栈单元 第2个表示FDO的IO堆栈单元 第1个表示PDO的堆栈单元

那么如果要发送此IRP到下层驱动FDO,则预先初始化new_IRP的FDO的IO堆栈单元,也就是第二个IO堆栈单元。

此时new_IRP 的 StackCount = 2, CurrentLocation = 3 这里的3表示new_IRP的当前IO堆栈单元 如果你需要把此IRP完FDO驱动传递,那么不用初始化这个IO堆栈单元,为什么呢?

因为IoCallDriver() 内部会调用 类似 IoSetNextIrpStackLocation() 的操作来调整 CurrentLocation的索引。也就是 索引-1 ;

所以要调用IoCallDriver() 把new_IRP传递到FDO这个下层驱动,需要先调用IoGetNextIrpStackLocation() 获取FDO对应的IO堆栈单元,并进行初始化。
初始化完成后才能调用IoCallDriver()
此时new_IRP的 CurrentLocation = 2 这个索引才是指向FDO的IO堆栈单元

下面是来自WMD一书的例子:我上面的话就是为了理解下面的代码片断

发往派遣例程
创建完IRP后,你可以调用IoGetNextIrpStackLocation函数获得该IRP第一个堆栈单元的指针。然后初始化这个堆栈单元。在初始化过程的最后,你需要填充MajorFunction代码。堆栈单元初始化完成后,就可以调用IoCallDriver函数把IRP发送到设备驱动程序:

PDEVICE_OBJECT DeviceObject;   //something gives you this
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
stack->MajorFunction = IRP_MJ_Xxx;
<other initialization of "stack">
NTSTATUS status = IoCallDriver(DeviceObject, Irp);


IoCallDriver函数的第一个参数是你在某处获得的设备对象的地址。我将在本章的结尾处描述获得设备对象指针的两个常用方法。在这里,我们先假设你已经有了这个指针。

IRP中的第一个堆栈单元指针被初始化成指向该堆栈单元之前的堆栈单元,因为I/O堆栈实际上是IO_STACK_LOCATION结构数组,你可以认为这个指针被初始化为指向一个不存在的“-1”元素,因此当我们要初始化第一个堆栈单元时我们实际需要的是“下一个”堆栈单元。IoCallDriver将沿着这个堆栈指针找到第0个表项,并提取我们放在那里的主功能代码,在上例中为IRP_MJ_Xxx。然后IoCallDriver函数将利用DriverObject指针找到设备对象中的MajorFunction表。IoCallDriver将使用主功能代码索引这个表,最后调用找到的地址(派遣函数)。

你可以把IoCallDriver函数想象为下面代码:

NTSTATUS IoCallDriver(PDEVICE_OBJECT device, PIRP Irp)
{
IoSetNextIrpStackLocation(Irp);
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
stack->DeviceObject = device;
ULONG fcn = stack->MajorFunction;
PDRIVER_OBJECT driver = device->DriverObject;
return (*driver->MajorFunction)(device, Irp);
}

另:

1) Irp = IoAllocateIrp( IN CCHAR StackSize, IN BOOLEAN ChargeQuota);

分配一个IRP,看看ms是怎么做的吧:

a. ) packetSize = IoSizeOfIrp(StackSize);



#define IoSizeOfIrp( StackSize ) \
    ((USHORT) (sizeof( IRP ) + ((StackSize) * (sizeof( IO_STACK_LOCATION )))))
计算 IRP的大小,看来一个IRP包括IRP头本身和stack location × StackSize,

b.) irp = ExAllocatePoolWithTag(NonPagedPool, allocateSize, ' prI');

分别空间,并且是分配在非分页内存池中。

c) IopInitializeIrp(irp, allocateSize, StackSize);

初始化一个IRP。

#define IopInitializeIrp( Irp, PacketSize, StackSize ) {          \
    RtlZeroMemory( (Irp), (PacketSize) );                         \
    (Irp)->Type = (CSHORT) IO_TYPE_IRP;                           \
    (Irp)->Size = (USHORT) ((PacketSize));                        \
    (Irp)->StackCount = (CCHAR) ((StackSize));                  \
    (Irp)->CurrentLocation = (CCHAR) ((StackSize) + 1);         \
    (Irp)->ApcEnvironment = KeGetCurrentApcEnvironment();         \
    InitializeListHead (&(Irp)->ThreadListEntry);               \
    (Irp)->Tail.Overlay.CurrentStackLocation =                  \
      ((PIO_STACK_LOCATION) ((UCHAR *) (Irp) +                  \
            sizeof( IRP ) +                                       \
            ( (StackSize) * sizeof( IO_STACK_LOCATION )))); }

一个IRP初始化是 当前栈要+1,然后指到分配空间的最后,一个IRP初始化之后,在内存中应该是这个样子:

----------------

|            |

|IRP头

|

|

|Tail.Overlay.CurrentStackLocation

|                                                   |

|

|------------

|

|               

|

|                                                |

|

|

|

|                                                   |

|

|

----------------<----------------------|

2)PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(Irp);

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

由于刚开始CurrentStackLocation指向处于分配范围之外的地址,所以此处 -1 得到真正的第一个STACK_LOCATION

3) IoCallDriver(DeviceObject, Irp);

将IRP发送到目的驱动

这是IoCallDriver的部分实现

view plaincopy to clipboardprint?
Irp->CurrentLocation--;   

   if (Irp->CurrentLocation <= 0) {   
       KiBugCheck3( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0 );   
   }   

   irpSp = IoGetNextIrpStackLocation( Irp );   
   Irp->Tail.Overlay.CurrentStackLocation = irpSp;   

   //   
   // Save a pointer to the device object for this request so that it can   
   // be used later in completion.   
   //   

   irpSp->DeviceObject = DeviceObject;   


   //   
   // Invoke the driver at its dispatch routine entry point.   
   //   

   driverObject = DeviceObject->DriverObject;   

   //   
   // Prevent the driver from unloading.   
   //   


   status = driverObject->MajorFunction( DeviceObject,   
                                                             Irp );   
Irp->CurrentLocation--;

    if (Irp->CurrentLocation <= 0) {
      KiBugCheck3( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0 );
    }

    irpSp = IoGetNextIrpStackLocation( Irp );
    Irp->Tail.Overlay.CurrentStackLocation = irpSp;

    //
    // Save a pointer to the device object for this request so that it can
    // be used later in completion.
    //

    irpSp->DeviceObject = DeviceObject;


    //
    // Invoke the driver at its dispatch routine entry point.
    //

    driverObject = DeviceObject->DriverObject;

    //
    // Prevent the driver from unloading.
    //


    status = driverObject->MajorFunction( DeviceObject,
                                                            Irp );


可以看到当calldriver时候,IoCallDriver 会将当前栈-1 ,同时将下一个STACK_LOCATION最为当前栈单元,通过设备对象找到其关联的驱动对象,调用MJ函数,IoCallDriver 返回。

眼角存留的泪 发表于 2015-4-22 04:07:01

看帖回帖,楼主辛苦了!       

古凝明 发表于 2015-5-6 14:00:54

硬实厉害啊
页: [1]
查看完整版本: 对IRP的一些理解