tiechui_lesson05_内核小文件拷贝

本文详细介绍了在Windows内核中进行文件操作的一系列API,包括ZwOpenFile用于打开文件,ZwCreateFile用于创建文件,ZwQueryInformationFile用于查询文件属性,以及ZwReadFile和ZwWriteFile分别用于读写文件。文章还提供了代码示例,展示了如何使用这些API进行文件的打开、创建、属性查询、读取和写入,并演示了实现文件拷贝的过程。
摘要由CSDN通过智能技术生成

主要学习在内核中的文件操作,包括文件的打开,创建,读取,写入,查询文件属性等。

涉及的API和宏函数

  • ZwOpenFile
  • ZwCreateFile
  • ZwQueryInformationFile
  • ZwReadFile
  • ZwWriteFile
  • ZwClose
  • InitializeObjectAttributes

1.文件的打开

ZwOpenFile

/************************************************************************
* 函数名称:ZwOpenFile
* 功能描述:打开文件
* 参数列表:
        FileHandle:返回打开的文件句柄
        DesiredAccess:打开的权限,一般设为GENERIC_ALL
        ObjectAttributes:objectAttributes结构
        IoStatusBlock:指向一个结构体的指针。该结构体指明打开文件的状态
        ShareAccess:共享的权限。可以是FILE_SHARE_READ或者FILE_SHARE_WRITE
        OpenOptions:打开选项,一般设为FILE_SYNCHRONOUS_IO_NONALERT
* 返回 值:指明文件是否被成功打开
*************************************************************************/
NTSTATUS ZwOpenFile(
    OUT PHANDLE FileHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    IN ULONG ShareAccess,
    IN ULONG OpenOptions);

 使用示例:

OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iostatus;
HANDLE hfile;
UNICODE_STRING logFileUnicodeString;

//初始化UNICODE_STRING字符串
RtlInitUnicodeString(&logFileUnicodeString, L"\\??\\C:\\1.log");
//或者写成 \\Device\\HarddiskVolume1\\1.log

//初始化objectAttributes
InitializeObjectAttributes(
    &objectAttributes,
    &logFileUnicodeString,
    OBJ_CASE_INSENSITIVE,
    NULL,
    NULL);

//创建文件
NTSTATUS ntStatus = ZwOpenFile(&hfile,
                               GENERIC_ALL,
                               &objectAttributes,
                               &iostatus,
                               FILE_SHARE_READ || FILE_SHARE_WRITE,
                               FILE_SYNCHRONOUS_IO_NONALERT);
if( NT_SUCCESS(ntStatus) )
{
    KdPrint(("Create FILE successfully!\n"));
}
else
{
    KdPrint(("Create FILE unsuccessfully!\n"));
}

//文件操作
//..........

//关闭文件句柄
ZwClose(hfile);

2.文件的创建

ZwCreateFile

/************************************************************************
* 函数名称:ZwCreateFile
* 功能描述:文件的创建
* 参数列表:
        FileHandle:返回打开文件的句柄
        DesiredAccess:对打开文件操作的描述,读,写或是其他。一般指定为GENERIC_READ 或 GENERIC_WRITE
        ObjectAttributes:是OBJECT_ATTRIBUTES结构的地址,该结构包含要打开的文件名
        IoStatusBlock:指向一个IO_STATUS_BLOCK结构,该结构接收ZwCreateFile操作的结果状态
        AllocationSize:是一个指针,指向一个64位整数,该数指定文件初始分配时的大小
                    该参数仅关系到创建或重写文件操作,如果忽略它,那么文件长度从0开始
                    病随着写入而增长
        FileAttributes:0或FILE_ATTRIBUTE_NORMAL,指定新创建文件的属性
        ShareAccess:FILE_SHARE_READ或0,指定文件的共享方式。
                    如果为写数据而打开文件,可能不希望其他线程访问该文件
        CreateDisposition:FILE_OPEN或FILE_OVERWRITE_IF,表明当指定文件存在或不存在时应如何处理
        CreateOptions:FILE_SYNCHARONOUS_IO_NONALERT,指定控制打开操作和句柄使用的附加标志位
        EaBuffer:一个指针,指向可选的扩展属性区
        EaLength:扩展属性区的长度
* 返回 值:    NTSTATUS
*************************************************************************/
NTSTATUS ZwCreateFile(
    OUT PHANDLE FileHandle, 
    IN ACCESS_MASK DesiredAccess, 
    IN POBJECT_ATTRIBUTES ObjectAttributes, 
    OUT PIO_STATUS_BLOCK IoStatusBlock, 
    IN PLARGE_INTEGER AllocationSize OPTIONAL, 
    IN ULONG FileAttributes, 
    IN ULONG ShareAccess, 
    IN ULONG CreateDisposition,
    IN ULONG CreateOptions, 
    IN PVOID EaBuffer OPTIONAL,
    IN ULONG EaLength);

