内核文件管理技术:构建IRP
字数 2369 2025-08-20 18:18:16

Windows内核文件管理技术:构建IRP

1. IRP基础概念

1.1 IRP简介

IRP(I/O Request Packet)是Windows操作系统内核中用于I/O操作的关键数据结构:

  • 用于驱动程序之间传递I/O请求
  • 描述并跟踪I/O操作的状态
  • 是驱动程序与操作系统处理I/O请求的核心机制

1.2 IRP结构

IRP总是伴随着一个或多个I/O栈位置结构(IO_STACK_LOCATION):

  • 分配IRP时必须指定I/O栈位置数量
  • 栈位置数量等于设备栈中设备对象的数量

重要成员变量:

  • MdlAddress:指向内存描述符列表(MDL)
  • AssociatedIrp.SystemBuffer:指向系统分配的缓冲区
  • IoStatus:包含操作状态和返回字节数
  • UserEvent:指向用户模式事件对象
  • UserBuffer:指向用户模式缓冲区
  • UserIosb:指向用户模式I/O状态块
  • RequestorMode:表示I/O请求模式(UserMode/KernelMode)
  • Flags:存储IRP相关标志位

1.3 IO_STACK_LOCATION结构

关键成员:

  • MajorFunction:IRP主功能代码(如IRP_MJ_READ)
  • MinorFunction:次功能代码
  • Parameters:与I/O请求类型相关的参数联合体
  • FileObject:指向相关文件对象
  • DeviceObject:指向当前处理IRP的设备对象

2. Windows I/O请求处理机制

2.1 驱动程序栈层次结构

  1. 最顶层:文件系统驱动程序(FSD)

    • 处理文件系统相关请求(如NTFS、FAT)
  2. 中间层:文件系统过滤驱动程序

    • 修改或监控I/O请求
    • 如杀毒软件的过滤驱动
  3. 底层:设备驱动程序

    • 直接与硬件设备交互(如disk.sys)
  4. 最底层:总线驱动程序

    • 管理硬件总线(PCI、USB等)

2.2 I/O请求处理流程

  1. 系统服务调度:用户模式调用(如CreateFile)转为内核调用(NtCreateFile)
  2. 创建IRP:I/O管理器创建并初始化IRP
  3. 驱动程序接收IRP:FSD根据MajorFunction传递给相应分发例程
  4. 分发例程处理
    • 执行相应操作(如权限检查)
    • 可传递给下层驱动程序
  5. 完成IRP
    • 填入IoStatus
    • 调用IoCompleteRequest
    • I/O管理器将结果返回用户模式

3. 分发例程

3.1 基本概念

  • 驱动程序处理IRP请求的核心回调函数
  • 函数原型:NTSTATUS MyDriverDispatchRoutine(PDEVICE_OBJECT, PIRP)
  • 必须调用IoCompleteRequest结束请求

3.2 主要分发例程类型

MajorFunction 对应API 功能
IRP_MJ_CREATE CreateFile/ZwCreateFile 创建文件或设备
IRP_MJ_CLOSE CloseHandle/ZwClose 关闭文件或设备
IRP_MJ_READ ReadFile/ZwReadFile 读取操作
IRP_MJ_WRITE WriteFile/ZwWriteFile 写入操作
IRP_MJ_DEVICE_CONTROL DeviceIoControl/ZwDeviceIoControl 设备控制请求

3.3 示例代码

NTSTATUS MyDriverCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    UNREFERENCED_PARAMETER(DeviceObject);
    
    // 设置IRP状态
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    
    // 完成IRP
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}

4. 构建IRP的关键函数

4.1 常用构建函数

  1. IoAllocateIrp

    • 仅分配并初始化空IRP
    • 原型:PIRP IoAllocateIrp(CCHAR StackSize, BOOLEAN ChargeQuota)
  2. IoBuildSynchronousFsdRequest

    • 分配并初始化同步FSD请求IRP
    • 原型:
      PIRP IoBuildSynchronousFsdRequest(
          ULONG MajorFunction,
          PDEVICE_OBJECT DeviceObject,
          PVOID Buffer,
          ULONG Length,
          PLARGE_INTEGER StartingOffset,
          PKEVENT Event,
          PIO_STATUS_BLOCK IoStatusBlock)
      
  3. IoBuildDeviceIoControlRequest

    • 构建设备I/O控制请求(IOCTL)
  4. IoBuildAsynchronousFsdRequest

    • 构建异步FSD请求

4.2 其他关键函数

  1. IoCallDriver

    • 将IRP传递给下一个驱动程序
    • 原型:NTSTATUS IoCallDriver(PDEVICE_OBJECT, PIRP)
  2. IoCreateFile

    • 内核模式专用文件操作函数
    • 可创建/打开文件、设备、目录或卷
  3. ObReferenceObjectByHandle

    • 通过句柄获取对象引用指针
    • 原型:
      NTSTATUS ObReferenceObjectByHandle(
          HANDLE Handle,
          ACCESS_MASK DesiredAccess,
          POBJECT_TYPE ObjectType,
          KPROCESSOR_MODE AccessMode,
          PVOID *Object,
          POBJECT_HANDLE_INFORMATION HandleInformation)
      

