内核文件管理技术:构建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 驱动程序栈层次结构
-
最顶层:文件系统驱动程序(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 示例代码
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 常用构建函数
-
IoAllocateIrp
- 仅分配并初始化空IRP
- 原型:
PIRP IoAllocateIrp(CCHAR StackSize, BOOLEAN ChargeQuota)
-
IoBuildSynchronousFsdRequest
- 分配并初始化同步FSD请求IRP
- 原型:
PIRP IoBuildSynchronousFsdRequest( ULONG MajorFunction, PDEVICE_OBJECT DeviceObject, PVOID Buffer, ULONG Length, PLARGE_INTEGER StartingOffset, PKEVENT Event, PIO_STATUS_BLOCK IoStatusBlock)
-
IoBuildDeviceIoControlRequest
- 构建设备I/O控制请求(IOCTL)
-
IoBuildAsynchronousFsdRequest
- 构建异步FSD请求
4.2 其他关键函数
-
IoCallDriver
- 将IRP传递给下一个驱动程序
- 原型:
NTSTATUS IoCallDriver(PDEVICE_OBJECT, PIRP)
-
IoCreateFile
- 内核模式专用文件操作函数
- 可创建/打开文件、设备、目录或卷
-
ObReferenceObjectByHandle
- 通过句柄获取对象引用指针
- 原型:
NTSTATUS ObReferenceObjectByHandle( HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID *Object, POBJECT_HANDLE_INFORMATION HandleInformation)
5. 构建IRP创建文件实现
5.1 实现步骤
- 初始化内核事件用于同步
- 设置对象属性(文件名、句柄属性等)
- 使用IoCreateFile创建文件获取句柄
- 引用文件对象句柄获取文件对象指针
- 获取关联的设备对象
- 构建IRP(IoBuildSynchronousFsdRequest)
- 获取IRP堆栈位置并设置文件对象
- 发送IRP(IoCallDriver)并等待完成
- 释放资源(文件对象、关闭句柄)
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. 注意事项
-
内存管理:
- 自定义FILE_OBJECT时需要为FileName成员分配内存
- 使用ExAllocatePool进行动态内存分配
- 对象销毁时必须释放内存
-
IRP处理:
- 确保正确处理IRP完成状态
- 避免在持有自旋锁时调用IoCompleteRequest(可能导致死锁)
-
安全考虑:
- Rootkit可能拦截I/O请求进行篡改
- 驱动程序应验证所有输入参数
-
开发环境:
- 在虚拟机中进行驱动开发
- 使用快照功能防止系统崩溃