/*
*备注:
* 1、CreateDisposition参数。
*     如果想打开文件,CreateDisposition参数设置成FILE_OPEN. 
*     如果想创建文件,CreateDisposition参数设置成FILE_OVERWRITE_IF
*        此时,无论文件是否存在,都会创建新文件
* 2、文件名是通过第三个参数ObjectAttributes
*     这个参数是一个OBJECT_ATTRIBUTES结构体
*     通过InitializeObjectAttributes初始化
*/

InitializeObjectAttributes

这实际是一个宏,用来初始化属性变量的内容:

/************************************************************************
* 函数名称:InitializeObjectAttributes
* 功能描述:初始化OBJECT_ATTRIBUTES结构体
* 参数列表:
        InitializedAttributes:返回的OBJECT_ATTRIBUTES结构体
        ObjectName:对象名称,用UNICODE_STRING描述,这里设置的是文件名
        Attributes:一般设置为OBJ_CASE_INSENSITIVE,对大小写敏感
        RootDirectory:一般设置为NULL
        SecurityDescriptor:一般设置为NULL
* 返回 值:相等的字节数
        不一致返回零        
*************************************************************************/
VOID InitializeObjectAttributes(
    OUT POBJECT_ATTRIBUTES InitializedAttributes,
    IN PUNICODE_STRING ObjectName,
    IN ULONG Attributes,
    IN HANDLE RootDirectory,
    IN PSECURITY_DESCRIPTOR SecurityDescriptor);

/*
*备注:
* 1、文件名[必须]是符号链接或者是设备名
* 2、例如:盘符 "c:",就是一个符号链接
*        这里应该用 "\??\c:" 代替 
*        "c:\1.log" 要写成 "\??\c:\1.log"
* 3、其中 "\??\c:" 是符号链接,内核会将它转换成设备名 "\Device\HarddiskColume1"
*/

3.获取文件属性 

ZwQueryInformationFile

/************************************************************************
* 函数名称:ZwQueryInformationFile
* 功能描述:获取文件属性
* 参数列表:
        FileHandle:文件句柄
        IoStatusBlock:返回设置的状态
        FileInformation:依据FileInformationClass不同而不同。作为输出信息
        Length:FileInformation数据的长度
        FileInformationClass:描述修改属性的类型
* 返回 值:设置属性查询
*************************************************************************/
NTSTATUS ZwQueryInformationFile(
            IN HANDLE FileHandle,
            OUT PIO_STATUS_BLOCK IoStatusBlock,
            OUT PVOID FileInformation,
            IN ULONG Length,
            IN FILE_INFORMATION_CLASS FileInformationClass);

4.文件的读取

ZwReadFile

/************************************************************************
* 函数名称:ZwReadFile
* 功能描述:文件的读操作
* 参数列表:
        FileHandle:文件打开的句柄
        Event:很少用到,一般设为NULL
        ApcRoutine:很少用到,一般设为NULL
        ApcContext:很少用到,一般设为NULL
        IoStatusBlock:记录些操作的状态。其中,IoStatusBlock.Infomation记录实际写了多少字节
        Buffer:从这个缓冲区开始开始从文件里读
        Length:准备读多少字节
        Byteoffset:从文件的多少偏移地址开始读
        Key:很少用到,一般设为NULL
* 返回 值:
*************************************************************************/
NTSTATUS ZwReadFile(
    IN HANDLE FileHandle,
    IN HANDLE Event OPTIONAL,
    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
    IN PVOID ApcContext OPTIONAL,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    IN PVOID Buffer,
    IN ULONG Length,
    IN PLARGE_INTEGER Byteoffset OPTIONAL,
    IN PULONG Key OPTIONAL);

