64位内核开发第8讲,文件操作.以及删除文件.

文件操作,以及强删文件.

一丶文件操作

1.文件操作的几种方式

操作

创建文件/文件夹

读/写

拷贝

移动

删除

属性访问与设置

1.2 文件的表示

文件路径表示表格:

表示层

文件路径表示方法

Ring3

L"C:\HelloWorld.txt"

Ring0

L"\??\C:\HelloWorld.txt"

其中两个 ****是代表一个.代表的是转义字符.

内核层的两个??其实是符号链接.代表的是是 \device\harddiskvolume3

内核中的文件路径完整表达是: ** L"\device\harddiskvolume3\HelloWorld.txt**

Ring3跟Ring0的其它路径.如设备对象.(符号链接)

表示层

路径表示

Ring3设备名

L"\\.\xxx符号名,或者 \\?\xxx符号名

Ring0设备名称

L"\device\xxx

Ring0符号连接名

L"\dosDevices\xxx符号连接名 或者\??\xxx符号连接

二丶文件操作的常见内核API

方法名

作用

ZwCreateFile

创建文件或者文件夹

ZwWriteFile

写文件

ZwReadFile

读文件

ZwQueryInfomationFile

查询文件

ZwQueryFullAttributeFile

查询文件

ZwSetInfomationFile

设置文件信息,设置文件大小,设置文件访问日期.设置属性隐藏文件.重命名.删除.对应IRP = Irp_mj_Set_Information.

ZwClose

关闭文件句柄

ZwQueryDirectoryFile

枚举文件跟目录

如ZwCreateFile

NTSTATUS 
  ZwCreateFile(
    __out PHANDLE  FileHandle,            文件句柄
    __in ACCESS_MASK  DesiredAccess,      创建权限
    __in POBJECT_ATTRIBUTES  ObjectAttributes,文件路径.这里放文件了解那个
    __out PIO_STATUS_BLOCK  IoStatusBlock,
    __in_opt PLARGE_INTEGER  AllocationSize,
    __in ULONG  FileAttributes,
    __in ULONG  ShareAccess,             文件是创建还是打开
    __in ULONG  CreateDisposition,
    __in ULONG  CreateOptions,
    __in_opt PVOID  EaBuffer,
    __in ULONG  EaLength
    );
NTSTATUS 
  ZwReadFile(
    IN HANDLE  FileHandle,               文件句柄
    IN HANDLE  Event  OPTIONAL,          异步过程调用
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,异步过程
    IN PVOID  ApcContext  OPTIONAL,      异步过程调用
    OUT PIO_STATUS_BLOCK  IoStatusBlock, 读写的IO状态
    OUT PVOID  Buffer,                   读写的Buffer
    IN ULONG  Length,                    读写的长度
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL, 读写的偏移
    IN PULONG  Key  OPTIONAL
    );

查询文件类型

NTSTATUS 
  ZwQueryInformationFile(
    IN HANDLE  FileHandle,       文件句柄
    OUT PIO_STATUS_BLOCK  IoStatusBlock, IO状态
    OUT PVOID  FileInformation,  根据参数四.传出的一个结构体乐行
    IN ULONG  Length,                     查询文件类型的长度
    IN FILE_INFORMATION_CLASS  FileInformationClass  查询的文件的类型, 你查询的信息是个结构体.这里放什么上面就放这个信息结构体的大小.
    );

上面这个函数简单来说就是 你参数4传入一个枚举类型.表示你想查询什么类型信息. 然后查询的信息通过参数3. FileInformation传出. 你参数4传入的是什么枚举.他就会返回查询的结构体给参数三. 伪代码:

