IRP小結 0x01 IRP & IO_STACK_LOCATION(結合WRK理解)

  寫博客整理記錄一下IRP相關的知識點,加深一下印象。數組

  全部的I/O請求都是以IRP的形式提交的。當I/O管理器爲了響應某個線程調用的的I/O API的時候,就會構造一個IRP,用於在I/O系統處理這個請求的過程當中表明該請求安全

 

0x01  IRP與IO_STACK_LOCATION的結構體概覽less

   IRP由兩部分組成,一個固定大小的頭(即IRP結構體的大小)以及一個或多個I/O棧單元(即IO_STACK_LOCATION數組)異步

           先看頭部的IRP結構體,主要信息有:請求的類型和大小,請求是同步仍是異步,緩衝區指針,可被改變的狀態值等等。ide

 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的構建與下發ui

    1.首先看IRP的構建IoAllocateIrpthis

  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函數中的關鍵信息:spa

              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
相關文章
相關標籤/搜索