5.文件的写入

ZwWriteFile

/************************************************************************
* 函数名称:ZwWriteFile
* 功能描述:文件的写操作
* 参数列表:
        FileHandle:文件打开的句柄
        Event:很少用到,一般设为NULL
        ApcRoutine:很少用到,一般设为NULL
        ApcContext:很少用到,一般设为NULL
        IoStatusBlock:记录些操作的状态。其中,IoStatusBlock.Infomation记录实际写了多少字节
        Buffer:从这个缓冲区开始往文件里写
        Length:准备写多少字节
        Byteoffset:从文件的多少便宜地址开始写
        Key:很少用到,一般设为NULL
* 返回 值:
*************************************************************************/
NTSTATUS ZwWriteFile(
    IN HANDLE FileHandle,
    IN HANDLE Event OPTIONAL,
    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
    IN PVOID ApcContext OPTIONAL,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    IN PVOID Buffer,
    IN ULONG Length,
    IN PLARGE_INTEGER Byteoffset OPTIONAL,
    IN PULONG Key OPTIONAL);

综合使用:实现文件拷贝

仅支持小文件,而且没有和应用层交互

#include <ntifs.h>
#include <windef.h>	//使用DWORD等类型

#define DEVICE_NAME L"\\Device\\MyFirstDeviceFilerw"		//设备名称
#define SYM_NAME	L"\\??\\MyFirstDeviceFilerw"			//符号链接

//定义自定义控制码 (做一个减法)
#define IOCTL_MUL	CTL_CODE(FILE_DEVICE_UNKNOWN,0x855,METHOD_BUFFERED, FILE_ANY_ACCESS)

