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

  • 相关阅读:
    jquery.js 一个非常不错的本脚库。
    AjaxPro 框架学习 (支持vs2003/vs2005)
    利用HttpResponse来导出excel文件.
    一个比较好的WEB时间控件
    CCR 编写一个不用创建线程,不用考虑资源互斥的多线程程序
    页面优化 .net 版
    [转]window.opener用法
    导出excel文件 解决科学计数法问题
    抽象类和抽象方法的特征和用途
    超强悍的右键菜单
  • 原文地址:https://www.cnblogs.com/iBinary/p/10990683.html
Copyright © 2020-2023  润新知