内核文件管理-IRP(一)创建或打开文件

内核下通过直接向 FSD (File System Driver) 发送 IRP (I/O Request Packet)来管理文件,可以绕过一些 API Hook。
本文讲怎样通过向 FSD 发送 IRP_MJ_CREATE 消息的 IRP 来创建或打开文件(或目录)。

头文件及其他声明或定义

#include <ntifs.h>

typedef struct _AUX_ACCESS_DATA {
	PPRIVILEGE_SET PrivilegesUsed;
	GENERIC_MAPPING GenericMapping;
	ACCESS_MASK AccessesToAudit;
	ACCESS_MASK MaximumAuditMask;
	ULONG Unknown[256];
} AUX_ACCESS_DATA, * PAUX_ACCESS_DATA;

// 内核的导出函数ObCreateObject和SeCreateAccessState
NTSTATUS ObCreateObject(
	__in KPROCESSOR_MODE ProbeMode,				// 决定是否要验证参数
	__in POBJECT_TYPE ObjectType,				// 对象类型指针
	__in POBJECT_ATTRIBUTES ObjectAttributes,	// 对象的属性, 最终会转化成ObAllocateObject需要的OBJECT_CREATE_INFORMATION结构
	__in KPROCESSOR_MODE OwnershipMode,			// 指定内核对象还是用户对象, 同上
	__inout_opt PVOID ParseContext,				// 这参数没用
	__in ULONG ObjectBodySize,					// 对象体大小
	__in ULONG PagedPoolCharge,					// ...
	__in ULONG NonPagedPoolCharge,				// ...
	__out PVOID* Object							// 接收对象体的指针
);

NTSTATUS SeCreateAccessState(
	PACCESS_STATE AccessState,
	PVOID AuxData,
	ACCESS_MASK DesiredAccess,
	PGENERIC_MAPPING GenericMapping
);

IrpCrateFile函数参数

NTSTATUS IrpCreateFile(
	OUT PFILE_OBJECT* ppFileObject,					// 用于返回文件句柄的指针
	IN	ACCESS_MASK DesiredAccess,					// 指定访问权限
	IN	PUNICODE_STRING pustrFilePath,				// 文件绝对路径
	OUT PIO_STATUS_BLOCK pIoStatusBlock,			// 用于返回最终完成的状态和其他请求操作的信息
	IN	PLARGE_INTEGER AllocationSize OPTIONAL,		// 包含初始分配大小的LARGE_INTEGER指针(用于文件创建或覆盖, NULL为不分配, 可选)
	IN	ULONG FileAttributes,						// 文件属性
	IN	ULONG ShareAccess,							// 共享访问类型
	IN	ULONG CreateDisposition,					// 指定在文件存在/不存在时要执行的操作
	IN	ULONG CreateOptions,						// 指定创建或打开文件时要应用的选项
	IN	PVOID EaBuffer OPTIONAL,					// 对于设备和中间驱动程序, 此参数必须是NULL指针。
	IN	ULONG EaLength);							//

路径参数形如 “C:\Test\test.txt”, 不用添加 “\??\” 前缀。
其他参数的值请参考Microsoft官网

IrpCrateFile实现

变量定义略,具体参见源代码

1.获得文件所在驱动器的句柄

根据文件路径参数得到驱动器名称, 再调用函数 IoCreateFile 打开驱动器, 成功则会返回其句柄 hDriver。

	wcscpy(wszRootPath, L"\\??\\A:\\");
	wszRootPath[4] = pustrFilePath->Buffer[0];		// 文件绝对路径第一个字符为盘符
	RtlInitUnicodeString(&ustrRootPath, wszRootPath);
	InitializeObjectAttributes(&objectAttributes, &ustrRootPath, OBJ_KERNEL_HANDLE, NULL, NULL);
	status = IoCreateFile(&hDriver, GENERIC_READ | SYNCHRONIZE, &objectAttributes,
		pIoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone,
		NULL, IO_NO_PARAMETER_CHECKING);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

2.由驱动器句柄获得其文件对象,由此再进一步得到文件系统逻辑卷对象和物理卷设备

