windows内核开发学习笔记十五:IRP结构

  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数据结构

typedef struct _IRP {
    PMDL              MdlAddress;
    ULONG             Flags;
    union {
        struct _IRP*   MasterIrp;
        PVOID          SystemBuffer;
    } AssociatedIrp;
    IO_STATUS_BLOCK   IoStatus;
    KPROCESSOR_MODE   RequestorMode;
    BOOLEAN           PendingReturned;
    BOOLEAN           Cancel;
    KIRQL             CancelIrql;
    PDRIVER_CANCEL    CancelRoutine;
    PVOID             UserBuffer;
    union {
        struct {
            union {
                KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
                struct {
                    PVOID    DriverContext[4];
                };
            };
            PETHREAD     Thread;
            LIST_ENTRY   ListEntry;
        } Overlay;
    } Tail;
} IRP, *PIRP;

kd> dt 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>

除去一些简单的成员,例如Type,Size等,我们来看一下各个成员的具体意义:

  • MdlAddress : 是一个MDL的指针,当内核层和用户层采用共享内存的结构传递数据的时候,这个MDL就代表共享的内存信息(共享物理内存,通过MDL映射)。这个成员生效的标记为:DO_DIRECT_IO, METHOD_IN_DIRECT 或者METHOD_OUT_DIRECT。MdlAddress 用来描述 user-mode buffer 的 MDL(memory descriptor list),这个域仅用于“direct I/O”。 假如最上层的 device object 的 flags 标志设置为 DO_DIRECT_IO 时:

(1) I/O 建立 IRP_MJ_READ 和 IRP_MJ_WRITE 时使用 MDL。

(2) I/O 建立 IRP_MJ_DEVICE_CONTROL 时假如 control 代码为 METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,使用 MDL。 MDL 描述 user-mode virtual buffer 也包含对应的 physical address,driver 使用它能尽快地访问 user-mode buffer。

  • AssociatedIrp : 这个成员是个联合体,其中存在一个SystemBuffer程序;当内核层使用用户层的数据的时候是通过拷贝数据的方式来实现的话,那么拷贝后的数据就放在了AssociatedIrp.SystemBuffer中了。这个成员生效的标记是DO_BUFFERED_IO或者METHOD_BUFFERED。  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 nonpaged 的 data buffer 区域,它使用在下面情形:

(1) 在 IRP_MJ_READ 和 IRP_MJ_WRITE 操作里,假如最上层的 device object 的 flags 提供了 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 : 返回的状态信息。IoStatus 是一个结构体,包含了两个域:Status 与 Information。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;
  • RequestorMode : UserMode或KernelMode,指定原始I/O请求的来源。驱动程序有时需要查看这个值来决定是否要信任某些参数。UserMode 或者 KernelMode 这两个值之一。
  • PendingReturned : Pending 状态,如果为TRUE,则表明处理该IRP的最低级派遣例程返回了STATUS_PENDING。
  • StackCount : 设备栈的数目。
  • CurrentLocation : 当前处于哪个设备栈的索引。
  • Cancel : IRP是否被取消,如果为TRUE,则表明IoCancelIrp已被调用(该函数用于取消这个请求)。如果为FALSE,则表明没有调用IoCancelIrp函数。
  • CancelIrql(KIRQL) : 是一个IRQL值,表明那个专用的取消自旋锁是在这个IRQL上获取的.
  • CancelRoutine(PDRIVER_CANCEL) : 是驱动程序取消例程的地址。你应该使用IoSetCancelRoutine函数设置这个域而不是直接修改该域(因为可以原子修改),是 driver 中 IRP cancel routine 的地址,需要使用 IoSetCancelRoutine() 设置,避免直接对它进行设置。
  • UserBuffer(PVOID) : 用户层参数的直接地址,设置标记METHOD_NEITHER时候有效,UserBuffer 是保存 user-mode 的 data buffer,与 kernel-mode data buffer 进行数据交换。
  • Tail.Overlay 是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 包括三个部分:Overlay,Apc 以及 CompletionKey。

在这个图中,以水平方向从左到右是这个联合的三个可选成员,在垂直方向是每个结构的成员描述:

  • Tail.Overlay.DeviceQueueEntry(KDEVICE_QUEUE_ENTRY)和Tail.Overlay.DriverContext(PVOID[4])是Tail.Overlayare内一个未命名联合的两个可选成员(只能出现一个)。I/O管理器把DeviceQueueEntry作为设备标准请求队列中的连接域。当IRP还没有进入某个队列时,如果你拥有这个IRP你可以使用这个域,你可以任意使用DriverContext中的四个指针;Tail.Overlay.DeviceQueueEntry主要用在StartIo相关例程上面。
  • Tail.Overlay.ListEntry(LIST_ENTRY)仅能作为你自己实现的私有队列的连接域;这个成员比较重要,因为如果需要自己实现IRP异步执行的队列,那么就需要使用这个成员将IRP挂入到自己的队列中。

三、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为例,处理过程如下:。

  • ReadFile调用ntdll中的N他ReadFile。其中ReadFile函数是Win32 API,而NtReadFile函数是Native API
  • ntdll中的N他ReadFile进入内核模式,并调用系统服务中的N他ReadFile函数。
  • 系统服务函数N他ReadFile创建IRP_MJ_WRITE类型的IRP,然后将这个IRP函数发送到对应驱动程序的派遣函数中。
  • 在对应的派遣函数中一般会通过IoCompleteRequest函数将IRP请求结束。