ZwQueryInfomationFile(hfile,&Iostatus,&FileInformatin,sizeof(FileInforMation),FileBaseicInfoMation

具体信息查询WDK帮助文档即可.

设置文件信息

NTSTATUS 
  ZwSetInformationFile(
    IN HANDLE  FileHandle,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN PVOID  FileInformation,
    IN ULONG  Length,
    IN FILE_INFORMATION_CLASS  FileInformationClass 文件的类型
    );

跟查询文件相反.只不过需要我们传入信息. 比如: 下面就是删除文件

FILE_DISPOSITION_INFORMATION FileInformation;
ZwSetInformationFile(hfile,&ioStatus,&FileInformation,sizeof(FileInformation),FileDispositionInformation);

三丶内核中三种定义结构体的方式

为什么说这个.因为在上面文件操作.如果你查询Wdk文档的话.会看到不同的结构体定义. 如:

typedef struct _FILE_RENAME_INFORMATION {
    BOOLEAN ReplaceIfExists;
    HANDLE RootDirectory;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;

更改名字的结构体. 可以看到第三个参数 跟第四个参数. 为什么这样定义. 这样定义代表这个结构体利用数组可以溢出的原理.设计的一个边长结构体. 他这个数组的大小根据第三个参数决定.

其余的两种就很简单了

struct stack
{
 int value
 char szBuffer[100]
}

这种类型.我们的szBuffer就是占你给定的大小.

指针类型

struct point
{
    int value
    char *pszBuffer
}

这种类型则是指针定义.pszBuffer指向一块地址.

四丶驱动创建文件的完整代码示例

4.1内核中创建一个文件

#include <ntddk.h>
#include <wdm.h>
#include <ntdef.h>
#include <ntstrsafe.h>

#define DEVICENAME L""
#define SYMBOLICLINKENAME L""


DRIVER_UNLOAD DriverUnload;  //函数声明
NTSTATUS  NtDeleteFile(const WCHAR *FileNmae);//删除文件的第一种方式.
NTSTATUS  NtCreateFile(UNICODE_STRING ustr);

NTSTATUS  NtCreateFile(UNICODE_STRING ustr)
{
    //创建文件

    /*
    #define InitializeObjectAttributes( p, n, a, r, s ) { \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES );          \
    (p)->RootDirectory = r;                             \
    (p)->Attributes = a;                                \
    (p)->ObjectName = n;                                \
    (p)->SecurityDescriptor = s;                        \
    (p)->SecurityQualityOfService = NULL;               \
    }
    */
    NTSTATUS NtStatus = 0;
    HANDLE hFile;
    IO_STATUS_BLOCK io_Status = { 0 };
    OBJECT_ATTRIBUTES ObjAttus = { 0 };
    InitializeObjectAttributes(&ObjAttus,   //初始化ObjAttus结构.
        &ustr,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL);

    NtStatus = ZwCreateFile(&hFile,
        GENERIC_WRITE,
        &ObjAttus,
        &io_Status,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        FILE_OPEN_IF,
        FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
        NULL,
        0);
    if (NT_SUCCESS(NtStatus))
    {
        //创建成功了
        ZwClose(hFile);
    }
    return NtStatus;
}
void DriverUnload(DRIVER_OBJECT  *DriverObject)
{
    UNICODE_STRING ustr;

    
    

    RtlUnicodeStringInit(&ustr,L"Driver UnLoad");
    DbgPrint("%wZ",&ustr);

}

NTSTATUS DriverEntry(PDRIVER_OBJECT PdriverObject, PUNICODE_STRING RegistryPath)
{
 
    //创建设备对象
    
    UNICODE_STRING uPrintString = { 0 };
    UNICODE_STRING uPathName = { 0 };
    NTSTATUS NtStatus;
    PdriverObject->DriverUnload = DriverUnload;
    RtlUnicodeStringInit(&uPrintString, L"启动驱动安装");
    DbgPrint("%wZ", &uPrintString);

    RtlUnicodeStringInit(&uPathName, L"\\??\\c:\\1.txt");//初始化字符串路径
    NtStatus = NtCreateFile(uPathName);
    if (NT_SUCCESS(NtStatus))
    {
        DbgPrint("创建文件成功");
    }
  return STATUS_UNSUCCESSFUL;
}

创建完毕截图:

下面只提供核心接口代码.直接添加到DLL DriverEntry中即可.

4.1.2 内核中创建文件目录

传参的uPathName = L"\\??\\c:\\IBinary\\"

NTSTATUS  IBinaryNtCreateDirectory(UNICODE_STRING uPathName)
{
    NTSTATUS ntStatus;
    HANDLE hFile;
    OBJECT_ATTRIBUTES objAttus = { 0 };
    IO_STATUS_BLOCK ioStatus = { 0 };
    //初始化文件属性结构体
    InitializeObjectAttributes(&objAttus,
                            &uPathName,
                            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                            NULL,
                            NULL);
    ntStatus = ZwCreateFile(&hFile,
        GENERIC_READ | GENERIC_WRITE,
        &objAttus,
        &ioStatus,
        NULL,
        FILE_ATTRIBUTE_DIRECTORY, //注意这个属性.我们设置创建文件
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
        FILE_OPEN_IF,
        FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, //表示创建的是目录,并且是同步执行
        NULL,
        0);
    if (NT_SUCCESS(ntStatus))

    {
        ZwClose(hFile);
    }
    return ntStatus;
}

4.1.3内核中写文件

原理: 使用ZwCreateFile打开文件.获取文件句柄.然后使用ZwWriteFile写文件即可. uPathName = "\\??\\C:\\1.txt"

NTSTATUS  IBinaryNtWriteFile(UNICODE_STRING uPathName)
{
    //首先打开文件,然后写入文件.
    OBJECT_ATTRIBUTES objAttri = { 0 };
    NTSTATUS ntStatus;
    HANDLE hFile;
    IO_STATUS_BLOCK ioStatus = { 0 };
    PVOID pWriteBuffer = NULL;
    

    
    
    KdBreakPoint();
    
    InitializeObjectAttributes(&objAttri,
        &uPathName,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        0);
    ntStatus = ZwCreateFile(&hFile,
        GENERIC_WRITE | GENERIC_WRITE,
        &objAttri,
        &ioStatus,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        FILE_OPEN,//注意此标志,打开文件文件不存在则失败.
        FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);
    if (!NT_SUCCESS(ntStatus))
    {
        return ntStatus;
    }
    //开始写文件

    pWriteBuffer = ExAllocatePoolWithTag(PagedPool, 0x20, "niBI");

    if (pWriteBuffer == NULL)
    {
        DbgPrint("写文件分配内存出错");
        ZwClose(hFile);
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    RtlZeroMemory(pWriteBuffer, 0x20);
    RtlCopyMemory(pWriteBuffer, L"HelloIBinary", wcslen(L"HelloIBinary"));
    ntStatus = ZwWriteFile(hFile,
        NULL,
        NULL,
        NULL,
        &ioStatus,
        pWriteBuffer,
        0x20,
        NULL,
        NULL);
    if (!NT_SUCCESS(ntStatus))
    {
        ZwClose(hFile);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    ZwClose(hFile);
    ExFreePoolWithTag(pWriteBuffer, "niBI");
    return ntStatus;
}

在拷贝字符串的时候我拷贝的是宽字符.所以显示如上图.在我们读文件之前.我稍微修改一下.这里就不在贴出代码了.

4.1.4内核中读文件

内核中读写文件其实是一样的.打开一个文件.读取数据即可.

代码如下:

uPathName = L"\\??\\c:\\1.txt 传入了缓冲区.只需要往缓冲区中读取数据即可.

NTSTATUS  IBinaryNtReadFile(PVOID pszBuffer, UNICODE_STRING uPathName)
{

    OBJECT_ATTRIBUTES objAttri = { 0 };
    NTSTATUS ntStaus;
    HANDLE hFile;
    IO_STATUS_BLOCK ioStatus = { 0 };
    PVOID pReadBuffer = NULL;
    
    if (NULL == pszBuffer)
        return STATUS_INTEGER_DIVIDE_BY_ZERO;
    
        
    //打开文件读取文件.

    InitializeObjectAttributes(&objAttri,
        &uPathName,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        0);

    ntStaus = ZwCreateFile(&hFile,
        GENERIC_READ | GENERIC_WRITE,
        &objAttri,
        &ioStatus,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
        FILE_OPEN,
        FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        NULL);
    
    if (!NT_SUCCESS(ntStaus))
    {
        ZwClose(hFile);
        if (NULL != pReadBuffer)
            ExFreePoolWithTag(pReadBuffer, "niBI");
        return STATUS_INTEGER_DIVIDE_BY_ZERO;
    }


    //读取文件
    pReadBuffer = ExAllocatePoolWithTag(PagedPool, 100, "niBI");
    if (NULL == pReadBuffer)
        return STATUS_INTEGER_DIVIDE_BY_ZERO;
    ntStaus = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatus, pReadBuffer, 100, NULL, NULL);
    if (!NT_SUCCESS(ntStaus))
    {
        ZwClose(hFile);
        if (NULL != pReadBuffer)
            ExFreePoolWithTag(pReadBuffer, "niBI");
        return STATUS_INTEGER_DIVIDE_BY_ZERO;
    }
    //将读取的内容拷贝到传入的缓冲区.
    RtlCopyMemory(pszBuffer, pReadBuffer, 100);


    ZwClose(hFile);
    if (NULL != pReadBuffer)
        ExFreePoolWithTag(pReadBuffer, "niBI");

    return ntStaus;
}

4.1.4内核中删除文件的两种方式

内核中可以删除文件.有两种方式.第一种调用 ZwDeleteFile.你需要包含一个 <ntifs.h>头文件. 但是我包含之后出错.就没再深究.自己声明了一下.

4.1.4.1 内核中删除文件第一种方式

uDeletePathName = L"\\??\\c:\\1.txt"

#include <ntddk.h>
#include <wdm.h>
#include <ntdef.h>
#include <ntstrsafe.h>
NTSTATUS ZwDeleteFile( IN POBJECT_ATTRIBUTES  ObjectAttributes); //函数声明


NTSTATUS  IBinaryNtZwDeleteFile(UNICODE_STRING uDeletePathName)
{

    
    OBJECT_ATTRIBUTES obAttri = { 0 };
    

    //初始化源文件路径并且打开

    InitializeObjectAttributes(&obAttri,
        &uDeletePathName,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL
        );

    return ZwDeleteFile(&obAttri);
}

这种方式删除文件.但是可能删除失败.比如文件被独占打开等等.我没有进行尝试.在虚拟机中我就算 打开 1.txt这个文件.当我要删除这个文件的时候一样删除成功.

4.1.4.2 内核中第二种删除文件方式

这种删除方式更加厉害. 比如上面我们说的文件可能因为各种因素删除失败.所以采用这种方法. 这种方法是使用 内核中的 ZwSetInformationFile设置文件信息从而进行删除的. 代码如下:

NTSTATUS  IBinaryNtSetInformationFileDeleteFile(UNICODE_STRING uDeletePathName)
{
    //删除文件的第二种方式

    /*
    思路:
    1.初始化文件路径
    2.使用读写方式打开文件. 以共享模式打开.
    3.如果是拒绝,则以另一种方式打开文件.并且设置这个文件的信息.
    4.设置成功之后就可以删除了.
    */

    OBJECT_ATTRIBUTES objAttri;
    NTSTATUS ntStatus;
    HANDLE hFile;
    IO_STATUS_BLOCK ioStatus;
    FILE_DISPOSITION_INFORMATION IBdelPostion = { 0 }; //通过ZwSetInformationFile删除.需要这个结构体
    __try
    {

        InitializeObjectAttributes(&objAttri,
            &uDeletePathName,
            OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
            NULL,
            NULL
            );

        ntStatus = ZwCreateFile(&hFile,
            DELETE | FILE_WRITE_DATA | SYNCHRONIZE, //注意权限,以删除权限.写权限.
            &objAttri,
            &ioStatus,
            NULL,
            FILE_ATTRIBUTE_NORMAL,                //文件的属性是默认
            FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,//文件的共享模式 删除 读写
            FILE_OPEN,  //文件的打开方式是 打开.如果不存在则返回失败.
            FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE, //文件的应用选项,如果是FILE_DELETE_ON_CLOSE则使用ZwClose关闭文件句柄的时候删除这个文件
            NULL,
            0
            );
        if (!NT_SUCCESS(ntStatus))
        {
            //如果不成功,判断文件是否拒绝访问.是的话我们就设置为可以访问.并且进行删除.
            if (STATUS_ACCESS_DENIED == ntStatus)
            {
                ntStatus = ZwCreateFile(&hFile,
                    SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,//删除权限失败就以读写模式
                    &objAttri,
                    &ioStatus,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,                                  //文件的属性为默认
                    FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ,//文件的共享属性为 读写删除
                    FILE_OPEN,                                            //文件的打开方式为 打开,不存在则失败
                    FILE_SYNCHRONOUS_IO_NONALERT,                         //文件的应用选项.
                    NULL,
                    0
                    );
                //如果打开成功了.则设置这个文件的信息
                if (NT_SUCCESS(ntStatus))
                {
                    FILE_BASIC_INFORMATION  IBFileBasic = { 0 };//
                    /*
                    使用ZwQueryInformationfile遍历文件的信息.这里遍历的是文件的基本信息
                    */
                    ntStatus = ZwQueryInformationFile(
                        hFile,
                        &ioStatus,
                        &IBFileBasic,
                        sizeof(IBFileBasic),
                        FileBasicInformation
                        );
                    //遍历失败.输出打印信息
                    if (!NT_SUCCESS(ntStatus))
                        DbgPrint("删除文件失败,遍历文件信息出错 文件名= %wZ", &uDeletePathName);

                    //设置文件的基本信息
                    IBFileBasic.FileAttributes = FILE_ATTRIBUTE_NORMAL; //设置属性为默认属性

                    ntStatus = ZwSetInformationFile(
                        hFile,
                        &ioStatus,
                        &IBFileBasic,
                        sizeof(IBFileBasic),
                        FileBasicInformation); //将我的FileBasic基本属性设置到这个文件中

                    if (!NT_SUCCESS(ntStatus))
                        DbgPrint("删除文件失败,设置文件信息出错");
                    //如果成功关闭文件句柄.
                    ZwClose(hFile);

                    //重新打开这个设置信息后的文件.

                    ntStatus = ZwCreateFile(&hFile,
                        SYNCHRONIZE | FILE_WRITE_DATA | DELETE,
                        &objAttri,
                        &ioStatus,
                        NULL,
                        FILE_ATTRIBUTE_NORMAL,
                        FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
                        FILE_OPEN,
                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE,
                        NULL,
                        0);
                }
                if (!NT_SUCCESS(ntStatus))
                    DbgPrint("打开文件失败,删除失败");
            }
        }

        //进行强制删除文件 通过 ZwSetInformationFile

        IBdelPostion.DeleteFile = TRUE; //此标志设置为TRUE即可删除
        ntStatus = ZwSetInformationFile(hFile, &ioStatus, &IBdelPostion, sizeof(IBdelPostion), FileDispositionInformation);
        if (!NT_SUCCESS(ntStatus))
        {
            ZwClose(hFile);
            DbgPrint("删除文件失败,设置文件信息出错");
            return ntStatus;
        }
        ZwClose(hFile);

    }
    __except (1)
    {
        DbgPrint("删除文件出现异常");
    }
    
    return ntStatus;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券