调用 ObReferenceObjectByHandle 函数由句柄得到文件对象后,利用文件对象中的 VPB (Volume Parameter Block) 获得文件系统逻辑卷对象和物理卷设备。

	status = ObReferenceObjectByHandle(hDriver, FILE_READ_ACCESS, *IoFileObjectType, KernelMode, &pDriverFileObject, NULL);
	if (!NT_SUCCESS(status))
	{
		ZwClose(hDriver);
		return status;
	}
	pDeviceObject = pDriverFileObject->Vpb->DeviceObject;
	pRealDevice = pDriverFileObject->Vpb->RealDevice;
	ObDereferenceObject(pDriverFileObject);
	ZwClose(hDriver);

此时不再用到驱动器句柄及其文件对象,分别调用 ZwCloseObDereferenceObject 函数关闭和释放。

3.根据物理卷设备栈大小创建 IRP, 再创建事件用于等待 IRP 处理完成

分别调用 IoAllocateIrpKeInitializeEvent 创建 IRP 和事件。
注意:IRP 最终要通过 IoFreeIrp 释放。

	pIRP = IoAllocateIrp(pDeviceObject->StackSize, FALSE);
	if (NULL == pIRP)
	{
		return STATUS_UNSUCCESSFUL;
	}
	KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);

4.创建一个空文件对象并设置

调用 ObCreateObject 创建一个空文件对象 pFileObject

	InitializeObjectAttributes(&objectAttributes, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
	status = ObCreateObject(KernelMode, *IoFileObjectType, &objectAttributes, 
		KernelMode, NULL, sizeof(FILE_OBJECT), 0, 0, >&pFileObject);
	if (!NT_SUCCESS(status))
	{
		IoFreeIrp(pIRP);
		return status;
	}

进行设置:

	RtlZeroMemory(pFileObject, sizeof(FILE_OBJECT));
	pFileObject->Type = IO_TYPE_FILE;
	pFileObject->Size = sizeof(FILE_OBJECT);
	pFileObject->DeviceObject = pRealDevice;
	pFileObject->Flags = FO_SYNCHRONOUS_IO;
	pFileObject->FileName.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, 512);
			// 这里最好动态创建,否则 ObDereferenceObject 该文件对象时会蓝屏。
	if (pFileObject->FileName.Buffer == NULL)
	{
		IoFreeIrp(pIRP);
		ObDereferenceObject(pFileObject);
		return STATUS_UNSUCCESSFUL;
	}
	pFileObject->FileName.MaximumLength = 512;
	pFileObject->FileName.Length = pustrFilePath->Length - 3 -1;
	RtlZeroMemory(pFileObject->FileName.Buffer, 512);
	RtlCopyMemory(pFileObject->FileName.Buffer, &pustrFilePath->Buffer[2], pFileObject->FileName.Length);
	KeInitializeEvent(&pFileObject->Lock, SynchronizationEvent, FALSE);
	KeInitializeEvent(&pFileObject->Event, NotificationEvent, FALSE);

5.创建权限状态来设置访问文件的权限,然后设置安全内容

调用 SeCreateAccessState 创建权限状态,设置访问文件的权限。

	RtlZeroMemory(&auxAccessData, sizeof(auxAccessData));
	status = SeCreateAccessState(&accessData, &auxAccessData, DesiredAccess, IoGetFileObjectGenericMapping());
	if (!NT_SUCCESS(status))
	{
		IoFreeIrp(pIRP);
		ObDereferenceObject(pFileObject);
		return status;
	}

设置安全内容 IO_SECURITY_CONTEXT

	ioSecurityContext.SecurityQos = NULL;
	ioSecurityContext.AccessState = &accessData;
	ioSecurityContext.DesiredAccess = DesiredAccess;
	ioSecurityContext.FullCreateOptions = 0;

6.设置 IRP,获取下一个 IRP 的 IO_STACK_LOCATION 并设置

设置 IRP:

	RtlZeroMemory(pIoStatusBlock, sizeof(IO_STATUS_BLOCK));
	pIRP->MdlAddress = NULL;
	pIRP->AssociatedIrp.SystemBuffer = EaBuffer;
	pIRP->Flags = IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API;
	pIRP->RequestorMode = KernelMode;
	pIRP->UserIosb = pIoStatusBlock;
	pIRP->UserEvent = &kEvent;
	pIRP->PendingReturned = FALSE;
	pIRP->Cancel = FALSE;
	pIRP->CancelRoutine = NULL;
	pIRP->Tail.Overlay.Thread = PsGetCurrentThread();
	pIRP->Tail.Overlay.AuxiliaryBuffer = NULL;
	pIRP->Tail.Overlay.OriginalFileObject = pFileObject;

