发送请求_内核层自己发送IRP请求操作文件全面总结

本文深入探讨了Windows内核中如何通过发送IRP请求来操作文件,涵盖了从IRP_MJ_CREATE到IRP_MJ_CLOSE的各种操作,包括文件的创建、关闭、读写、设置信息和查询等。作者低调putchar提供了自己封装发送IRP请求的实现,并给出了测试案例。
摘要由CSDN通过智能技术生成
b8dd8c54f6cd4107fdf8211e0a3dc8bf.png

本文为看雪论精华文章

看雪论坛作者ID:低调putchar

e741b88e3ecc4960ce61e65cd4d9023c.gif

一、概述

e741b88e3ecc4960ce61e65cd4d9023c.gif
Windows操作系统中,文件系统过滤驱动的过滤设备绑定在文件系统(FSD)设备之上,监视和过滤我们的文件访问。 当应用层发起文件操作调用时内核层都会转换为IRP发送到设备栈中位于栈顶的设备,然后通过IO栈单元(IO_STACK_LOCATION)保存一些参数把IRP请求继续向下层设备发送,最后至FSD,由FSD完成实际的文件操作。 卷参数块(VPB)保存了文件系统中在一个卷设备创建(卷的挂载)成功时的一些卷参数信息。wdm.h中对其结构定义如下:
typedef struct _VPB {
        CSHORT Type;    CSHORT Size;    USHORT Flags;    USHORT VolumeLabelLength; // in bytes    struct _DEVICE_OBJECT *DeviceObject;  //文件系统卷设备    struct _DEVICE_OBJECT *RealDevice;    //卷管理器生成的卷设备    ULONG SerialNumber;    ULONG ReferenceCount;    WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)];} VPB, *PVPB;
文件系统过滤驱动对文件操作的过滤,实际上就是把过滤设备绑定在文件系统卷设备: Vpb->DeviceObject上来实现过滤的。 这里要总结的是:如何在内核层自己创建并填写IRP及下层栈单元,把请求直接送往FSD的卷设备上。常用的文件操作包括:
  • 文件的生成/打开(IRP_MJ_CREATE)

  • 文件的关闭(IRP_MJ_CLEANUP:  表示句柄数为0,IRP_MJ_CLOSE: 表示文件对象引用计数为0,即将销毁)

  • 文件读/写(IRP_MJ_READ/IRP_MJ_WRITE)

  • 文件设置(IRP_MJ_SET_INFORMATION)

  • 文件查询(IRP_MJ_QUERY_INFORMATION)

  • 目录下项查询(MajorFuncton: IRP_MJ_DIRECTORY_CONTROL, MinorFunction: IRP_MN_QUERY_DIRECTORY)


每类操作对应的内核函数如下表所示: 49130264ad7a673dfc833a051a591a79.png e741b88e3ecc4960ce61e65cd4d9023c.gif

二、自己发送IRP请求操作文件的封装

e741b88e3ecc4960ce61e65cd4d9023c.gif 1. 实现MDL链解锁及释放,完成例程的功能,后续的各IRP完成时统一使用。
(1)解锁并释放MDL链
//解锁并释放MDLVOID MyFreeMdl(    IN PMDL pMdl){
        PMDL pMdlCurrent = pMdl, pMdlNext;    while (pMdlCurrent != NULL) {
            pMdlNext = pMdlCurrent->Next;        if (pMdlCurrent->MdlFlags&MDL_PAGES_LOCKED) {
                MmUnlockPages(pMdlCurrent);        }        IoFreeMdl(pMdlCurrent);        pMdlCurrent = pMdlNext;    }    return;}

(2)文件操作完成例程:
//文件操作完成例程NTSTATUS FileOperationCompletion(    IN PDEVICE_OBJECT pDeviceObject,    IN PIRP pIrp,    IN PVOID pContext OPTIONAL){
        PKEVENT pkEvent = pIrp->UserEvent;    ASSERT(pkEvent != NULL);    //趁机拷贝完成状态    RtlCopyMemory(        pIrp->UserIosb,        &pIrp->IoStatus,        sizeof(IO_STATUS_BLOCK)    );    //检查并释放MDL    if (pIrp->MdlAddress != NULL) {
            MyFreeMdl(pIrp->MdlAddress);        pIrp->MdlAddress = NULL;    }    //释放IRP    IoFreeIrp(pIrp);    //设置事件    KeSetEvent(pkEvent,        IO_NO_INCREMENT,        FALSE    );    return STATUS_MORE_PROCESSING_REQUIRED;}