5. 构建IRP创建文件实现

5.1 实现步骤

  1. 初始化内核事件用于同步
  2. 设置对象属性(文件名、句柄属性等)
  3. 使用IoCreateFile创建文件获取句柄
  4. 引用文件对象句柄获取文件对象指针
  5. 获取关联的设备对象
  6. 构建IRP(IoBuildSynchronousFsdRequest)
  7. 获取IRP堆栈位置并设置文件对象
  8. 发送IRP(IoCallDriver)并等待完成
  9. 释放资源(文件对象、关闭句柄)

5.2 完整代码示例

#include <ntddk.h>

NTSTATUS MyCreateFile(PUNICODE_STRING FileName) {
    NTSTATUS status;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE fileHandle = NULL;
    PFILE_OBJECT fileObject = NULL;
    PDEVICE_OBJECT deviceObject = NULL;
    IO_STATUS_BLOCK ioStatusBlock;
    PIRP irp = NULL;
    KEVENT event;

    // 初始化事件
    KeInitializeEvent(&event, SynchronizationEvent, FALSE);

    // 初始化对象属性
    InitializeObjectAttributes(&objectAttributes, FileName, 
        OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);

    // 创建或打开文件
    status = IoCreateFile(
        &fileHandle, 
        GENERIC_WRITE | SYNCHRONIZE,
        &objectAttributes, 
        &ioStatusBlock,
        NULL, 
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        FILE_OPEN_IF, 
        FILE_SYNCHRONOUS_IO_NONALERT,
        NULL, 0, CreateFileTypeNone, NULL, 
        IO_NO_PARAMETER_CHECKING);

    if (!NT_SUCCESS(status)) {
        DbgPrint("IoCreateFile failed: 0x%X\n", status);
        return status;
    }

    // 引用文件对象
    status = ObReferenceObjectByHandle(fileHandle, FILE_READ_ACCESS, 
        *IoFileObjectType, KernelMode, (PVOID*)&fileObject, NULL);
    if (!NT_SUCCESS(status)) {
        DbgPrint("ObReferenceObjectByHandle failed: 0x%X\n", status);
        ZwClose(fileHandle);
        return status;
    }

    // 获取设备对象
    deviceObject = IoGetRelatedDeviceObject(fileObject);

    // 构建IRP
    irp = IoBuildSynchronousFsdRequest(
        IRP_MJ_WRITE, 
        deviceObject,
        NULL, 0, NULL, &event, &ioStatusBlock);

    if (irp == NULL) {
        DbgPrint("IoBuildSynchronousFsdRequest failed\n");
        ObDereferenceObject(fileObject);
        ZwClose(fileHandle);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // 设置IRP堆栈位置
    PIO_STACK_LOCATION irpStack = IoGetNextIrpStackLocation(irp);
    irpStack->FileObject = fileObject;

    // 发送IRP
    status = IoCallDriver(deviceObject, irp);
    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatusBlock.Status;
    }

    // 处理结果
    if (NT_SUCCESS(status)) {
        DbgPrint("File created successfully");
    } else {
        DbgPrint("Failed to create file. Status: 0x%X\n", status);
    }

    // 清理资源
    ObDereferenceObject(fileObject);
    ZwClose(fileHandle);
    return status;
}