调用 IoGetNextIrpStackLocation 获取下一个 IRP 的 IO_STACK_LOCATION 并设置:

	pIoStackLocation = IoGetNextIrpStackLocation(pIRP);
	pIoStackLocation->MajorFunction = IRP_MJ_CREATE;
	pIoStackLocation->DeviceObject = pDeviceObject;
	pIoStackLocation->FileObject = pFileObject;
	pIoStackLocation->Parameters.Create.SecurityContext = &ioSecurityContext;
	pIoStackLocation->Parameters.Create.Options = (CreateDisposition << 24) | CreateOptions;
	pIoStackLocation->Parameters.Create.FileAttributes = (USHORT)FileAttributes;
	pIoStackLocation->Parameters.Create.ShareAccess = (USHORT)ShareAccess;
	pIoStackLocation->Parameters.Create.EaLength = EaLength;

7.设置完成实例

IRP 处理完成后会执行该函数,这里该函数用来通知 IRP 处理完成,同时释放资源。
调用 IoSetCompletionRoutine 设置完成实例:

	IoSetCompletionRoutine(pIRP, CompleteRoutine, NULL, TRUE, TRUE, TRUE);

CompleteRoutine 函数如下:

NTSTATUS CompleteRoutine(
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP pIRP,
	IN PVOID Context)
{
	*pIRP->UserIosb = pIRP->IoStatus;
	// 设置事件信号来通知完成
	if (pIRP->UserEvent)
	{
		KeSetEvent(pIRP->UserEvent, IO_NO_INCREMENT, FALSE);
	}
	// 释放IRP
	IoFreeIrp(pIRP);
	pIRP = NULL;

	return STATUS_MORE_PROCESSING_REQUIRED;
}

8.发送 IRP,等待处理完成后判断结果,返回文件句柄

调用 IoCallDriver 发送 IRP:

	status = IoCallDriver(pDeviceObject, pIRP);

等待 IRP 处理完成:

	if (STATUS_PENDING == status)
	{
		KeWaitForSingleObject(&kEvent, Executive, KernelMode, TRUE, NULL);
	}

判断 IRP 处理结果,返回文件对象句柄:

	status = pIoStatusBlock->Status;
	if (!NT_SUCCESS(status))
	{
		ObDereferenceObject(pFileObject);
		return status;
	}
	InterlockedIncrement(&pFileObject->DeviceObject->ReferenceCount);
	if (pFileObject->Vpb)
	{
		InterlockedIncrement(&pFileObject->Vpb->ReferenceCount);
	}
	*ppFileObject = pFileObject;	// 设置返回的文件对象句柄

	return status;					// 函数返回

例子

以下代码在 C:\ 处创建了一个文件夹 Hello (如果不存在的话):

	UNICODE_STRING ustrCreateFile;
	RtlInitUnicodeString(&ustrCreateFile, L"C:\\Hello");
	PFILE_OBJECT hFile = NULL;
	IO_STATUS_BLOCK iosb = { 0 };
	NTSTATUS status = STATUS_SUCCESS;
	status = IrpCreateFile(&hFile, SYNCHRONIZE,
		&ustrCreateFile, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE,
		FILE_OPEN_IF, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
		NULL, 0);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("[ERROR]   IrpCreateFile: 创建目录\n");
		return;
	}
	DbgPrint("[SUCCESS] IrpCreateFile: 创建目录\n");
	ObDereferenceObject(hFile);

以下代码在 C:\Hello\ 处创建了一个文件 test.txt (如果不存在的话,存在则单纯打开又关闭):

	UNICODE_STRING ustrCreateFile;
	RtlInitUnicodeString(&ustrCreateFile, L"C:\\Hello\\test.txt");
	PFILE_OBJECT hFile = NULL;
	IO_STATUS_BLOCK iosb = { 0 };
	NTSTATUS status = STATUS_SUCCESS;
	status = IrpCreateFile(&hFile, GENERIC_READ, &ustrCreateFile, 
		&iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0,
		FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("[ERROR]   IrpCreateFile: 创建文件\n");
		return;
	}
	DbgPrint("[SUCCESS] IrpCreateFile: 创建文件\n");
	ObDereferenceObject(hFile);

下一篇:内核文件管理-IRP(二)文件属性查询和设置

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值