前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >64位内核开发第8讲,文件操作.以及删除文件.

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

作者头像
IBinary
发布2019-06-14 14:25:23
1.1K0
发布2019-06-14 14:25:23
举报
文章被收录于专栏:逆向技术逆向技术

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

一丶文件操作

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

代码语言:javascript
复制
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
    );
代码语言:javascript
复制
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
    );

查询文件类型

代码语言:javascript
复制
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传入的是什么枚举.他就会返回查询的结构体给参数三. 伪代码:

代码语言:javascript
复制
ZwQueryInfomationFile(hfile,&Iostatus,&FileInformatin,sizeof(FileInforMation),FileBaseicInfoMation

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

设置文件信息

代码语言:javascript
复制
NTSTATUS 
  ZwSetInformationFile(
    IN HANDLE  FileHandle,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN PVOID  FileInformation,
    IN ULONG  Length,
    IN FILE_INFORMATION_CLASS  FileInformationClass 文件的类型
    );

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

代码语言:javascript
复制
FILE_DISPOSITION_INFORMATION FileInformation;
ZwSetInformationFile(hfile,&ioStatus,&FileInformation,sizeof(FileInformation),FileDispositionInformation);

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

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

代码语言:javascript
复制
typedef struct _FILE_RENAME_INFORMATION {
    BOOLEAN ReplaceIfExists;
    HANDLE RootDirectory;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;

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

其余的两种就很简单了

代码语言:javascript
复制
struct stack
{
 int value
 char szBuffer[100]
}

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

指针类型

代码语言:javascript
复制
struct point
{
    int value
    char *pszBuffer
}

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

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

4.1内核中创建一个文件
代码语言:javascript
复制
#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\\"

代码语言:javascript
复制
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"

代码语言:javascript
复制
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 传入了缓冲区.只需要往缓冲区中读取数据即可.

代码语言:javascript
复制
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"

代码语言:javascript
复制
#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设置文件信息从而进行删除的. 代码如下:

代码语言:javascript
复制
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;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文件操作,以及强删文件.
    • 一丶文件操作
      • 1.文件操作的几种方式
      • 1.2 文件的表示
    • 二丶文件操作的常见内核API
      • 三丶内核中三种定义结构体的方式
      • 四丶驱动创建文件的完整代码示例
      • 4.1.4内核中读文件
      • 4.1.4内核中删除文件的两种方式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档