//分发函数
NTSTATUS MyCreate(PDEVICE_OBJECT pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device has be opened!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyClearUp(PDEVICE_OBJECT pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyClearUp!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyClose(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyClose!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyRead(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyRead!");

	//获取当前IRP堆栈信息
	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);

	//对应用户层的读请求
	ULONG readsize = pstack->Parameters.Read.Length;
	DbgPrint("用户请求读大小为%u\n", readsize);
	//对应用户层所分配的缓冲区的内存位置
	PCHAR readbuffer = pirp->AssociatedIrp.SystemBuffer;	

	// readbuffer的赋值实际上是对用户缓冲区的改变
	RtlCopyMemory(readbuffer,
		"This Message Come From Kernel.",
		strlen("This Message Come From Kernel."));

	pirp->IoStatus.Status = status;
	//对于Information的赋值是返回给用户程序实际读取的长度。
	pirp->IoStatus.Information = strlen("This Message Come From Kernel.");

	//输出下字符串的长度
	DbgPrint("Really Read Info Len is %lld\n", 
		strlen("This Message Come From Kernel."));
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyWrite(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyWrite!");

	//获取当前IRP堆栈信息
	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);

	//对应用户层的写请求
	ULONG writesize = pstack->Parameters.Write.Length;
	DbgPrint("用户请求写大小为%u\n", writesize);
	//对应用户层所分配的缓冲区的内存位置
	PCHAR writebuffer = pirp->AssociatedIrp.SystemBuffer;

	// 写入扩展设备之前先进行清0操作
	RtlZeroMemory(pdevice->DeviceExtension, 200);
	// writebuffer的数据写入到设备扩展里边
	RtlCopyMemory(pdevice->DeviceExtension,writebuffer,writesize);

	DbgPrint("写缓冲区内存地址:%p,设备扩展内容%s\n", writebuffer,
		(PCHAR)pdevice->DeviceExtension);

	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 13;

	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyControl(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyControl!");

	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);
	ULONG	iocode = pstack->Parameters.DeviceIoControl.IoControlCode;	// 获取控制码

	ULONG inlen = pstack->Parameters.DeviceIoControl.InputBufferLength;
	ULONG outlen = pstack->Parameters.DeviceIoControl.OutputBufferLength;
	ULONG ioinfo = 0;

	DbgPrint("InputBufferLength is %u\n", inlen);
	DbgPrint("OutputBufferLength is %u\n", outlen);

	switch (iocode)
	{
	case IOCTL_MUL:
	{
		//做一个减法

		DWORD indata = *(PDWORD)pirp->AssociatedIrp.SystemBuffer;
		DbgPrint("--Kernel Indata %d \n", indata);
		indata = indata * 5;
		*(PDWORD)pirp->AssociatedIrp.SystemBuffer = indata;
		ioinfo = 50;

		//别忘记break!
		break;
	}
	default:
		status = STATUS_UNSUCCESSFUL;
		ioinfo = 0;
		break;
	}

	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = ioinfo;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}


//内核文件拷贝
NTSTATUS KernelCopyFile(PWCHAR dstfile_path, PWCHAR sourcefile_path)
{
	NTSTATUS status = STATUS_SUCCESS;
	HANDLE hfile1 = NULL;
	UNICODE_STRING sourcefilepath = { 0 };
	OBJECT_ATTRIBUTES obja1 = { 0 };
	IO_STATUS_BLOCK iostack = { 0 };

	//初始化
	RtlInitUnicodeString(&sourcefilepath, sourcefile_path);
	InitializeObjectAttributes(&obja1, &sourcefilepath, 
		OBJ_CASE_INSENSITIVE |OBJ_KERNEL_HANDLE, NULL, NULL);
	
	//打开文件句柄
	status = ZwOpenFile(
		&hfile1, 
		GENERIC_ALL, 
		&obja1,
		&iostack, 
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		FILE_SYNCHRONOUS_IO_NONALERT
	);

	if (!NT_SUCCESS(status))
	{
		DbgPrint("ZwOpenFile 文件打开失败 错误码 %x \n",status);
		return status; 
	}

	//查询文件大小
	FILE_STANDARD_INFORMATION fsiFileInfo = { 0 };
	status = ZwQueryInformationFile(
		hfile1, 
		&iostack,
		&fsiFileInfo,
		sizeof(FILE_STANDARD_INFORMATION),
		FileStandardInformation
	);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("ZwQueryDirectoryFile 查询失败 错误码 %x \n", status);
		ZwClose(hfile1);
		return status;
	}

	//查询成功过后申请缓冲区放置查询内容
	PVOID filebuffer = NULL;
	filebuffer = ExAllocatePoolWithTag(NonPagedPool,
		fsiFileInfo.EndOfFile.QuadPart, '1212');
	if (!filebuffer)
	{
		DbgPrint("filebuffer 缓冲区申请失败 错误码 %x \n", status);
		ZwClose(hfile1);
		return status;
	}

	RtlZeroMemory(filebuffer, fsiFileInfo.EndOfFile.QuadPart);

	//读文件
	LARGE_INTEGER readoffset = { 0 };
	readoffset.QuadPart = 0;	//从0开始读
	status = ZwReadFile(
		hfile1, 
		NULL, 
		NULL,
		NULL,
		&iostack,
		filebuffer,
		(ULONG)fsiFileInfo.EndOfFile.QuadPart, 
		&readoffset,
		NULL
	);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("读文件失败 错误码 %x \n", status);
		ZwClose(hfile1);
		ExFreePoolWithTag(filebuffer, '1212');
		return status;
	}

	DbgPrint("Ioinfo--- %lld", iostack.Information);

	ZwClose(hfile1);

	//创建新文件
	HANDLE hfile2 = NULL;
	UNICODE_STRING dstfilepath = { 0 };
	OBJECT_ATTRIBUTES obja2 = { 0 };
	IO_STATUS_BLOCK iostack2 = { 0 };

	RtlInitUnicodeString(&dstfilepath, dstfile_path);
	InitializeObjectAttributes(&obja2, &dstfilepath,
		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);	//文件操作描述
	// 参数好多!
	status = ZwCreateFile(
		&hfile2, 
		GENERIC_ALL, 
		&obja2, 
		&iostack2,
		NULL, 
		FILE_ATTRIBUTE_NORMAL, 
		FILE_SHARE_WRITE,
		FILE_SUPERSEDE,
		FILE_SYNCHRONOUS_IO_NONALERT, 
		NULL, 
		0
	);
	
	if (!NT_SUCCESS(status))
	{
		DbgPrint("创建文件失败   错误码 %x \n", status);
		ExFreePoolWithTag(filebuffer, '1212');
		return status;
	}

	// 写入文件
	LARGE_INTEGER writeoffset = { 0 };
	writeoffset.QuadPart = 0;
	status = ZwWriteFile(
		hfile2, 
		NULL, 
		NULL, 
		NULL,
		&iostack2,
		filebuffer, 
		(ULONG)fsiFileInfo.EndOfFile.QuadPart, 
		&writeoffset,
		NULL
	);

	if (!NT_SUCCESS(status))
	{
		DbgPrint("写入失败 错误码 %x \n", status);
		ExFreePoolWithTag(filebuffer, '1212');
		ZwClose(hfile2);
		return status;
	}

	DbgPrint("write length = %lld ", iostack2.Information);

	ExFreePoolWithTag(filebuffer, '1212');
	ZwClose(hfile2);

	return status;
}