2. 文件的打开/生成: 与其它IRP不同,在发送IRP_MJ_CREATE请求前就需要创建并填写好文件对象,FSD的卷设备可以从根目录pRootFileObject->Vpb->DeviceObject得到,对应的卷管理器生成的卷设备可以从: pRootFileObject->Vpb->RealDevice得到。 (1)创建文件对象可以用已导出但未公开的nt!ObCreateObject,该函数原型如下:
//未公开的函数,直接声明下就可以用了//创建对象NTKERNELAPINTSTATUS NTAPI ObCreateObject(    IN KPROCESSOR_MODE ObjectAttributesAccessMode  OPTIONAL,    IN POBJECT_TYPE  Type,    IN POBJECT_ATTRIBUTES ObjectAttributes  OPTIONAL,    IN KPROCESSOR_MODE  AccessMode,    IN OUT PVOID ParseContext  OPTIONAL,    IN ULONG  ObjectSize,    IN ULONG PagedPoolCharge  OPTIONAL,    IN ULONG NonPagedPoolCharge  OPTIONAL,    OUT PVOID * Object);
(2)设置安全状态可以用已导出但未公开的nt!SeCreateAccessState,该函数原型如下:
//设置安全状态NTKERNELAPINTSTATUS NTAPI SeCreateAccessState(    __out PACCESS_STATE AccessState,    __out PAUX_ACCESS_DATA AuxData,    __in ACCESS_MASK DesiredAccess,    __in_opt PGENERIC_MAPPING GenericMapping);未公开的数据结构//SeCreateAccessState第2个参数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;
(3)自己创建并发送IRP进行文件打开/生成的实现:
//自己发送IRP请求生成/打开文件(IRP_MJ_CREATE)NTSTATUS IrpCreateFile(    OUT PFILE_OBJECT *ppFileObject,    OUT PDEVICE_OBJECT *ppDeviceObject, //如果成功,这里保存:(*ppFileObject)->Vpb->DeviceObject    IN ACCESS_MASK DesiredAccess,    IN PUNICODE_STRING punsFilePath, //必须以"盘符:\"开头, 比如: "C:\..."    OUT PIO_STATUS_BLOCK pIoStatusBlock,    IN PLARGE_INTEGER AllocationSize OPTIONAL,    IN ULONG FileAttributes,    IN ULONG ShareAccess,    IN ULONG Disposition,    IN ULONG CreateOptions,    IN PVOID EaBuffer OPTIONAL,    IN ULONG EaLength){
        PAUX_ACCESS_DATA pAuxData = NULL;    HANDLE hRoot = NULL;    PFILE_OBJECT pRootObject = NULL, pFileObject = NULL;    PDEVICE_OBJECT pDeviceObject = NULL, pRealDeviceObject = NULL;    PIRP pIrp;    PIO_STACK_LOCATION pIrpSp;    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;    ACCESS_STATE AccessState;    KEVENT kEvent;    IO_SECURITY_CONTEXT IoSecurityContext;    OBJECT_ATTRIBUTES objAttrs;    UNICODE_STRING unsRootPath;    IO_STATUS_BLOCK userIosb;    WCHAR wRootPath[8];  //"\??\C:\"    //路径长度检查(最短: "C:\")    if (punsFilePath->Length < 3 * sizeof(CHAR)) {
            return STATUS_INVALID_PARAMETER;    }    RtlZeroMemory(pIoStatusBlock, sizeof(IO_STATUS_BLOCK));    //根目录符号链接    ntStatus = RtlStringCbPrintfW(        wRootPath,        sizeof(wRootPath),        L"\\??\\%c:\\",        punsFilePath->Buffer[0]);    ASSERT(NT_SUCCESS(ntStatus));    RtlInitUnicodeString(&unsRootPath, (PCWSTR)wRootPath);