windows内核开发学习笔记十五:IRP结构相关推荐

  1. windows内核开发学习笔记十八:IRP 处理的标准模式

    windows内核开发学习笔记十八:IRP 处理的标准模式 在 Windows 内核中的请求基本上是通过 I/O Request Packet 完成的. I/O manager ---> Dis ...

  2. windows内核开发学习笔记十七:IRP 和 IO_STACK_LOCATION 的交互

    windows内核开发学习笔记十七:IRP 和 IO_STACK_LOCATION 的交互 前面两篇学习笔记分别介绍了IRP和IO_STACK_LOCATION,整个设备栈来处理这个IRP,但是每个设 ...

  3. Windows驱动开发学习笔记(五)—— SSDT HOOK

    Windows驱动开发学习笔记(五)-- SSDT HOOK 系统服务表 系统服务描述符表 实验一:通过代码获取SSDT表地址 通过页表基址修改页属性 方法1:修改页属性 方法2:修改CR0寄存器 实 ...

  4. Polyworks脚本开发学习笔记(十五)-用Python连接Polyworks的COM组件

    Polyworks脚本开发学习笔记(十五)-用Python连接Polyworks的COM组件 用Polyworks脚本开发,没有高级语言的支持,功能难免单一,一些比较复杂的交互实现不了,界面和报告也很 ...

  5. windows内核开发学习笔记四十四:注册表存储结构-储巢

    上一篇文章学习了注册表的逻辑结构,接下来我这篇文章来学习注册表的存储结构.注册表实际存储是由一组储巢构成,每个储巢包含了一个由键和值构成的层次结构.下面表是windows的各个储巢的注册表路径和文件路 ...

  6. Windows驱动开发学习笔记(七)—— 多核同步内核重载

    Windows驱动开发学习笔记(七)-- 多核同步 基础知识 并发与同步 分析 InterlockedIncrement 原子操作相关API 内核文件 多核同步 临界区 示例一:错误的临界区 示例二: ...

  7. Windows驱动开发学习笔记(三)—— 内核空间内核模块

    Windows驱动开发学习笔记(三)-- 内核空间&内核模块 内核空间 实验 第一步:编译如下代码 第二步:将 .sys 文件拷贝到虚拟机中 第三步:部署 .sys 文件并运行 第四步:创建一 ...

  8. Windows驱动开发学习笔记(二)—— 驱动调试内核编程基础

    Windows驱动开发学习笔记(二)-- 驱动调试&内核编程基础 基础知识 驱动调试 PDB(Program Debug Database) WinDbg 加载 PDB 实验:调试 .sys ...

  9. Windows驱动开发学习笔记(六)—— Inline HOOK

    Windows驱动开发学习笔记(六)-- Inline HOOK SSDT HOOK Inline Hook 挂钩 执行流程 脱钩 实验一:3环 Inline Hook 实验二:0环 Inline H ...

最新文章

  1. Jenkins+Git+Maven持续集成经典教程
  2. 笔记本win7共享wifi操作说明
  3. 学习java技术有前途吗?当然有前途
  4. 使用 read_graphviz 将 GraphViz Dot 文本图加载到 BGL adjacency_list 图的简单示例
  5. 17.默认值Default.rs
  6. Windows高级编程学习笔记(三)
  7. hp服务器硬盘ultra320,惠普/hp 300GB 1.5万转 3.5寸 SAS服务器硬盘 P/N:431944-B21
  8. for循环两个分号之间不要乱加判断条件(记洛谷P2141题WA的经历,Java语言描述)
  9. JS进阶 你真的掌握变量和类型了吗?
  10. 二叉树——美国血统(洛谷 P1827)
  11. 互联网上,极致才能成功
  12. jsp中EL表达式不好使
  13. pid控制算法c语言,PID控制算法的C语言实现(三)
  14. Windows 7硬盘安装工具 NT6 HDD Installer v3.0(含图文教程)
  15. 数组和集合有什么区别
  16. 英语听力学习-VOA
  17. 解决CentOS7下用ntpdate同步时间问题
  18. python文字游戏循环3次_Python寻宝游戏中的无限循环
  19. 相机光学(十一)——镜头
  20. Wps文件如何转成word文档

热门文章

  1. MATLAB | 一文解决各类曲面交线绘制,包含三维隐函数曲面交线
  2. 转自博客园:http://www.cnblogs.com/txw1958/p/wechat-tutorial.html
  3. 谷粒商城高级篇(36)——商品上架之上传数据到Elasticsearch
  4. 微信小程序 post请求发送x-www-form-urlencoded类型数据
  5. 虚拟机启动时,提示找不到ISO映像文件
  6. c位边上还有什么位_λ(c位旁边二个位置叫什么)
  7. pygame-KidsCanCode系列jumpy-part1-如何组织复杂游戏的代码
  8. C# 金额转中文大写
  9. 07Linux打包解压文件-Exiting with failure status due to previous errors
  10. Codeforces1486 C1.Guessing the Greatest (easy version)(交互题+二分)