内核下通过直接向 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);
此时不再用到驱动器句柄及其文件对象,分别调用 ZwClose 和 ObDereferenceObject 函数关闭和释放。
3.根据物理卷设备栈大小创建 IRP, 再创建事件用于等待 IRP 处理完成
分别调用 IoAllocateIrp 和 KeInitializeEvent 创建 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);