主要学习在内核中的文件操作,包括文件的打开,创建,读取,写入,查询文件属性等。
涉及的API和宏函数
- ZwOpenFile
- ZwCreateFile
- ZwQueryInformationFile
- ZwReadFile
- ZwWriteFile
- ZwClose
- InitializeObjectAttributes
1.文件的打开
ZwOpenFile
/************************************************************************
* 函数名称:ZwOpenFile
* 功能描述:打开文件
* 参数列表:FileHandle:返回打开的文件句柄DesiredAccess:打开的权限,一般设为GENERIC_ALLObjectAttributes:objectAttributes结构IoStatusBlock:指向一个结构体的指针。该结构体指明打开文件的状态ShareAccess:共享的权限。可以是FILE_SHARE_READ或者FILE_SHARE_WRITEOpenOptions:打开选项,一般设为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_WRITEObjectAttributes:是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:一般设置为NULLSecurityDescriptor:一般设置为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:很少用到,一般设为NULLApcRoutine:很少用到,一般设为NULLApcContext:很少用到,一般设为NULLIoStatusBlock:记录些操作的状态。其中,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:很少用到,一般设为NULLApcRoutine:很少用到,一般设为NULLApcContext:很少用到,一般设为NULLIoStatusBlock:记录些操作的状态。其中,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了做些处理,对于文件来说还是和应用层类似,注意文件句柄的创建和销毁,还有对文件操作的时的权限设置要符合需求。
另外还可以拓展,通过设定一个临时长度循环读写来拷贝大文件,还有和应用层进行应用交互文件地址等。看完系列课程有时间再做😉😉😉