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

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

一丶文件操作

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指向一块地址.

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

3.1.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中即可.

3.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;
}

3.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;
}

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

3.2 内核中读文件

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

代码如下:

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;
}

3.3 内核中删除文件的两种方式(常规)

内核中可以删除文件.有两种方式.第一种调用 ZwDeleteFile.

3.3.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这个文件.当我要删除这个文件的时候一样删除成功.

3.3.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;
}

3.3.3 强删文件的实现,包括驱动加载后强制删除驱动。

链接参考另一篇:x64内核强删文件. - iBinary - 博客园

四丶 文件属性的设置与查询

4.1 ZwSetInformationFile 使用

ZwSetInformationFile 可以设置文件属性。
其中主要是第五个参数,需要你指定信息。 根据第五个参数指定的信息 来决定你第三个参数所需要的结构体是那种
基本的文件属性示例如下:

  FILE_POSITION_INFORMATION poffset;    //设置文件指针 相当于是c语言的fseek()
    poffset.CurrentByteOffset.QuadPart = 100;

    ntStatus = ZwSetInformationFile(hFile, &iosa, &poffset, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
    if (!NT_SUCCESS(ntStatus))
    {
        goto RELEASE;
    }

    FILE_BASIC_INFORMATION fbi = {0};       //设置文件基础信息。设置为只读。
    fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
    ntStatus = ZwSetInformationFile(hFile, &iosa, &fbi, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation);
    if (!NT_SUCCESS(ntStatus))
    {
        goto RELEASE;
    }

五丶FileObject中的路径转换与操作

通过Zw函数打开或者创建文件之后会返回句柄,有了句柄则可以通过 ObReferenceObjectByHandle来获取文件对象指针了
也就是获取FILE_OBJECT 通过FILE_OBJECT可以互相转换路径。

首先介绍一下路径的概念。 路径分为DOS路径与NT路径。
DOS路径: Dos路径则是我们常常看到的

C:\xxxx\xxxx路径

NT路径: Nt 路径则是我们系统中的常见的路径 例如:

L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\test.dat"

与之等价的则是

"\Device\HarddiskVolume4\Users\ibinary\Desktop\11\test.dat"

介绍两个函数

  • ObQueryNameString
    通过Object查找名字
    使用方式如下:
获取FileObject 
 ntStatus = ObReferenceObjectByHandle(hFile,
                                         GENERIC_READ | GENERIC_WRITE,
                                         *IoFileObjectType,
                                         KernelMode,
                                         (PVOID *)&pFileObject,
                                         NULL);

    if (!NT_SUCCESS(ntStatus))
    {
        goto RELEASE;
    }

 POBJECT_NAME_INFORMATION pObjBuffer = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, 1024, 'abcd');
    if (pObjBuffer == NULL)
    {
        goto RELEASE;
    }
    RtlZeroMemory(pObjBuffer, 1024);
    ntStatus = ObQueryNameString(pFileObject, pObjBuffer, 1024, &uRetLength);
    if (!NT_SUCCESS(ntStatus))
    {
        goto RELEASE;
    }
查询出来 pObjBuffer 值为:
"\Device\HarddiskVolume4\Users\ibinary\Desktop\11\test.dat"

ObQueryNameString 可以进行两次调用。第一次调用 Buffer以及Buffer大小都传递为NULL。 然后他会在uRetLength给返回你需要的大小
然后再次调用的时候你在根据uRetLength申请空间即可。

  • NT路径转Dos路径
RtlZeroMemory(pObjBuffer, 1024);
    // IoVolumeDeviceToDosName()
    IoQueryFileDosDeviceName(pFileObject, &pObjBuffer);
pObjBuffer = "C:\Users\ibinary\Desktop\11\test.dat" 

六丶文件常用API封装

6.1 封装CopyFile

windows内核并没有为我们提供CopyFIle的功能.或者MoveFile的功能.所以需要我们自己去实现. 原理也很简单.使用 ZwReadFile和ZwWriteFile 来实现

代码如下:

// copy srcFile to DestFile
NTSTATUS ZwCopyFileW(PWCHAR pDestFileName, PWCHAR pSrcFileName)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrSrcFileName = {0};
    UNICODE_STRING ustrDestFileName = {0};
    HANDLE hSrcFileHandle = NULL;
    HANDLE hDstFileHandle = NULL;
    OBJECT_ATTRIBUTES objSrcAttr = {0};
    OBJECT_ATTRIBUTES objDstAttr = {0};
    IO_STATUS_BLOCK iosta = {0};
    LARGE_INTEGER ullOffset = {0};
    PCHAR pTempBuffer = NULL;
    const ULONG BUFFER_SIZE = 0x1000;
    ULONG uReadLength = 0;
    if (pDestFileName == NULL || pSrcFileName == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        goto END;
    }
    // open src and dest get handle
    status = RtlUnicodeStringInit(&ustrSrcFileName, pSrcFileName);
    if (NT_ERROR(status))
    {
        goto END;
    }
    status = RtlUnicodeStringInit(&ustrDestFileName, pDestFileName);
    if (NT_ERROR(status))
    {
        goto END;
    }
    InitializeObjectAttributes(&objSrcAttr, &ustrSrcFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    InitializeObjectAttributes(&objDstAttr, &ustrDestFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    // open src
    status = ZwCreateFile(
        &hSrcFileHandle,
        GENERIC_READ,
        &objSrcAttr,
        &iosta,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ,
        FILE_OPEN,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);
    if (NT_ERROR(status))
    {
        goto END;
    }
    // open dest
    status = ZwCreateFile(
        &hDstFileHandle,
        GENERIC_WRITE,
        &objDstAttr,
        &iosta,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ,
        FILE_OPEN_IF,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);
    if (NT_ERROR(status))
    {
        goto END;
    }

    // while readFile  and write buffer to dstFile
    pTempBuffer = (PCHAR)ExAllocatePool(NonPagedPool, BUFFER_SIZE);

    while (TRUE)
    {

        if (pTempBuffer == NULL)
        {
            goto END;
        }
        RtlZeroMemory(pTempBuffer, BUFFER_SIZE);
        status = ZwReadFile(
            hSrcFileHandle,
            NULL,
            NULL,
            NULL,
            &iosta,
            pTempBuffer,
            BUFFER_SIZE,
            &ullOffset, // out filepointer offset
            NULL);
        if (!NT_SUCCESS(status))
        {
            if (status == STATUS_END_OF_FILE)
            {
                status = STATUS_SUCCESS;
                goto END;
            }
            else
            {
                goto END;
            }
        }
        uReadLength = iosta.Information;
        status = ZwWriteFile(
            hDstFileHandle,
            NULL,
            NULL,
            NULL,
            &iosta,
            pTempBuffer,
            uReadLength,
            &ullOffset,
            NULL);
        if (NT_ERROR(status))
        {
            goto END;
        }
        // mov filepointer
        ullOffset.QuadPart += uReadLength;
    }

    status = STATUS_SUCCESS;

END:
    if (pTempBuffer != NULL)
    {
        ExFreePool(pTempBuffer);
        pTempBuffer = NULL;
    }
    if (hDstFileHandle != NULL)
    {
        ZwClose(hDstFileHandle);
        hDstFileHandle = NULL;
    }
    if (hSrcFileHandle != NULL)
    {
        ZwClose(hSrcFileHandle);
        hSrcFileHandle = NULL;
    }
    return status;
}

调用例子:

PWCHAR pSrcFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\1.txt";
PWCHAR pDestFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\2.txt";
ZwCopyFileW(pDestFileName, pSrcFileName);

6.2 封装GetFileSize

获取文件大小我们也可以封装下 核心原理就是使用 ZwQueryInformationFile

FILE_STANDARD_INFORMATION 调用号来实现的.

代码如下:

BOOLEAN GetFileSizeEx(
    IN HANDLE hFile,
    OUT PLARGE_INTEGER lpFileSize)
{
    BOOLEAN bRet = FALSE;
    FILE_STANDARD_INFORMATION fsi = {0};
    IO_STATUS_BLOCK iosta = {0};
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    if (hFile == NULL)
    {
        goto END;
    }
    if (lpFileSize == NULL)
    {
        goto END;
    }
    status = ZwQueryInformationFile(
        hFile,
        &iosta,
        &fsi,
        sizeof(FILE_STANDARD_INFORMATION),
        FileStandardInformation);
    if (NT_ERROR(status))
    {
        bRet = FALSE;
        goto END;
    }
    lpFileSize->QuadPart = fsi.EndOfFile.QuadPart;
    bRet = TRUE;
END:
    return bRet;
}
NTSTATUS GetFileSizeExample(PWCHAR fileName)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    HANDLE hFile = NULL;
    UNICODE_STRING uStrFileName = {0};
    OBJECT_ATTRIBUTES objStrFileNameAttri = {0};
    IO_STATUS_BLOCK iosta = {0};
    LARGE_INTEGER laFileSize = {0};
    status = RtlUnicodeStringInit(&uStrFileName, fileName);
    if (NT_ERROR(status))
    {
        goto END;
    }
    InitializeObjectAttributes(&objStrFileNameAttri, &uStrFileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
    status = ZwCreateFile(
        &hFile,
        GENERIC_READ,
        &objStrFileNameAttri,
        &iosta,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ,
        FILE_OPEN,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);
    if (NT_ERROR(status))
    {
        goto END;
    }
    GetFileSizeEx(hFile, &laFileSize);
END:
    if (hFile != NULL)
    {
        ZwClose(hFile);
        hFile = NULL;
    }
    return status;
}

调用例子:

PWCHAR pSrcFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\1.txt";
GetFileSizeExample(pSrcFileName);

6.3 封装创建目录以及隐藏目录

创建目录以及创建隐藏目录 本质还是调用ZwCreateFile 只不过创建的属性分别为无属性以及隐藏属性. 创建标志标志属性指明为 创建的是DIR即可.

6.3.1 创建无属性目录

NTSTATUS ZwCreateDirectoryW(PWCHAR pDirName)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrDirName = {0};
    OBJECT_ATTRIBUTES objDirAttri = {0};
    IO_STATUS_BLOCK iosta = {0};
    HANDLE hFile = NULL;
    if (pDirName == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        goto END;
    }
    status = RtlUnicodeStringInit(&ustrDirName, pDirName);
    if (NT_ERROR(status))
    {
        goto END;
    }
    InitializeObjectAttributes(&objDirAttri, &ustrDirName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);

    status = ZwCreateFile(
        &hFile,
        GENERIC_READ,
        &objDirAttri,
        &iosta,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        0,
        FILE_CREATE,
        FILE_DIRECTORY_FILE,
        NULL,
        0);
    if (NT_ERROR(status))
    {
        goto END;
    }
END:
    if (hFile != NULL)
    {
        ZwClose(hFile);
        hFile = NULL;
    }
    return status;
}

