IRP(I/O Request Package)詳解

風靈使發表於2018-09-04

篇一:

簡介:

IRP(I/O Request Package)windows核心中,有一種系統元件——IRP,即輸入輸出請求包。

當上層應用程式需要訪問底層輸入輸出裝置時,發出I/O請求,系統會把這些請求轉化為IRP資料,不同的IRP會啟動I/O裝置驅動中對應的派遣函式。

IRP型別

由於IRP是響應上層應用程式的。可想而知,IRP型別是與上層對底層裝置的訪問型別相對應。

檔案相關的I/O函式如:CreateFile/ReadFile/WriteFile/CloseHandle等,作業系統就會將其轉為

IRP_MJ_CREATE/IRP_MJ_READ/IRP_MJ_WRITE/IRP_MJ_CLOSEIRP型別,這些IRP再被傳送到驅動程式的派遣函式中。

IRP列表如下:

名稱 描述 呼叫者
IRP_MJ_CREATE 請求一個控制程式碼 CreateFile
IRP_MJ_CLEANUP 在關閉控制程式碼時取消懸掛的IRP CloseHandle
IRP_MJ_CLOSE 關閉控制程式碼 CloseHandle
IRP_MJ_READ 從裝置得到資料 ReadFile
IRP_MJ_WRITE 傳送資料到裝置 WriteFile
IRP_MJ_DEVICE_CONTROL 控制操作(利用IOCTL巨集) DeviceIoControl
RP_MJ_INTERNAL_DEVICE_CONTROL 控制操作(只能被核心呼叫) N/A
IRP_MJ_QUERY_INFORMATION 得到檔案的長度 GetFileSize
IRP_MJ_SET_INFORMATION 設定檔案的長度 SetFileSize
IRP_MJ_FLUSH_BUFFERS 寫輸出緩衝區或者丟棄輸入緩衝區 FlushFileBuffers
FlushConsoleInputBuffer
PurgeComm
IRP_MJ_SHUTDOWN 系統關閉 InitiateSystemShutdown

IRP對應的派遣函式處理過程

多數的IRP都來自於Win32 API函式,如CreateFileReadFileWriteFile函式等等。

一種簡單的IRP派遣函式的實現就是:將該IRP中的狀態置為成功(pIRP->IoStatus =STATUS_SUCCESS ),然後結束該IRP請求(呼叫I噢CompleteRequest函式),並返回成功狀態。

NTSTATUS status = STATUS_SUCCESS;  
// 完成IRP  
pIrp->IoStatus.Status = status;  
pIrp->IoStatus.Information = 0;  // bytes xfered  
IoCompleteRequest( pIrp, IO_NO_INCREMENT );  

KdPrint(("Leave HelloDDKDispatchRoutin\n"));  

return status;  
VOID 
  IoCompleteRequest(
    IN PIRP  Irp,
    IN CCHAR  PriorityBoost
    );

下面以ReadFile為例,詳細介紹。

  1. ReadFile呼叫ntdll中的N他ReadFile。其中ReadFile函式是Win32 API,而NtReadFile函式是Native API
  2. ntdll中的N他ReadFile進入核心模式,並呼叫系統服務中的N他ReadFile函式。
  3. 系統服務函式N他ReadFile建立IRP_MJ_WRITE型別的IRP,然後將這個IRP函式傳送到對應驅動程式的派遣函式中。
  4. 在對應的派遣函式中一般會通過IoCompleteRequest函式將IRP請求結束。

篇2:

第5章 I/O Request Packet

5.1 資料結構

在處理 I/O 請求上,有兩個重要的資料結構:IRP(I/O request packet)IO_STACK_LOCATION

5.1.1 IRP 的結構

下面是在 windbg 裡得到的 IRP 結構:

nt!_IRP
   +0x000 Type             : Int2B
   +0x002 Size             : Uint2B
   +0x004 MdlAddress       : Ptr32 _MDL
   +0x008 Flags            : Uint4B
   +0x00c AssociatedIrp    : <unnamed-tag>
   +0x010 ThreadListEntry  : _LIST_ENTRY
   +0x018 IoStatus         : _IO_STATUS_BLOCK
   +0x020 RequestorMode    : Char
   +0x021 PendingReturned  : UChar
   +0x022 StackCount       : Char
   +0x023 CurrentLocation  : Char
   +0x024 Cancel           : UChar
   +0x025 CancelIrql       : UChar
   +0x026 ApcEnvironment   : Char
   +0x027 AllocationFlags  : UChar
   +0x028 UserIosb         : Ptr32 _IO_STATUS_BLOCK
   +0x02c UserEvent        : Ptr32 _KEVENT
   +0x030 Overlay          : <unnamed-tag>
   +0x038 CancelRoutine    : Ptr32     void 
   +0x03c UserBuffer       : Ptr32 Void
   +0x040 Tail             : <unnamed-tag>

MdlAddress 用來描述 user-mode bufferMDLmemory descriptor list),這個域僅用於“direct I/O”。
假如最上層的 device objectflags 標誌設定為 DO_DIRECT_IO 時:
(1) I/O 建立 IRP_MJ_READIRP_MJ_WRITE 時使用 MDL
(2) I/O 建立 IRP_MJ_DEVICE_CONTROL 時假如 control 程式碼為 METHOD_IN_DIRECTMETHOD_OUT_DIRECT,使用 MDL

MDL 描述 user-mode virtual buffer 也包含對應的 physical addressdriver 使用它能儘快地訪問 user-mode buffer

AssociatedIrq 是一個 union 成員,它的結構如下:

union {
        struct _IRP *MasterIrp;                                 // 此 IRP 是 associate IRP,它指向 master IRP
        LONG IrpCount;                                          // 此 IRP 是 master IRP,它指示 associate IRP 的個數
        PVOID SystemBuffer;                                     // 此 IRP 是 master IRP,它指向 system buffer
} AssociatedIrp;                                                // 用於和 user-mode buffer 進行資料交換。

AssociatedIrq.SystemBuffer 指向 kernel-mode nonpageddata buffer 區域,它使用在下面情形:
(1) 在 IRP_MJ_READIRP_MJ_WRITE 操作裡,假如最上層的 device objectflags 提供了 DO_BUFFERED_IO
(2) 在 IRP_MJ_DEVICE_CONTROL 操作裡,假如 I/O control code 指示需要 buffer。呼叫 WriteFile() 或者 DeviceIoControl() 用作輸入 data
I/O manager 複製 user-mode data buffer 到 kernel-mode data buffer 裡。
(3) 在讀操作裡,I/O manager 複製 kernel-mode data buffer 到 user-mode data buffer 裡。
IoStatus 是一個結構體,包含了兩個域:StatusInformation。IoStatus.Status 接收 NTSTATUS 碼,而 IoStatus.Information 是 ULONG 型別,
接收一個確切的值依賴於 IRP 的型別和完成的狀態。一個通常的用法是:Information 域儲存傳送資料的 bytes 數(例如在 IRP_MJ_READ 操作上)。
它的結構類似如下:

typedef struct _IO_STATUS_BLOCK
{
        union {
                ULONG Status;
                PVOID Pointer;
        };
        ULONG Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

RequestorModeUserMode 或者 KernelMode 這個值之一。
CancelRoutine 是 driver 中 IRP cancel routine 的地址,需要使用 IoSetCancelRoutine() 設定,避免直接對它進行設定。
UserBuffer 是儲存 user-modedata buffer,與 kernel-mode data buffer 進行資料交換。
最後是 Tail 成員,它是一個 union 變數,這個 Tail 比較複雜,它的結構如下:

union
{
    struct
    {
        union
        {   
            KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
            struct
            {
                PVOID DriverContext[4];
            };
        };

        PETHREAD thread;
        PCHAR AuxiliaryBuffer;

        struct
        {
            LIST_ENTRY ListEntry;
            union
            {
                struct _IO_STACK_LOCATION *CurrentStackLocation;
                ULONG PacketType;
            };
        };

        PFILE_OBJECT OriginalFileObject;
    } Overlay;

    KAPC Apc;
    PVOID CompletionKey;

} Tail;

Tail union 包括三個部分:OverlayApc 以及 CompletionKey

5.1.2 I/O stack

kernel-mode 程式建立一個 IRP 時,它同時也建立相應的 IO_STACK_LOCATION 結構的陣列,每個元素被稱為 stack location
一個 stack location 包含著 IRPtypeparameter 資訊,也包含著 completion routine 地址,它的結構如下:

nt!_IO_STACK_LOCATION
   +0x000 MajorFunction    : UChar
   +0x001 MinorFunction    : UChar
   +0x002 Flags            : UChar
   +0x003 Control          : UChar
   +0x004 Parameters       : <unnamed-tag>
   +0x014 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x018 FileObject       : Ptr32 _FILE_OBJECT
   +0x01c CompletionRoutine : Ptr32     long 
   +0x020 Context          : Ptr32 Void

其中,Parameters 成員定義為一個複雜的 union 結構,如下:

union
{
    //
    // NtCreateFIle 引數
    //
    struct 
    {
        PIO_SECURITY_CONTEXT SecurityContext;
        ULONG Options;
        USHORT POINTER_ALIGNMENT FileAttributes;
        USHORT ShareAccess;
        ULONG POINTER_ALIGNMENT EaLength;
    } Create;

    //
    // NtCreateNamedPipeFile 引數
    //
    struct
    {
        PIO_SECURITY_CONTEXT SecurityContext;
        ULONG Options;
        USHORT POINTER_ALIGNMENT Reserved;
        USHORT SharedAccess;
        PNAMED_PIPE_CREATE_PARAMETERS Parameters;
    } CreatePipe;

    //
    // NtCreateMailsotFIle  引數
    //
    struct
    {
        PIO_SECURITY_CONTEXT SecurityContext;
        ULONG Options;
        USHORT POINTER_ALIGNMENT Reserved;
        USHORT SharedAccess;
        PMAILSLOT_CREATE_PARAMETERS Parameters;
    } CreateMailslot;


    //
    // NtReadFile 引數
    //
    struct
    {
        ULONG Length;
        ULONG PINTER_ALIGNMENT Key;
        LARGE_INTEGER ByteOffset;
    } Read;

    //
    // NtWriteFile 引數
    //
    struct
    {
        ULONG Length;
        ULONG POINTER_ALIGNMENT Key;
        LARGE_INTEGER ByteOffset;
    } Write;

    //
    // NtQueryDirectoryFile 引數
    //
    struct
    {
        ULONG Length;
        PSTRING FileName;
        FILE_INFORMATION_CLASS FileInformationClass;
        ULONG POINTER_ALIGNMENT FileIndex;
    } QueryDirectory;

    //
    // NtNotifyChangeDirectoryFile 引數
    //
    struct
    {
        ULONG Length;
        ULONG POINTER_ALIGNMENT CompletionFilter;
    } NotifyDirectory;


    //
    // NtQueryInformationFile 引數
    //
    struct
    {
        ULONG Length;
        FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
    } QueryFile;

    //
    // NtSetInformationFile 引數
    //
    struct
    {
        ULONG Length;
        FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
        PFILE_OBJECT FileObject;
        union
        {
            struct
            {
                BOOLEAN ReplaceIfExists;
                BOOLEAN AdvanceOnly;
            };
            ULONG ClusterCount;
            HANDLE DeleteHandle;
        };
    } SetFile;


    //
    // NtQueryEaFile 引數
    //
    struct
    {
        ULONG Length;
        PVOID EaList;
        ULONG EaListLength;
        ULONG POINTER_ALIGNMENT EaIndex;
    } QueryEa;

    //
    // NtSetEaFIle 引數
    //
    struct
    {
        ULONG Length;
    } SetEa;

    //
    // NtQueryVolumeInformationFile 引數
    //
    struct
    {
        ULONG Length;
        FS_INORTMATION_CLASS POINTER_ALIGNMENT FsInformationClass;
    } QueryVoume;


    // 
    // NtSetVolumeInformationFile  引數
    //
    struct 
    {
        ULONG Length;
        FS_INFORMATION_CLASS POINTER_ALIGNMENT FsInformationClass;
    } setValue;


    //
    // NtFsControlFile  引數
    //
    struct
    {
        ULONG OutputBufferLength;
        ULONG POINTER_ALIGNMENT InputBufferLength;
        ULONG POINTER_ALIGNMENT  FsControlCode;
        PVOID Type3InputBuffer;
    } FileSystemControl;


    //
    // NtLockFile/NtUnlockFile 引數
    //
    struct
    {
        PLARGE_INTEGER Length;
        ULONG POINTER_ALIGNMENT Key;
        LARGE_INTEGER ByteOffset;
    } LockControl;


    //
    // NtDeviceIoControlFile 引數
    //
    struct
    {
        ULONG OutputBufferLength;
        ULONG POINTER_ALIGNMENT InputBufferLength;
        ULONG POINTER_ALIGNMENT IoControlCode;
        PVOID Type3InputBuffer;
    } DeviceIoControl;


    //
    // NtQuerySecurityObject 引數
    //
    struct
    {
        SECURITY_INFORMATION SecurityInformation;
        ULONG POINTER_ALIGNMENT Length;
    } QuerySecurity;


    //
    // NtSetScurityObject 引數
    //
    struct
    {
        SECURITY_INFORMATION SecurityInformation;
        PSECURITY_DESCRIPTOR SecurityDescriptor;
    } SetSecurity;


    //
    // MountVoluem  引數
    //
    struct
    {
        PVPB Vpb;
        PDEVICE_OBJECT DeviceObject;
    } MountVolume;


    //
    // VerifyVolume 引數
    //
    struct
    {
        PVPB Vpb;
        PDEVICE_OBJECT DeviceObject;
    } VerityVolume;


    //
    // Scsi 內部 device control
    //
    struct
    {
        struct _SCSI_REQUEST_BLOCK *Srb;
    } Scsi;


    //
    // NtQueryQuotaInformationFile  引數
    //
    struct
    {
        ULONG Length;
        PSID StartSid;
        PFILE_GET_QUOTA_INFORMATION SidList;
        ULONG SidListLength;
    } QueryQuota;


    //
    // NtSetQuotaInformationFile  引數
    //
    struct
    {
        ULONG Length;
    } SetQuota;


    //
    // IRP_MN_QUERY_DEVICE_RELATIONS 引數
    //
    struct
    {
        DEVICE_RELATION_TYPE Type;
    } QueryDevceRelations;


    //
    // IRP_MN_QUERY_INTERFACE 引數
    //
    struct
    {
        CONST GUID *InterfaceType;
        USHORT Size;
        USHORT Version;
        PINTERFACE Interface;
        PVOID InterfacespecificData;
    } QueryInterface;

    //
    // IRP_MN_QUERY_CAPABILITIES 引數
    //
    struct
    {
        PDEVICE_CAPABILITIES Capabilities;
    } DeviceCapabilities;


    //
    // IRP_MN_FILTER_RESOURCE_REQUIREMENTS 引數
    //
    struct
    {
        PIO_RESOURCE_REQUIREMENTS_LIST IoResourceRequirementList;
    } FilterResourceRequirements;


    //
    // IRP_MN_READ_CONFIG 和 IRP_MN_WRITE_CONFIG 引數
    //
    struct
    {
        ULONG WhichSpace;
        PVOID Buffer;
        ULONG Offset;
        ULONG POINTER_ALIGNMENT Length;
    } ReadWriteConfig;


    //
    // IRP_MN_SET_LOCK 引數
    //
    struct
    {
        BOOLEAN Lock;
    } SetLock;

    //
    // IRP_MN_QUERY_ID 引數
    //
    struct
    {
        BUS_QUERY_ID_TYPE IdType;   
    } QueryId;


    //
    // IRP_MN_QUERY_DEVICE_TEXT 引數
    //
    struct
    {
        DEVICE_TEXT_TYPE DeviceTextType;
        LCID POINTER_ALIGNMENT LocaleId;
    }  QueryDeviceText;


    //
    // IRP_MN_DEVICE_USAGE_NOTIFICATION 引數
    //
    struct
    {
        BOOLEAN InPath;
        BOOLEAN Reserved[3];
        DEVICE_USAGE_NOTIFICATION_TYPE POINTER_ALIGNMENT Type;
    } UsageNotification;


    //
    // IRP_MN_WAIT_WAKE 引數
    //
    struct
    {
        SYSTEM_POWER_STATE PowerState;
    } WaitWake;


    //
    // IRP_MN_POWER_SEQUENCE 引數
    //
    struct
    {
        PPOWER_SEQUENCE PowerSequence;
    } PowerSquence;


    //
    // IRP_MN_SET_POWER 和 IRP_MN_QUERY_POWER 引數
    //
    struct
    {
        ULONG SystemCotext;
        POWER_STATE_TYPE POINTER_ALIGNMENT Type;    
        POWER_STATE POINTER_ALIGNMENT State;
        POWER_ACTION POINTER_ALIGNMENT ShutdownType;
    } Power;


    //
    // StartDevice  引數
    //
    struct
    {
        PCM_RESOURCE_LIST AllocatedResources;
        PCM_RESOURCE_LIST AllocatedResourcesTranslated;
    } StartDevice;


    //
    // WMI Irps 引數
    //
    struct
    {
        ULONG_PTR ProviderId;
        PVOID DataPath;
        ULONG BufferSize;
        PVOID Buffer;
    } WMI;


    //
    // 其它 device 提供的引數
    //
    struct
    {
        PVOID Argument1
        PVOID Argument2;
        PVOID Argument3;
        PVOID Argument4;
    } Others;

} Parameters;

Parameters 為每個型別的 request 提供引數,例如:Create(IRP_MJ_CREATE 請求),Read(IRP_MJ_READ 請求),StartDevice(IRP_MJ_PNP 的子類 IRP_MN_START_DEVICE
MajorFunctionMinorFunction 分別的 IRP 的主功能號和子功能號。DeviceObjectstack entry 相應的 device objectIoCallDriver() 將填寫此域。
FileObject 指向 kernel file objectdriver 經常使用這個 FileObject 指向關聯的 IRP 在一個 request 佇列裡。
CompletionRoutine 是提供一個 I/O completion routine,不要直接設定這個域,而是使用 IoSetCompletionRoutine() 來設定。
Context 是任意的值,傳遞給 completion routine 作為引數。不要直接設定這個域,而是使用 IoSetCompletionRoutine() 來設定。

5.2 IRP 處理的標準模式

下面幾個階段:

I/O manager ---> Dispatch routine ---> StartIo routine ---> ISR ---> DPC routine ---> I/O manager

5.2.1 建立一個 IRP

IRP 的生存期從呼叫 I/O manager function 建立 IRP 開始,你可以使用下面 4 個 function 來建立一個新的 IRP
(1) IoBuildAsynchronousFsdRequest(): 建立一個 IRP 不希望等待。這個函式只適合用於某類的 IRP
(2) IoBuildSynchronousFsdRequest(): 建立一個 IRP 需要等待完成。
(3) IoBuildDeviceIoControlRequest(): 建立一個同步的 IRP_MJ_DEVICE_CONTROL 或者 IRP_MJ_INTERNAL_DEVICE_CONTROL
(4) IoAllcateIrp(): 建立一個 asynchronousIRP

>>> 建立 synchronous IRP
使用 IoBuildSynchronousFsdRequest()IoBuildDeviceIoControlRequest() 來建立同步的 IRP,同步 IRP 是屬於建立者執行緒。
由它有一個物主,由此有下面的一系列結果:
(1) 假如物主執行緒終止,I/O manager 自動取消屬於該執行緒的同步 IRPpending
(2) 由於建立執行緒擁有這個同步 IRP 的緣故,你不能在任意執行緒 context 裡建立同步 IRP,當物主執行緒終止後不能請求 I/O manager 取消 IRP
(3) 呼叫 IoCompleteRequest()I/O manager 自動清同步 IRP,並且置你必須提供的 eventsignaled 狀態。
(4) 在 I/O managerevent object signaled 狀態後,event object 仍存在時你必須小心地處理 event object
必須在 PASSIVE_LEVEL 裡呼叫這兩個函式,特別是不能在 APC_LEVEL 級別上。因為:在獲得 fast mutex 後進入到 APC_LEVEL,然後 I/O manager不能提交 special APC routine 去處理所有 complete 處理。

PIRP Irp = IoBuildSycnhronousFsdRequest(...);
ExAcqurireFastMutex(...);
NTSTATUS status = IoCallDriver(...);
if (status == STATUS_PENDING)
{
        KeWaitForSingleObject(...);             // 錯誤:不要這樣做
}
ExReleaseFastMutex(...);

在上面的程式碼裡,使用 KeWaitForSingleObject() 等待會進入死鎖:當完成執行 IoCompleteRequest(),這個 APC routine 執行將設定 event
因為已經在 APC_LEVEL 級別上,APC routine 不能執行去設定 event signled
假如你需要傳送一個 synchronous IRP 到其它 driver,考慮下面的選擇:
(1) 使用定期的 kernel mutex 來代替 fast mutexkernel mutex 將返回到 PASSIVE_LEVEL 級別上,並不會抑制 special APC 執行。
(2) 使用 KeEnterCriticalRegion() 來抑制所有除了 special APC 外,然後使用 ExAcquireFastMutexUnsafe() 來獲得 mutex
(3) 使用 asynchronous IRP(代替 synchronous IRP),完成後 signaled event

這兩個函式所建立的 IRP 為下表所示:

support function                                IRP 型別
------------------------------------------------------------------------------------
IoBuildSynchronousFsdRequest()                  IRP_MJ_READ
                                                IRP_MJ_WRITE
                                                IRP_MJ_FLUSH_BUFFERS
                                                IRP_MJ_SHUTDOWN
                                                IRP_MJ_PNP
                                                IRP_MJ_POWER(僅用於 IRP_MN_POWER_SEQUENCE)
-------------------------------------------------------------------------------------
IoBuildDeviceControlRequest()                   IRP_MJ_DEVICE_CONTROL
                                                IRP_MJ_INTERNAL_DEVICE_CONTROL

>>> 建立非同步 IRP
IoBuildAsynchronousFsdRequest() 和 IoAllocateIrp() 這兩上函式建立非同步的 IRP,這些可以建立 IRP 如下表所示:

support function                                IRP 型別
--------------------------------------------------------------------------------------
IoBuildAsynchronousFsdRequest()                 IRP_MJ_READ
                                                IRP_MJ_WRITE
                                                IRP_MJ_FLUSH_BUFFERS
                                                IRP_MJ_SHUTDOWN
                                                IRP_MJ_PNP
                                                IRP_MJ_POWER(僅用於 IRP_MN_POWER_SEQUENCE)
---------------------------------------------------------------------------------------
IoAllocateIrp()                                 任何(但必須初始化 Marjor function 表)

非同步 IRP 不屬於建立者執行緒,當 IRP 完成後,I/O manager 不呼叫 APC 以及不清理 IRP。考慮下面的問題:
(1) 當執行緒終止後,I/O manager 不會取消任何非同步 IRPpending
(2) 可以在任意執行緒 context 裡建立
(3) 由於 IRP 完成後 I/O manager 不清理 IRP,你必須提供一個complete routine 去釋放 buffer 以及呼叫 IoFreeIrp() 釋放 IRP 所使用的記憶體。
(4) 當長時期沒發生操作時,你可能需要提供一個 cancel routine
(5) 由於不需要等待非同步 IRP 的完成,你可以建立 IRPIRQL <= DISPATCH_LEVEL 級別上,當獲得 fast mutex 傳送非同步 IRP 是可以的。

5.2.2 dispatch routine

當建立一個 IRP 後,可以使用 IoGetNextIrpStackLocation() 來獲得 first stack location,然後需要對 stack location 進行初始化。
如果是使用 IoAllcateIrp() 建立的需要填寫相關的 MajorFunction 表。

PEDEVICE_OBJECT DeviceObject;
PIO_STACK_LOCATION stack = IoGetNextIrpStackLoction(Irp);
stack->MajorFunction = IRP_MJ_Xxx;
// ... stack 初始化程式碼
NTSTATUS status = IoCallDriver(DeviceObject, Irp);

IoGetNextIrpStackLocation() 是一個巨集用來獲得當前的 stack location,它的定義如下:
#define IoGetNextIrpStackLocation(Irp)          ((Irp)->Tail.Overlay.CurrentStackLocation - 1)

>>> What IoCallDriver Does
IoCallDriver() 看起來像下面:

NTSTATUS IoCallDriver(PDEVICE_OBJECT DeviceObject,
                PIRP Irp)
{
        IoSetNextIrpStackLocation(Irp);
        PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
        Stack->DeviceObject = DeviceObject;
        ULONG fcn = Stack->MajorFunction;
        PDRVIER_OBJECT Driver = DeviceObject->DriverObject;
        return (*Driver->MajorFunction[fcn]))(DeviceObject, Irp);
}

IoCallDriver() 簡單地呼叫 stack 指標對應的 driverdispatch routine

>>> location device objects
除了使用 IoAttachDeviceToDeviceStack() 外,可以使用另外的兩個方法:IoGetDeviceObjectPointer()IoGetAttachedDeviceReference()

NTSTATUS IoGetDeviceObjectPointer(
    IN PUNICODE_STRING  ObjectName,
    IN ACCESS_MASK  DesiredAccess,
    OUT PFILE_OBJECT  *FileObject,
    OUT PDEVICE_OBJECT  *DeviceObject
    );

假如你知道 device object 的名字,那麼你可以使用這個函式 IoGetDeviceObjectPointer() 得到 device object,像下面的用法:

PUNICODE_STRING devname;
ASSESS_MASK access;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
NTSTATUS status;

ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
status = IoGetDeviceObjectPointer(devname, access, &FileObject, &DeviceObject);

這個函式返回兩個指標:一個指向 FILE_OBJECT, 一個指向 DEVICE_OBJECT

PIRP Irp = IoXxx(...);
PIO_STACK_LOCATION Stack = IoGetNextIrpStackLocation(Irp);
ObReferenceObject(FileObject);
Stack->FileObject = FileObject;
IoCallDriver(DeviceObject, Irp);
ObDereferenceObject(FileObject);

IoGetDeviceObjectPointer() 執行一些步驟來定位返回的兩個指標:
(1) 使用 ZwOpenFile() 開啟一個命名的 device object,它將引發 object manager 建立一個 file object 和傳送 IRP_MJ_CREATE 到目標裝置,ZwOpenFile() 返回 file handle
(2) 呼叫 ObReferenceObjectByHandle() 得到代表 file handleFILE_OBJECT 結構地址,這個地址以 FileObject 返回。
(3) 呼叫 IoGetRelatedDeviceObject() 得到被 FileObject 引用的 DEVICE_OBJECT 結構地址,這個地址以 DeviceObject 返回。
(4) 呼叫 ZwClose() 關閉 Handle

5.2.3 Dispatch routine 職責

一個 Dispatch routine 的原型,看起來像下面:

NTSTATUS DispatchXxx(PDEVICE_OBJECT fdo, PRIP Irp)
{
        PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
        PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;

        ...
        return STATUS_Xxx;
}

1.你通常需要訪問當前 stack location 去檢測引數或都檢查 minor 功能號
2.你通常也需要訪問你建立的 device extension 結構(在 AddDevice() 時候初始化的)。
3.最後要返回到 IoCallDriver() 呼叫上。傳送一個 NTSTATUS 值。

>>> IRP 的完成
完成一個 IRP 必須填允 IRPIoStatus 域內的 StatusInformation 值,然後呼叫 IoCompleteRequest() 函式。
這個 Status 值是在 NTSTATUS.h 檔案裡定義的 status code 之一。而 Information 值依賴於 IRP 的型別而定,大多時候當 IRP 失敗後Information 設定為 0
IRP 引發資料的傳送操作,通常設定 Information 值為傳送的位元組數。

相關文章