3. 文件关闭的实现:  分两步: 发送主功能号为IRP_MJ_CLEANUP的IRP向FSD表明: 文件对象句柄数已为0 => 发送主功能号为IRP_MJ_CLOSE的IRP向FSD表明: 文件对象引用计数已为0,由FSD负责销毁文件对象。 (1)文件关闭1:IRP_MJ_CLEANUP
//自己发送IRP请求关闭文件的第1步(IRP_MJ_CLEANUP),表示文件对象句柄数为0NTSTATUS IrpCleanupFile(    IN PDEVICE_OBJECT pDeviceObject,    IN PFILE_OBJECT pFileObject){
        PIRP pIrp;    PIO_STACK_LOCATION pIrpSp;    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;    KEVENT kEvent;    IO_STATUS_BLOCK userIosb;    //卷参数块检查    if (pFileObject->Vpb == NULL || pFileObject->Vpb->DeviceObject == NULL) {
            return STATUS_INVALID_PARAMETER;    }    //    // IRP_MJ_CLEANUP    //    //分配IRP    pIrp = IoAllocateIrp(pDeviceObject->StackSize,        FALSE);    if (pIrp == NULL) {
            return STATUS_INSUFFICIENT_RESOURCES;    }    //填写IRP    pIrp->MdlAddress = NULL;    pIrp->AssociatedIrp.SystemBuffer = NULL;    pIrp->UserBuffer = NULL;    pIrp->Flags = IRP_CLOSE_OPERATION | IRP_SYNCHRONOUS_API;    pIrp->RequestorMode = KernelMode;    pIrp->UserIosb = &userIosb;    KeInitializeEvent(&kEvent,        NotificationEvent,        FALSE);    pIrp->UserEvent = &kEvent;    pIrp->Tail.Overlay.Thread = PsGetCurrentThrea
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
发送IRP删除文件,可以使用以下步骤: 1. 打开文件对象:首先,需要打开要删除的文件对象。可以使用ZwCreateFile或NtOpenFile函数来打开文件。 2. 构建IRPIRP(I/O Request Packet)是用于向内核发送I/O请求的数据结构。可以使用IoBuildSynchronousFsdRequest函数构建IRP。 3. 设置IRP参数:设置IRP的参数,包括请求类型、文件对象、I/O栈位置、缓冲区等。 4. 发送IRP:使用IoCallDriver函数发送IRP到驱动程序。 5. 处理IRP响应:等待驱动程序处理IRP请求,并处理IRP响应。如果删除文件成功,则可以关闭文件对象并返回成功信息。 下面是一个示例代码,演示如何使用IRP删除文件: NTSTATUS DeleteFile(PUNICODE_STRING FileName) { NTSTATUS status; HANDLE fileHandle; OBJECT_ATTRIBUTES objAttr; IO_STATUS_BLOCK ioStatus; PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; PIRP irp; KEVENT event; InitializeObjectAttributes(&objAttr, FileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwCreateFile(&fileHandle, FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &objAttr, &ioStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0); if (!NT_SUCCESS(status)) { return status; } status = ObReferenceObjectByHandle(fileHandle, FILE_WRITE_ATTRIBUTES, *IoFileObjectType, KernelMode, (PVOID*)&fileObject, NULL); if (!NT_SUCCESS(status)) { ZwClose(fileHandle); return status; } deviceObject = IoGetRelatedDeviceObject(fileObject); irp = IoBuildSynchronousFsdRequest(IRP_MJ_SET_INFORMATION, deviceObject, NULL, 0, NULL, &event, &ioStatus); if (!irp) { ObDereferenceObject(fileObject); ZwClose(fileHandle); return STATUS_INSUFFICIENT_RESOURCES; } irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irp->IoStatus.Information = 0; PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; irpSp->Parameters.SetFile.FileInformationClass = FileDispositionInformation; irpSp->Parameters.SetFile.DeleteFile = TRUE; KeInitializeEvent(&event, NotificationEvent, FALSE); status = IoCallDriver(deviceObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } ObDereferenceObject(fileObject); ZwClose(fileHandle); return status; } 这个函数接受一个UNICODE_STRING类型的文件名作为参数,并返回NTSTATUS类型的状态码。它使用ZwCreateFile函数打开文件,然后使用IoBuildSynchronousFsdRequest函数构建一个IRP,用于删除文件。最后,它使用IoCallDriver函数发送IRP,等待处理响应,并返回状态码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值