zoukankan      html  css  js  c++  java
  • IRP小结 0x01 IRP & IO_STACK_LOCATION(结合WRK理解)

      写博客整理记录一下IRP相关的知识点,加深一下印象。

      所有的I/O请求都是以IRP的形式提交的。当I/O管理器为了响应某个线程调用的的I/O API的时候,就会构造一个IRP,用于在I/O系统处理这个请求的过程中代表该请求

    0x01  IRP与IO_STACK_LOCATION的结构体概览

       IRP由两部分组成,一个固定大小的头(即IRP结构体的大小)以及一个或多个I/O栈单元(即IO_STACK_LOCATION数组)

               先看头部的IRP结构体,主要信息有:请求的类型和大小,请求是同步还是异步,缓冲区指针,可被改变的状态值等等。

     1 typedef struct _IRP {
     2   CSHORT Type;//结构类型
     3   USHORT Size;//irp的实际分配长度(包含后面的栈空间数组)
     4   struct _MDL *MdlAddress;//关联的MDL链表
     5   ULONG Flags;//irp标志
     6   union {
     7     //(一个irp可以分成n个子irp发给下层的驱动)
     8     struct _IRP *MasterIrp;//当该irp是子irp时表示所属的主irp
     9     volatile LONG IrpCount;//当该irp是主irp时,表示子irp个数
    10     PVOID SystemBuffer;//关联的系统缓冲地址
    11   } AssociatedIrp;
    12   LIST_ENTRY ThreadListEntry;//用来挂入线程的未决(指Pending)irp链表
    13   IO_STATUS_BLOCK IoStatus;//该irp的完成结果(内置的)
    14   KPROCESSOR_MODE RequestorMode;//表示是来自用户模式还是内核模式的irp请求
    15   BOOLEAN PendingReturned;//表示下层设备当初处理该irp时是否是Pending异步方式处理的
    16   CHAR StackCount;//本irp的栈层数,也即本结构体后面的数组元素个数
    17   CHAR CurrentLocation;//从上往下数,当前的栈空间位置序号(每个栈层就是一个栈空间)
    18   BOOLEAN Cancel;//表示用户是否发出了取消该irp的命令
    19   KIRQL CancelIrql;//取消时的irql
    20   CCHAR ApcEnvironment;//该irp当初分配时,线程的apc状态(挂靠态/常态)
    21   UCHAR AllocationFlags;//用于保存当初分配该irp结构时的分配标志
    22   PIO_STATUS_BLOCK UserIosb;//可为空。表示用户自己提供的一个结构,用来将完成结果返回给用户
    23   PKEVENT UserEvent;//可为空。表示用户自己提供的irp完成事件
    24   union {
    25     struct {
    26       _ANONYMOUS_UNION union {
    27         PIO_APC_ROUTINE UserApcRoutine;//用户提供的APC例程
    28         PVOID IssuingProcess;
    29       } DUMMYUNIONNAME;
    30       PVOID UserApcContext;//APC例程的参数
    31     } AsynchronousParameters;
    32     LARGE_INTEGER AllocationSize;//本结构当初分配时总的分配长度(包含后面的数组)
    33   } Overlay;
    34   volatile PDRIVER_CANCEL CancelRoutine;//本irp关联的取消例程(取消时将执行这个函数)
    35   PVOID UserBuffer;//关联的用户空间缓冲区地址(直接使用可能不安全)
    36   union {
    37     struct {
    38       _ANONYMOUS_UNION union {
    39         KDEVICE_QUEUE_ENTRY DeviceQueueEntry;//用来挂入设备对象内置的irp队列
    40         _ANONYMOUS_STRUCT struct {
    41           PVOID DriverContext[4];
    42         } DUMMYSTRUCTNAME;
    43       } DUMMYUNIONNAME;
    44       PETHREAD Thread;//该irp的发起者线程
    45       PCHAR AuxiliaryBuffer;//关联的辅助缓冲
    46       _ANONYMOUS_STRUCT struct {
    47         LIST_ENTRY ListEntry;
    48         _ANONYMOUS_UNION union {
    49           //这个字段与CurrentLocation的作用一样,只是一个表示指针,一个表示序号
    50           struct _IO_STACK_LOCATION *CurrentStackLocation;//当前的栈空间位置
    51           ULONG PacketType;
    52         } DUMMYUNIONNAME;
    53       } DUMMYSTRUCTNAME;
    54      //irp本来是发给设备的,但是我们也可以看做是发给文件对象(各栈层可能有变动)
    55       struct _FILE_OBJECT *OriginalFileObject;//本irp最初发往的文件对象
    56     } Overlay;
    57     KAPC Apc;//与本次irp相关的APC例程
    58     PVOID CompletionKey;
    59   } Tail;
    60 } IRP, *PIRP;

      每个IRP结构体后面紧跟一个数组,那就是IRP的I/O设备栈,数组中每个元素的类型为IO_SATCK_LOCATION(只列出部分字段),它包含的关键字段有主功能码次功能码,以及不同请求对应的功能参数等:

     1 typedef struct _IO_STACK_LOCATION {
     2     UCHAR MajorFunction;//主功能码
     3     UCHAR MinorFunction;//次功能码
     4     UCHAR Flags;
     5     UCHAR Control;//DeviceControl的控制码
     6  
     7     union {
     8  
     9         struct {
    10             ULONG Length;//读请求的长度
    11             ULONG POINTER_ALIGNMENT Key;
    12             LARGE_INTEGER ByteOffset;//读请求的文件偏移位置
    13         } Read;
    14 
    15         struct {
    16             ULONG Length; //写请求的长度
    17             ULONG POINTER_ALIGNMENT Key;
    18             LARGE_INTEGER ByteOffset; //写请求的文件偏移位置
    19         } Write;
    20  
    21  
    22         struct {
    23             ULONG OutputBufferLength;
    24             ULONG POINTER_ALIGNMENT InputBufferLength;
    25             ULONG POINTER_ALIGNMENT IoControlCode;
    26             PVOID Type3InputBuffer;
    27         } DeviceIoControl;//NtDeviceIoControlFile
    28     
    29         struct {
    30             PVOID Argument1;
    31             PVOID Argument2;
    32             PVOID Argument3;
    33             PVOID Argument4;
    34         } Others;//没有列举的结构可以用这几个字段
    35         
    36          ...
    37     } Parameters;//一个复杂的联合体,对应各种irp的参数
    38  
    39     PDEVICE_OBJECT DeviceObject;//本栈层的设备对象
    40     PFILE_OBJECT FileObject;//关联的文件对象
    41     PIO_COMPLETION_ROUTINE CompletionRoutine;//记录着上层设置的完成例程
    42     PVOID Context;//完成例程的参数
    43  
    44 } IO_STACK_LOCATION, *PIO_STACK_LOCATION;

    0x02  从WRK源码看IRP的构建与下发

        1.首先看IRP的构建IoAllocateIrp

      1 PIRP
      2 IopAllocateIrpPrivate(
      3     IN CCHAR StackSize,
      4     IN BOOLEAN ChargeQuota
      5     )
      6 
      7 /*++
      8 
      9 Routine Description:
     10 
     11     This routine allocates an I/O Request Packet from the system nonpaged pool.
     12     The packet will be allocated to contain StackSize stack locations.  The IRP
     13     will also be initialized.
     14 
     15 Arguments:
     16 
     17     StackSize - Specifies the maximum number of stack locations required.
     18 
     19     ChargeQuota - Specifies whether quota should be charged against thread.
     20 
     21 Return Value:
     22 
     23     The function value is the address of the allocated/initialized IRP,
     24     or NULL if one could not be allocated.
     25 
     26 --*/
     27 
     28 {
     29     USHORT allocateSize;
     30     UCHAR fixedSize;
     31     PIRP irp;
     32     UCHAR lookasideAllocation;
     33     PNPAGED_LOOKASIDE_LIST lookasideList;
     34     UCHAR mustSucceed;
     35     PP_NPAGED_LOOKASIDE_NUMBER number;
     36     USHORT packetSize;
     37     PKPRCB prcb;
     38 
     39     //
     40     // If the size of the packet required is less than or equal to those on
     41     // the lookaside lists, then attempt to allocate the packet from the
     42     // lookaside lists.
     43     //IopLargeIrpStackLocations的值是8
     44 
     45     irp = NULL;
     46     fixedSize = 0;
     47     mustSucceed = 0;
     48     packetSize = IoSizeOfIrp(StackSize);//((USHORT) (sizeof( IRP ) + ((StackSize) * (sizeof( IO_STACK_LOCATION )))))注意这里是Irp大小+设备栈大小的总大小
     49     allocateSize = packetSize;
     50     //如果栈层数小于等于8又不计较配额浪费,那么就从预置的irp容器分配
     51     if ((StackSize <= (CCHAR)IopLargeIrpStackLocations) &&
     52         ((ChargeQuota == FALSE) || (IopLookasideIrpFloat < IopLookasideIrpLimit))) {
     53         fixedSize = IRP_ALLOCATED_FIXED_SIZE;
     54         number = LookasideSmallIrpList;
     55         if (StackSize != 1) {
     56             allocateSize = IoSizeOfIrp((CCHAR)IopLargeIrpStackLocations);//对齐8个栈层大小
     57             number = LookasideLargeIrpList;
     58         }
     59 
     60         prcb = KeGetCurrentPrcb();
     61         lookasideList = prcb->PPLookasideList[number].P;//尝试从该容器的P链表中分配出一个irp
     62         lookasideList->L.TotalAllocates += 1;//该链表总的分配请求计数++
     63         irp = (PIRP)ExInterlockedPopEntrySList(&lookasideList->L.ListHead,
     64                                                &lookasideList->Lock);//分配内存
     65         if (irp == NULL) {//如果分配失败
     66             lookasideList->L.AllocateMisses += 1;//该链表的分配失败计数++
     67             //再尝试从该容器的L链表中分配出一个irp
     68             lookasideList = prcb->PPLookasideList[number].L;
     69             lookasideList->L.TotalAllocates += 1;
     70             irp = (PIRP)ExInterlockedPopEntrySList(&lookasideList->L.ListHead,
     71                                                    &lookasideList->Lock);
     72         }
     73     }
     74 
     75     //
     76     // If an IRP was not allocated from the lookaside list, then allocate
     77     // the packet from nonpaged pool and charge quota if requested.
     78     //
     79 
     80     lookasideAllocation = 0;
     81     if (!irp) {//如果仍然分配失败或者尚未分配
     82         if (fixedSize != 0) {
     83             lookasideList->L.AllocateMisses += 1;
     84         }
     85 
     86         //
     87         // There are no free packets on the lookaside list, or the packet is
     88         // too large to be allocated from one of the lists, so it must be
     89         // allocated from nonpaged pool. If quota is to be charged, charge it
     90         // against the current process. Otherwise, allocate the pool normally.
     91         //
     92 
     93         if (ChargeQuota) {
     94             try {
     95                 irp = ExAllocatePoolWithQuotaTag(NonPagedPool, allocateSize,' prI');//直接从非分页池中分配
     96 
     97             } except(EXCEPTION_EXECUTE_HANDLER) {
     98                 NOTHING;
     99             }
    100 
    101         } else {
    102 
    103             //
    104             // Attempt to allocate the pool from non-paged pool.  If this
    105             // fails, and the caller's previous mode was kernel then allocate
    106             // the pool as must succeed.
    107             //
    108 
    109             irp = ExAllocatePoolWithTag(NonPagedPool, allocateSize, ' prI');
    110             if (!irp) {
    111                 mustSucceed = IRP_ALLOCATED_MUST_SUCCEED;
    112                 if (KeGetPreviousMode() == KernelMode ) {
    113                     irp = ExAllocatePoolWithTag(NonPagedPoolMustSucceed,
    114                                                 allocateSize,
    115                                                 ' prI');
    116                 }
    117             }
    118         }
    119 
    120         if (!irp) {
    121             return NULL;
    122         }
    123 
    124     } else {
    125         if (ChargeQuota != FALSE) {
    126             lookasideAllocation = IRP_LOOKASIDE_ALLOCATION;
    127             InterlockedIncrement( &IopLookasideIrpFloat );
    128         }
    129         ChargeQuota = FALSE;
    130     }
    131 
    132     //
    133     // Initialize the packet.
    134     //
    135     //分配完irp后,做一些基本字段的初始化
    136     IopInitializeIrp(irp, packetSize, StackSize);
    137     irp->AllocationFlags = (fixedSize | lookasideAllocation | mustSucceed);
    138     if (ChargeQuota) {
    139         irp->AllocationFlags |= IRP_QUOTA_CHARGED;
    140     }
    141 
    142     return irp;
    143 }
    144 
    145 #define IoSizeOfIrp(_StackSize)  sizeof(IRP) + _StackSize * sizeof(IO_STACK_LOCATION)

        提炼一下IopAllocateIrpPrivate函数中的关键信息:

                  1.IopAllocateIrpPrivate通过StackSize即I/O设备栈的层数来分配内存的大小,即一次性分配的大小为:IRP结构体大小+IO_STACK_LOCATION大小*设备栈的层数

        2.由于IRP的频繁分配,所以irp栈层数小于等于8时,按8对齐(类似于内存对齐),直接从容器中分配irp整个结构(包括设备栈)。

     1 #define IopInitializeIrp( Irp, 
     2                                 PacketSize, ,//实际分配的大小
     3                                 StackSize ) {//栈层数          
     4     RtlZeroMemory( (Irp), (PacketSize) );                         
     5     (Irp)->Type = (CSHORT) IO_TYPE_IRP;                           
     6     (Irp)->Size = (USHORT) ((PacketSize));                        
     7     (Irp)->StackCount = (CCHAR) ((StackSize));                    
     8     (Irp)->CurrentLocation = (CCHAR) ((StackSize) + 1); //注意这里:初始栈空间位置在栈顶的上面          
     9     (Irp)->ApcEnvironment = KeGetCurrentApcEnvironment();         
    10     InitializeListHead (&(Irp)->ThreadListEntry);                 
    11     (Irp)->Tail.Overlay.CurrentStackLocation =                    
    12         ((PIO_STACK_LOCATION) ((UCHAR *) (Irp) +                  
    13             sizeof( IRP ) +                                       
    14             ( (StackSize) * sizeof( IO_STACK_LOCATION )))); }                    

        这里字段的初始化需要注意的是:CurrentLocation (记录的是一个序号,表示当前所在的设备栈,最顶层的设备栈,即最先接收到此IRP的设备对象对应的设备栈,所属的CurrentLocation 的值应当是整个设备栈中最大的,这一点涉及到之后IRP的传递问题)的初始值是((StackSize) + 1),即为栈层数+1;

        CurrentLocation字段记录了该irp在各层驱动的处理进度。该数组中,第一个元素表示设备栈的栈底,最后一个元素表示栈顶。每当将irp转发到下层设备时,irp头部中的CurrentLocation字段递减,而不是递增;

        而CurrentStackLocation 指针,指向的则是设备栈的最顶层(高地址),即第一个接收到此IRP的设备对象对应的那层设备栈。

        

        2.再看IRP的下发IoCalllDriver

    NTSTATUS
    FASTCALL
    IopfCallDriver(
        IN PDEVICE_OBJECT DeviceObject,
        IN OUT PIRP Irp
        )
    
    /*++
    
    Routine Description:
    
        This routine is invoked to pass an I/O Request Packet (IRP) to another
        driver at its dispatch routine.
    
    Arguments:
    
        DeviceObject - Pointer to device object to which the IRP should be passed.
    
        Irp - Pointer to IRP for request.
    
    Return Value:
    
        Return status from driver's dispatch routine.
    
    --*/
    
    {
        PIO_STACK_LOCATION irpSp;
        PDRIVER_OBJECT driverObject;
        NTSTATUS status;
    
        //
        // Ensure that this is really an I/O Request Packet.
        //
    
        ASSERT( Irp->Type == IO_TYPE_IRP );
    
        //
        // Update the IRP stack to point to the next location.
        //
        Irp->CurrentLocation--;//序号--,代表当前栈层位置向下滑动,指向下层栈空间
    
        if (Irp->CurrentLocation <= 0) {
            KeBugCheckEx( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0, 0 );
        }
    
        irpSp = IoGetNextIrpStackLocation( Irp );//指针下移,代表当前栈层位置向下滑动,指向下层栈空间(注意这里CurrentLocation代表的是序号,CurrentStackLocation代表的是指针)
        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;
    
        PERFINFO_DRIVER_MAJORFUNCTION_CALL(Irp, irpSp, driverObject);
    
        status = driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject,
                                                                  Irp );//调用下层驱动对应irp的派遣函数
    
        PERFINFO_DRIVER_MAJORFUNCTION_RETURN(Irp, irpSp, driverObject);
    
        return status;
    }
    
    
    #define IoGetNextIrpStackLocation( Irp ) (
        (Irp)->Tail.Overlay.CurrentStackLocation - 1 )

        

        可以看到,上层驱动在调用这个函数,将irp发到下层设备时,会自动在内部将当前栈空间位置向下滑动一个位置,指向下层的栈空间(递减IRP的CurrentLocation,并获得下一层的IO_STACK_LOCATION,设置到IRP的CurrentStackLocation指针中)。

    PS:

     1 ddk提供了一个宏,用来移动irp的栈空间位置
     2 
     3 #define IoSetNextIrpStackLocation(Irp) 
     4 
     5 { 
     6 
     7    Irp->CurrentLocation--;    //序号向下滑动一项
     8 
     9    Irp->Tail.Overlay.CurrentStackLocation--;     //数组元素指针也向下滑动一项
    10 
    11 }
    12 
    13 下面的宏实际上获取的就是当前栈空间的位置
    14 
    15 #define IoGetCurrentIrpStackLocation(irp)  irp->Tail.Overlay.CurrentStackLocation
    16 
    17 下面的宏实际上获取的就是下层栈空间的位置
    18 
    19 #define IoGetNextIrpStackLocation(irp)  irp->Tail.Overlay.CurrentStackLocation – 1
  • 相关阅读:
    flutter 屏幕宽高 状态栏高度
    flutter 图片圆角
    flutter ListView嵌套高度问题
    Dubbo原码解析(version:2.5.3)
    ms
    InnoDB锁问题 & DB事务隔离级别
    Spring父容器与子容器
    Spring bean 的加载过程和生命周期
    logback
    Disconf (version : 2.6.21)
  • 原文地址:https://www.cnblogs.com/lsh123/p/10414117.html
Copyright © 2011-2022 走看看