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

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

一丶文件操作

1.文件操作的几种方式

操作
创建文件/文件夹
读/写
拷贝
移动
删除
属性访问与设置

1.2 文件的表示

文件路径表示表格:

表示层 文件路径表示方法
Ring3 L"C:HelloWorld.txt"
Ring0 L"??C:HelloWorld.txt"

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

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

内核中的文件路径完整表达是: ** L"deviceharddiskvolume3HelloWorld.txt**

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

表示层 路径表示
Ring3设备名 L"\.xxx符号名,或者 \?xxx符号名
Ring0设备名称 L"devicexxx
Ring0符号连接名 L"dosDevicesxxx符号连接名 或者??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;
}

原文地址:https://www.cnblogs.com/iBinary/p/10990683.html