//卸载函数
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
	DbgPrint("basedriver 卸载驱动\n");
	if (DriverObject->DeviceObject)
	{
		IoDeleteDevice(DriverObject->DeviceObject);
	}
	UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYM_NAME);
	IoDeleteSymbolicLink(&symLink);
}

//入口函数
NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT DriverObject, 
	IN PUNICODE_STRING RegistryPath
)
{
	UNREFERENCED_PARAMETER(RegistryPath);
	//注册卸载函数
	DriverObject->DriverUnload = DriverUnload;

	NTSTATUS status = STATUS_SUCCESS;
	PDEVICE_OBJECT pdevice;		// 用来接收创建的设备对象
	UNICODE_STRING devicename = { 0 };
	RtlInitUnicodeString(&devicename, DEVICE_NAME);

	//创建设备对象
	status = IoCreateDevice(DriverObject, 
							200,	// 定义设备扩展的大小,用来存放写入的数据
							&devicename, 
							FILE_DEVICE_UNKNOWN, 
							0, 
							TRUE, 
							&pdevice);

	if (!NT_SUCCESS(status))
	{
		KdPrint(("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n",status));
		DbgPrint("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n", status);
	}

	if (pdevice->Flags)
	{
		//不要忘了设备对象的读写方式,否则会蓝屏
		pdevice->Flags |= DO_BUFFERED_IO;	// 缓冲区方式的读写
	}



	//
	//创建成功,创建符号链接
	//
	UNICODE_STRING symname = { 0 };
	RtlInitUnicodeString(&symname, SYM_NAME);
	status = IoCreateSymbolicLink(&symname, &devicename);	// 符号链接名 设备名
	if (!NT_SUCCESS(status))
	{
		KdPrint(("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status));
		DbgPrint("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status);
	}

	//设置分发例程
	DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
	DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MyClearUp;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
	DriverObject->MajorFunction[IRP_MJ_READ] = MyRead;
	DriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyControl;

	KernelCopyFile(L"\\??\\C:\\888.exe", L"\\??\\C:\\567.exe");

	return 0;
}

效果:

小结

这节课主要是使用内核API了做些处理,对于文件来说还是和应用层类似,注意文件句柄的创建和销毁,还有对文件操作的时的权限设置要符合需求。

另外还可以拓展,通过设定一个临时长度循环读写来拷贝大文件,还有和应用层进行应用交互文件地址等。看完系列课程有时间再做😉😉😉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值