6.3.2 创建隐藏目录

NTSTATUS ZwCreateHideDirectoryW(PWCHAR pDirName)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrDirName = {0};
    OBJECT_ATTRIBUTES objDirAttri = {0};
    IO_STATUS_BLOCK iosta = {0};
    HANDLE hFile = NULL;
    if (pDirName == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        goto END;
    }
    status = RtlUnicodeStringInit(&ustrDirName, pDirName);
    if (NT_ERROR(status))
    {
        goto END;
    }
    InitializeObjectAttributes(&objDirAttri, &ustrDirName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);

    status = ZwCreateFile(
        &hFile,
        GENERIC_READ,
        &objDirAttri,
        &iosta,
        NULL,
        FILE_ATTRIBUTE_HIDDEN, // Hide
        0,
        FILE_CREATE,
        FILE_DIRECTORY_FILE,
        NULL,
        0);
    if (NT_ERROR(status))
    {
        goto END;
    }
END:
    if (hFile != NULL)
    {
        ZwClose(hFile);
        hFile = NULL;
    }
    return status;
}

6.3.3 调用示例

extern "C" NTSTATUS DriverEntry(
    PDRIVER_OBJECT pDriverObj,
    PUNICODE_STRING pReg)
{
    UNREFERENCED_PARAMETER(pReg);
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    pDriverObj->DriverUnload = DriverUnLoad;
    KdBreakPoint();

    PWCHAR pSrcFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\1.txt";
    PWCHAR pDestFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\2.txt";
    PWCHAR pCreateDirName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\test";
    PWCHAR pCreateDirName1 = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\test1";
    ZwCopyFileW(pDestFileName, pSrcFileName);
    GetFileSizeExample(pSrcFileName);
    ZwCreateDirectoryW(pCreateDirName);
    ZwCreateHideDirectoryW(pCreateDirName1);
    return STATUS_SUCCESS;
}

6.4 删除文件与文件夹的封装

可以删除文件也可以删除隐藏文件夹(或者普通文件夹)
本质还是ZwDeleteFile的封装 删除文件有两种方式,如果遇到普通文件夹删除不掉可以尝试修改文件(夹)属性为无属性
然后继续尝试删除.
代码如下:

NTSTATUS ZwRemoveDirectoryW(PWCHAR pName)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrName = {0};
    OBJECT_ATTRIBUTES objName = {0};
    if (pName == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        goto END;
    }
    status = RtlUnicodeStringInit(&ustrName, pName);
    if (NT_ERROR(status))
    {
        goto END;
    }
    InitializeObjectAttributes(&objName, &ustrName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);

    status = ZwDeleteFile(&objName);
END:
    return status;
}

调用:

extern "C" NTSTATUS DriverEntry(
    PDRIVER_OBJECT pDriverObj,
    PUNICODE_STRING pReg)
{
    UNREFERENCED_PARAMETER(pReg);
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    pDriverObj->DriverUnload = DriverUnLoad;
    KdBreakPoint();

    PWCHAR pSrcFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\1.txt";
    PWCHAR pDestFileName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\2.txt";
    PWCHAR pCreateDirName = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\test";
    PWCHAR pCreateDirName1 = L"\\??\\C:\\Users\\ibinary\\Desktop\\11\\test1";
    ZwCopyFileW(pDestFileName, pSrcFileName);
    GetFileSizeExample(pSrcFileName);
    ZwCreateDirectoryW(pCreateDirName);
    ZwCreateHideDirectoryW(pCreateDirName1);
    ZwRemoveDirectoryW(pCreateDirName);
    ZwRemoveDirectoryW(pCreateDirName1);
    ZwRemoveDirectoryW(pDestFileName);
    return STATUS_SUCCESS;
}
posted @ 2019-06-08 14:47  iBinary  阅读(3329)  评论(1编辑  收藏  举报