VOID MyDriverUnload(PDRIVER_OBJECT DriverObject) {
    DbgPrint("Driver Unload Called\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNICODE_STRING fileName;
    RtlInitUnicodeString(&fileName, L"\\??\\C:\\work\\syswork10\\File.txt");
    
    DriverObject->DriverUnload = MyDriverUnload;
    
    NTSTATUS status = MyCreateFile(&fileName);
    if (!NT_SUCCESS(status)) {
        DbgPrint("Failed to create file. Status: 0x%X\n", status);
    }
    return status;
}

6. 注意事项

  1. 内存管理

    • 自定义FILE_OBJECT时需要为FileName成员分配内存
    • 使用ExAllocatePool进行动态内存分配
    • 对象销毁时必须释放内存
  2. IRP处理

    • 确保正确处理IRP完成状态
    • 避免在持有自旋锁时调用IoCompleteRequest(可能导致死锁)
  3. 安全考虑

    • Rootkit可能拦截I/O请求进行篡改
    • 驱动程序应验证所有输入参数
  4. 开发环境

    • 在虚拟机中进行驱动开发
    • 使用快照功能防止系统崩溃
Windows内核文件管理技术:构建IRP 1. IRP基础概念 1.1 IRP简介 IRP(I/O Request Packet)是Windows操作系统内核中用于I/O操作的关键数据结构: 用于驱动程序之间传递I/O请求 描述并跟踪I/O操作的状态 是驱动程序与操作系统处理I/O请求的核心机制 1.2 IRP结构 IRP总是伴随着一个或多个I/O栈位置结构(IO_ STACK_ LOCATION): 分配IRP时必须指定I/O栈位置数量 栈位置数量等于设备栈中设备对象的数量 重要成员变量: MdlAddress :指向内存描述符列表(MDL) AssociatedIrp.SystemBuffer :指向系统分配的缓冲区 IoStatus :包含操作状态和返回字节数 UserEvent :指向用户模式事件对象 UserBuffer :指向用户模式缓冲区 UserIosb :指向用户模式I/O状态块 RequestorMode :表示I/O请求模式(UserMode/KernelMode) Flags :存储IRP相关标志位 1.3 IO_ STACK_ LOCATION结构 关键成员: MajorFunction :IRP主功能代码(如IRP_ MJ_ READ) MinorFunction :次功能代码 Parameters :与I/O请求类型相关的参数联合体 FileObject :指向相关文件对象 DeviceObject :指向当前处理IRP的设备对象 2. Windows I/O请求处理机制 2.1 驱动程序栈层次结构 最顶层 :文件系统驱动程序(FSD) 处理文件系统相关请求(如NTFS、FAT) 中间层 :文件系统过滤驱动程序 修改或监控I/O请求 如杀毒软件的过滤驱动 底层 :设备驱动程序 直接与硬件设备交互(如disk.sys) 最底层 :总线驱动程序 管理硬件总线(PCI、USB等) 2.2 I/O请求处理流程 系统服务调度 :用户模式调用(如CreateFile)转为内核调用(NtCreateFile) 创建IRP :I/O管理器创建并初始化IRP 驱动程序接收IRP :FSD根据MajorFunction传递给相应分发例程 分发例程处理 : 执行相应操作(如权限检查) 可传递给下层驱动程序 完成IRP : 填入IoStatus 调用IoCompleteRequest I/O管理器将结果返回用户模式 3. 分发例程 3.1 基本概念 驱动程序处理IRP请求的核心回调函数 函数原型: NTSTATUS MyDriverDispatchRoutine(PDEVICE_OBJECT, PIRP) 必须调用IoCompleteRequest结束请求 3.2 主要分发例程类型 | MajorFunction | 对应API | 功能 | |--------------|--------|------| | IRP_ MJ_ CREATE | CreateFile/ZwCreateFile | 创建文件或设备 | | IRP_ MJ_ CLOSE | CloseHandle/ZwClose | 关闭文件或设备 | | IRP_ MJ_ READ | ReadFile/ZwReadFile | 读取操作 | | IRP_ MJ_ WRITE | WriteFile/ZwWriteFile | 写入操作 | | IRP_ MJ_ DEVICE_ CONTROL | DeviceIoControl/ZwDeviceIoControl | 设备控制请求 | 3.3 示例代码 4. 构建IRP的关键函数 4.1 常用构建函数 IoAllocateIrp 仅分配并初始化空IRP 原型: PIRP IoAllocateIrp(CCHAR StackSize, BOOLEAN ChargeQuota) IoBuildSynchronousFsdRequest 分配并初始化同步FSD请求IRP 原型: IoBuildDeviceIoControlRequest 构建设备I/O控制请求(IOCTL) IoBuildAsynchronousFsdRequest 构建异步FSD请求 4.2 其他关键函数 IoCallDriver 将IRP传递给下一个驱动程序 原型: NTSTATUS IoCallDriver(PDEVICE_OBJECT, PIRP) IoCreateFile 内核模式专用文件操作函数 可创建/打开文件、设备、目录或卷 ObReferenceObjectByHandle 通过句柄获取对象引用指针 原型: 5. 构建IRP创建文件实现 5.1 实现步骤 初始化内核事件用于同步 设置对象属性(文件名、句柄属性等) 使用IoCreateFile创建文件获取句柄 引用文件对象句柄获取文件对象指针 获取关联的设备对象 构建IRP(IoBuildSynchronousFsdRequest) 获取IRP堆栈位置并设置文件对象 发送IRP(IoCallDriver)并等待完成 释放资源(文件对象、关闭句柄) 5.2 完整代码示例 6. 注意事项 内存管理 : 自定义FILE_ OBJECT时需要为FileName成员分配内存 使用ExAllocatePool进行动态内存分配 对象销毁时必须释放内存 IRP处理 : 确保正确处理IRP完成状态 避免在持有自旋锁时调用IoCompleteRequest(可能导致死锁) 安全考虑 : Rootkit可能拦截I/O请求进行篡改 驱动程序应验证所有输入参数 开发环境 : 在虚拟机中进行驱动开发 使用快照功能防止系统崩溃