8.1 Windows驱动开发:内核文件读写系列函数

在应用层下的文件操作只需要调用微软应用层下的API函数及C库标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头即可在内核中使用,例如本章要讲解的文件与目录操作相关函数,多数ARK反内核工具都具有对文件的管理功能,实现对文件或目录的基本操作功能也是非常有必要的。

首先无论在内核态还是在用户态,我们调用的文件操作函数其最终都会转换为一个IRP请求,并发送到文件系统驱动上的IRP_MJ_READ派遣函数里面,这个读写流程大体上可分为如下四步;

  • 对于FAT32分区会默认分发到FASTFAT.SYS,而相对于NTFS分区则会分发到NTFS.SYS驱动上。
  • 文件系统驱动经过处理后,就把IRP传给磁盘类驱动的IRP_MJ_READ分发函数处理,当磁盘类驱动处理完毕后,又把IRP传给磁盘小端口驱动。
  • 在磁盘小端口驱动里,无论是读还是写,用的都是IRP_MJ_SCSI这个分发函数。
  • IRP被磁盘小端口驱动处理完之后,就要依靠HAL.DLL进行端口IO,此时数据就真的从硬盘里读取了出来。

8.1.1 创建文件或目录

实现创建文件或目录,创建文件或目录都可调用ZwCreateFile()这个内核函数来实现,唯一不同的区别在于当用户传入参数中包含有FILE_SYNCHRONOUS_IO_NONALERT属性时则会默认创建文件,而如果包含有FILE_DIRECTORY_FILE属性则默认为创建目录,该函数的微软定义以及备注信息如下所示;

NTSYSAPI NTSTATUS ZwCreateFile([out]          PHANDLE            FileHandle,        // 指向HANDLE变量的指针,该变量接收文件的句柄。[in]           ACCESS_MASK        DesiredAccess,     // 指定一个ACCESS_MASK值,该值确定对对象的请求访问权限。[in]           POBJECT_ATTRIBUTES ObjectAttributes,  // 指向OBJECT_ATTRIBUTES结构的指针,该结构指定对象名称和其他属性。[out]          PIO_STATUS_BLOCK   IoStatusBlock,     // 指向IO_STATUS_BLOCK结构的指针,该结构接收最终完成状态和有关所请求操作的其他信息。 [in, optional] PLARGE_INTEGER     AllocationSize,    // 指向LARGE_INTEGER的指针,其中包含创建或覆盖的文件的初始分配大小(以字节为单位)。[in]           ULONG              FileAttributes,    // 指定一个或多个FILE_ATTRIBUTE_XXX标志,这些标志表示在创建或覆盖文件时要设置的文件属性。[in]           ULONG              ShareAccess,       // 共享访问的类型,指定为零或以下标志的任意组合。[in]           ULONG              CreateDisposition, // 指定在文件存在或不存在时要执行的操作。[in]           ULONG              CreateOptions,     // 指定要在驱动程序创建或打开文件时应用的选项。[in, optional] PVOID              EaBuffer,          // 对于设备和中间驱动程序,此参数必须是NULL指针。[in]           ULONG              EaLength           // 对于设备和中间驱动程序,此参数必须为零。
);

参数DesiredAccess用于指明对象访问权限的,常用的权限有FILE_READ_DATA读取文件,FILE_WRITE_DATA写入文件,FILE_APPEND_DATA追加文件,FILE_READ_ATTRIBUTES读取文件属性,以及FILE_WRITE_ATTRIBUTES写入文件属性。

参数ObjectAttributes指向了一个OBJECT_ATTRIBUTES指针,通常会通过InitializeObjectAttributes()宏对其进行初始化,当一个例程打开对象时由此结构体指定目标对象的属性。

参数ShareAccess用于指定访问属性,通常属性有FILE_SHARE_READ读取,FILE_SHARE_WRITE写入,FILE_SHARE_DELETE删除。

参数CreateDisposition用于指定在文件存在或不存在时要执行的操作,一般而言我们会指定为FILE_OPEN_IF打开文件,或FILE_OVERWRITE_IF打开文件并覆盖,FILE_SUPERSEDE替换文件。

参数CreateOptions用于指定创建文件或目录,一般FILE_SYNCHRONOUS_IO_NONALERT代表创建文件,参数FILE_DIRECTORY_FILE代表创建目录。

相对于创建文件而言删除文件或目录只需要调用ZwDeleteFile()系列函数即可,此类函数只需要传递一个OBJECT_ATTRIBUTES参数即可,其微软定义如下所示;

NTSYSAPI NTSTATUS ZwDeleteFile([in] POBJECT_ATTRIBUTES ObjectAttributes
);

接下来我们就封装三个函数MyCreateFile()用于创建文件,MyCreateFileFolder()用于创建目录,MyDeleteFileOrFileFolder()用于删除空目录。

#include <ntifs.h>
#include <ntstrsafe.h>// 创建文件
BOOLEAN MyCreateFile(UNICODE_STRING ustrFilePath)
{HANDLE hFile = NULL;OBJECT_ATTRIBUTES objectAttributes = { 0 };IO_STATUS_BLOCK iosb = { 0 };NTSTATUS status = STATUS_SUCCESS;// 初始化对象属性结构体 FILE_SYNCHRONOUS_IO_NONALERTInitializeObjectAttributes(&objectAttributes, &ustrFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 创建文件status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return FALSE;}// 关闭句柄ZwClose(hFile);return TRUE;
}// 创建目录
BOOLEAN MyCreateFileFolder(UNICODE_STRING ustrFileFolderPath)
{HANDLE hFile = NULL;OBJECT_ATTRIBUTES objectAttributes = { 0 };IO_STATUS_BLOCK iosb = { 0 };NTSTATUS status = STATUS_SUCCESS;// 初始化对象属性结构体InitializeObjectAttributes(&objectAttributes, &ustrFileFolderPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 创建目录 FILE_DIRECTORY_FILEstatus = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_DIRECTORY_FILE, NULL, 0);if (!NT_SUCCESS(status)){return FALSE;}// 关闭句柄ZwClose(hFile);return TRUE;
}// 删除文件或是空目录
BOOLEAN MyDeleteFileOrFileFolder(UNICODE_STRING ustrFileName)
{NTSTATUS status = STATUS_SUCCESS;OBJECT_ATTRIBUTES objectAttributes = { 0 };// 初始化属性InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 执行删除操作status = ZwDeleteFile(&objectAttributes);if (!NT_SUCCESS(status)){return FALSE;}return TRUE;
}VOID UnDriver(PDRIVER_OBJECT driver)
{BOOLEAN ref = FALSE;// 删除文件UNICODE_STRING ustrDeleteFile;RtlInitUnicodeString(&ustrDeleteFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt");ref = MyDeleteFileOrFileFolder(ustrDeleteFile);if (ref != FALSE){DbgPrint("[LyShark] 删除文件成功 \n");}else{DbgPrint("[LyShark] 删除文件失败 \n");}// 删除空目录UNICODE_STRING ustrDeleteFilder;RtlInitUnicodeString(&ustrDeleteFilder, L"\\??\\C:\\LySharkFolder");ref = MyDeleteFileOrFileFolder(ustrDeleteFilder);if (ref != FALSE){DbgPrint("[LyShark] 删除空目录成功 \n");}else{DbgPrint("[LyShark] 删除空目录失败 \n");}DbgPrint("驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");BOOLEAN ref = FALSE;// 创建目录UNICODE_STRING ustrDirectory;RtlInitUnicodeString(&ustrDirectory, L"\\??\\C:\\LySharkFolder");ref = MyCreateFileFolder(ustrDirectory);if (ref != FALSE){DbgPrint("[LyShark] 创建目录成功 \n");}else{DbgPrint("[LyShark] 创建文件失败 \n");}// 创建文件UNICODE_STRING ustrCreateFile;RtlInitUnicodeString(&ustrCreateFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt");ref = MyCreateFile(ustrCreateFile);if (ref != FALSE){DbgPrint("[LyShark] 创建文件成功 \n");}else{DbgPrint("[LyShark] 创建文件失败 \n");}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

运行如上代码,分别创建LySharkFolder目录,并在其中创建lyshark.txt最终再将其删除,输出效果如下;

8.1.2 重命名文件或目录

在内核中重命名文件或目录核心功能的实现依赖于ZwSetInformationFile()这个内核函数,该函数可用于更改有关文件对象的各种信息,其微软官方定义如下;

NTSYSAPI NTSTATUS ZwSetInformationFile([in]  HANDLE                 FileHandle,          // 文件句柄[out] PIO_STATUS_BLOCK       IoStatusBlock,       // 指向 IO_STATUS_BLOCK 结构的指针[in]  PVOID                  FileInformation,     // 指向缓冲区的指针,该缓冲区包含要为文件设置的信息。[in]  ULONG                  Length,              // 缓冲区的大小(以字节为单位)[in]  FILE_INFORMATION_CLASS FileInformationClass // 为文件设置的类型
);

这其中最重要的参数就是FileInformationClass根据该参数的不同则对文件的操作方式也就不同,如果需要重命名文件则此处应使用FileRenameInformation而如果需要修改文件的当前信息则应使用FilePositionInformation创建链接文件则使用FileLinkInformation即可,以重命名为例,首先我们需要定义一个FILE_RENAME_INFORMATION结构并按照要求填充,最后直接使用ZwSetInformationFile()并传入相关信息后即可完成修改,其完整代码流程如下;

#include <ntifs.h>
#include <ntstrsafe.h>// 重命名文件或文件夹
BOOLEAN MyRename(UNICODE_STRING ustrSrcFileName, UNICODE_STRING ustrDestFileName)
{HANDLE hFile = NULL;OBJECT_ATTRIBUTES objectAttributes = { 0 };IO_STATUS_BLOCK iosb = { 0 };NTSTATUS status = STATUS_SUCCESS;PFILE_RENAME_INFORMATION pRenameInfo = NULL;ULONG ulLength = (1024 + sizeof(FILE_RENAME_INFORMATION));// 为PFILE_RENAME_INFORMATION结构申请内存pRenameInfo = (PFILE_RENAME_INFORMATION)ExAllocatePool(NonPagedPool, ulLength);if (NULL == pRenameInfo){return FALSE;}// 设置重命名信息RtlZeroMemory(pRenameInfo, ulLength);// 设置文件名长度以及文件名pRenameInfo->FileNameLength = ustrDestFileName.Length;wcscpy(pRenameInfo->FileName, ustrDestFileName.Buffer);pRenameInfo->ReplaceIfExists = 0;pRenameInfo->RootDirectory = NULL;// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrSrcFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, SYNCHRONIZE | DELETE, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING, NULL, 0);if (!NT_SUCCESS(status)){ExFreePool(pRenameInfo);return FALSE;}// 利用ZwSetInformationFile来设置文件信息status = ZwSetInformationFile(hFile, &iosb, pRenameInfo, ulLength, FileRenameInformation);if (!NT_SUCCESS(status)){ZwClose(hFile);ExFreePool(pRenameInfo);return FALSE;}// 释放内存,关闭句柄ExFreePool(pRenameInfo);ZwClose(hFile);return TRUE;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");// 重命名文件UNICODE_STRING ustrOldFile, ustrNewFile;RtlInitUnicodeString(&ustrOldFile, L"\\??\\C:\\MyCreateFolder\\lyshark.txt");RtlInitUnicodeString(&ustrNewFile, L"\\??\\C:\\MyCreateFolder\\hello_lyshark.txt");BOOLEAN ref = MyRename(ustrOldFile, ustrNewFile);if (ref == TRUE){DbgPrint("已完成改名 \n");}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

运行后将会把C:\\MyCreateFolder\\lyshark.txt目录下的文件改名为hello_lyshark.txt,前提是该目录与该文件必须存在;

那么如果你需要将文件设置为只读模式或修改文件的创建日期,那么你就需要看一下微软的定义FILE_BASIC_INFORMATION结构,依次填充此结构体并调用ZwSetInformationFile()即可实现修改,该结构的定义如下所示;

typedef struct _FILE_BASIC_INFORMATION {LARGE_INTEGER CreationTime;LARGE_INTEGER LastAccessTime;LARGE_INTEGER LastWriteTime;LARGE_INTEGER ChangeTime;ULONG FileAttributes;
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;

当然如果你要修改日期你还需要自行填充LARGE_INTEGER结构,该结构的微软定义如下所示,分为高位和低位依次填充即可;

#if defined(MIDL_PASS)
typedef struct _LARGE_INTEGER {
#else // MIDL_PASS
typedef union _LARGE_INTEGER {struct {ULONG LowPart;LONG HighPart;} DUMMYSTRUCTNAME;struct {ULONG LowPart;LONG HighPart;} u;
#endif //MIDL_PASSLONGLONG QuadPart;
} LARGE_INTEGER;

我们就以修改文件属性为只读模式为例,其核心代码可以被描述为如下样子,相比于改名而言其唯一的变化就是更换了PFILE_BASIC_INFORMATION结构体,其他的基本一致;

HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;PFILE_BASIC_INFORMATION pReplaceInfo = NULL;ULONG ulLength = (1024 + sizeof(FILE_BASIC_INFORMATION));// 为FILE_POSITION_INFORMATION结构申请内存
pReplaceInfo = (PFILE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ulLength);
if (NULL == pReplaceInfo)
{return FALSE;
}RtlZeroMemory(pReplaceInfo, ulLength);// 设置文件基础信息,将文件设置为只读模式
pReplaceInfo->FileAttributes |= FILE_ATTRIBUTE_READONLY;

8.1.3 读取文件大小

读取特定文件的所占空间,核心原理是调用了ZwQueryInformationFile()这个内核函数,该函数可以返回有关文件对象的各种信息,参数传递上与ZwSetInformationFile()很相似,其FileInformationClass都需要传入一个文件类型结构,该函数的完整定义如下;

NTSYSAPI NTSTATUS ZwQueryInformationFile([in]  HANDLE                 FileHandle,          // 文件句柄。[out] PIO_STATUS_BLOCK       IoStatusBlock,       // 指向接收最终完成状态和操作相关信息的 IO_STATUS_BLOCK 结构的指针。[out] PVOID                  FileInformation,     // 指向调用方分配的缓冲区的指针,例程将请求的有关文件对象的信息写入其中。[in]  ULONG                  Length,              // 长度。[in]  FILE_INFORMATION_CLASS FileInformationClass // 指定要在 FileInformation 指向的缓冲区中返回的有关文件的信息类型。
);

本例中我们需要读入文件的所占字节数,那么FileInformation字段就需要传入FileStandardInformation来获取文件的基本信息,获取到的信息会被存储到FILE_STANDARD_INFORMATION结构内,用户只需要解析该结构体fsi.EndOfFile.QuadPart即可得到文件长度,其完整代码如下所示;

#include <ntifs.h>
#include <ntstrsafe.h>// 获取文件大小
ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
{HANDLE hFile = NULL;OBJECT_ATTRIBUTES objectAttributes = { 0 };IO_STATUS_BLOCK iosb = { 0 };NTSTATUS status = STATUS_SUCCESS;FILE_STANDARD_INFORMATION fsi = { 0 };// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return 0;}// 获取文件大小信息status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);if (!NT_SUCCESS(status)){ZwClose(hFile);return 0;}return fsi.EndOfFile.QuadPart;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");// 获取文件大小UNICODE_STRING ustrFileSize;RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\lyshark.exe");ULONG64 ullFileSize = MyGetFileSize(ustrFileSize);DbgPrint("获取文件大小: %I64d Bytes \n", ullFileSize);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

编译并运行如上程序,即可读取到C盘下的lyshark.exe程序的大小字节数,如下图所示;

8.1.4 内核文件读写

内核读取文件可以使用ZwReadFile(),内核写入文件则可使用ZwWriteFile(),这两个函数的参数传递基本上一致,如下是读写两个函数的对比参数。

NTSYSAPI NTSTATUS ZwReadFile([in]           HANDLE           FileHandle,      // 文件对象的句柄。[in, optional] HANDLE           Event,           // (可选)事件对象的句柄,在读取操作完成后设置为信号状态。[in, optional] PIO_APC_ROUTINE  ApcRoutine,      // 此参数为保留参数。[in, optional] PVOID            ApcContext,      // 此参数为保留参数。[out]          PIO_STATUS_BLOCK IoStatusBlock,   // 接收实际从文件读取的字节数。[out]          PVOID            Buffer,          // 指向调用方分配的缓冲区的指针,该缓冲区接收从文件读取的数据。[in]           ULONG            Length,          // 缓冲区指向的缓冲区的大小(以字节为单位)。[in, optional] PLARGE_INTEGER   ByteOffset,      // 指定将开始读取操作的文件中的起始字节偏移量。[in, optional] PULONG           Key
);NTSYSAPI NTSTATUS ZwWriteFile([in]           HANDLE           FileHandle,[in, optional] HANDLE           Event,[in, optional] PIO_APC_ROUTINE  ApcRoutine,[in, optional] PVOID            ApcContext,[out]          PIO_STATUS_BLOCK IoStatusBlock,[in]           PVOID            Buffer,[in]           ULONG            Length,[in, optional] PLARGE_INTEGER   ByteOffset,[in, optional] PULONG           Key
);

读取文件的代码如下所示,分配非分页pBuffer内存,然后调用MyReadFile()函数,将数据读入到pBuffer并输出,完整代码如下所示;

#include <ntifs.h>
#include <ntstrsafe.h>// 读取文件数据
BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
{HANDLE hFile = NULL;IO_STATUS_BLOCK iosb = { 0 };OBJECT_ATTRIBUTES objectAttributes = { 0 };NTSTATUS status = STATUS_SUCCESS;// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL,FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return FALSE;}// 初始化RtlZeroMemory(&iosb, sizeof(iosb));// 读入文件status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);if (!NT_SUCCESS(status)){*pulReadDataSize = iosb.Information;ZwClose(hFile);return FALSE;}// 获取实际读取的数据*pulReadDataSize = iosb.Information;// 关闭句柄ZwClose(hFile);return TRUE;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");UNICODE_STRING ustrScrFile;ULONG ulBufferSize = 40960;LARGE_INTEGER liOffset = { 0 };// 初始化需要读取的文件名RtlInitUnicodeString(&ustrScrFile, L"\\??\\C:\\lyshark.exe");// 分配非分页内存PUCHAR pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);// 读取文件MyReadFile(ustrScrFile, liOffset, pBuffer, &ulBufferSize);// 输出文件前16个字节for (size_t i = 0; i < 16; i++){DbgPrint("%02X \n", pBuffer[i]);}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

编译并运行这段代码,并循环输出lyshark.exe文件的头16个字节的数据,效果图如下所示;

文件写入MyWriteFile()与读取类似,如下通过运用文件读写实现了文件拷贝功能,实现完整代码如下所示;

#include <ntifs.h>
#include <ntstrsafe.h>// 读取文件数据
BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
{HANDLE hFile = NULL;IO_STATUS_BLOCK iosb = { 0 };OBJECT_ATTRIBUTES objectAttributes = { 0 };NTSTATUS status = STATUS_SUCCESS;// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL,FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return FALSE;}// 初始化RtlZeroMemory(&iosb, sizeof(iosb));// 读入文件status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);if (!NT_SUCCESS(status)){*pulReadDataSize = iosb.Information;ZwClose(hFile);return FALSE;}// 获取实际读取的数据*pulReadDataSize = iosb.Information;// 关闭句柄ZwClose(hFile);return TRUE;
}// 向文件写入数据
BOOLEAN MyWriteFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pWriteData, PULONG pulWriteDataSize)
{HANDLE hFile = NULL;IO_STATUS_BLOCK iosb = { 0 };OBJECT_ATTRIBUTES objectAttributes = { 0 };NTSTATUS status = STATUS_SUCCESS;// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, GENERIC_WRITE, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return FALSE;}// 初始化RtlZeroMemory(&iosb, sizeof(iosb));// 写出文件status = ZwWriteFile(hFile, NULL, NULL, NULL, &iosb, pWriteData, *pulWriteDataSize, &liOffset, NULL);if (!NT_SUCCESS(status)){*pulWriteDataSize = iosb.Information;ZwClose(hFile);return FALSE;}// 获取实际写入的数据*pulWriteDataSize = iosb.Information;// 关闭句柄ZwClose(hFile);return TRUE;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");// 文件读写UNICODE_STRING ustrScrFile, ustrDestFile;RtlInitUnicodeString(&ustrScrFile, L"\\??\\C:\\lyshark.exe");RtlInitUnicodeString(&ustrDestFile, L"\\??\\C:\\LyShark\\new_lyshark.exe");ULONG ulBufferSize = 40960;ULONG ulReadDataSize = ulBufferSize;LARGE_INTEGER liOffset = { 0 };// 分配非分页内存PUCHAR pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);do{// 读取文件ulReadDataSize = ulBufferSize;MyReadFile(ustrScrFile, liOffset, pBuffer, &ulReadDataSize);// 数据为空则读取结束if (0 >= ulReadDataSize){break;}// 写入文件MyWriteFile(ustrDestFile, liOffset, pBuffer, &ulReadDataSize);// 更新偏移liOffset.QuadPart = liOffset.QuadPart + ulReadDataSize;DbgPrint("[+] 更新偏移: %d \n", liOffset.QuadPart);} while (TRUE);// 释放内存ExFreePool(pBuffer);DbgPrint("[*] 已将文件复制到新目录 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

编译并运行这段程序,则自动将C:\\lyshark.exe盘符下的文件拷贝到C:\\LyShark\\new_lyshark.exe目录下,实现效果图如下所示;

8.1.5 实现文件读写传递

通过如上学习相信你已经掌握了如何使用文件读写系列函数了,接下来将封装一个文件读写驱动,应用层接收,驱动层读取;

此驱动部分完整代码如下所示;

#include <ntifs.h>
#include <windef.h>#define READ_FILE_SIZE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define READ_FILE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)#define DEVICENAME L"\\Device\\ReadWriteDevice"
#define SYMBOLNAME L"\\??\\ReadWriteSymbolName"typedef struct
{ULONG64 size;      // 读写长度BYTE* data;        // 读写数据集
}FileData;// 获取文件大小
ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
{HANDLE hFile = NULL;OBJECT_ATTRIBUTES objectAttributes = { 0 };IO_STATUS_BLOCK iosb = { 0 };NTSTATUS status = STATUS_SUCCESS;FILE_STANDARD_INFORMATION fsi = { 0 };// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return 0;}// 获取文件大小信息status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);if (!NT_SUCCESS(status)){ZwClose(hFile);return 0;}return fsi.EndOfFile.QuadPart;
}// 读取文件数据
BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
{HANDLE hFile = NULL;IO_STATUS_BLOCK iosb = { 0 };OBJECT_ATTRIBUTES objectAttributes = { 0 };NTSTATUS status = STATUS_SUCCESS;// 初始化结构InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);// 打开文件status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)){return FALSE;}// 初始化RtlZeroMemory(&iosb, sizeof(iosb));// 读入文件status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);if (!NT_SUCCESS(status)){*pulReadDataSize = iosb.Information;ZwClose(hFile);return FALSE;}// 获取实际读取的数据*pulReadDataSize = iosb.Information;// 关闭句柄ZwClose(hFile);return TRUE;
}NTSTATUS DriverIrpCtl(PDEVICE_OBJECT device, PIRP pirp)
{PIO_STACK_LOCATION stack;stack = IoGetCurrentIrpStackLocation(pirp);FileData* FileDataPtr;switch (stack->MajorFunction){case IRP_MJ_CREATE:{break;}case IRP_MJ_CLOSE:{break;}case IRP_MJ_DEVICE_CONTROL:{// 获取应用层传值FileDataPtr = pirp->AssociatedIrp.SystemBuffer;switch (stack->Parameters.DeviceIoControl.IoControlCode){// 读取内存函数case READ_FILE_SIZE_CODE:{LARGE_INTEGER liOffset = { 0 };UNICODE_STRING ustrFileSize;RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\Windows\\system32\\ntoskrnl.exe");// 获取文件长度ULONG64 ulBufferSize = MyGetFileSize(ustrFileSize);DbgPrint("获取文件大小: %I64d Bytes \n", ulBufferSize);// 将长度返回应用层FileDataPtr->size = ulBufferSize;break;}// 读取文件case READ_FILE_CODE:{FileData ptr;LARGE_INTEGER liOffset = { 0 };UNICODE_STRING ustrFileSize;RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\Windows\\system32\\ntoskrnl.exe");// 获取文件长度ULONG64 ulBufferSize = MyGetFileSize(ustrFileSize);DbgPrint("获取文件大小: %I64d Bytes \n", ulBufferSize);// 读取内存到缓冲区BYTE* pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);MyReadFile(ustrFileSize, liOffset, pBuffer, &ulBufferSize);// 返回数据FileDataPtr->size = ulBufferSize;RtlCopyMemory(FileDataPtr->data, pBuffer, FileDataPtr->size);break;}}pirp->IoStatus.Information = sizeof(FileDataPtr);break;}}pirp->IoStatus.Status = STATUS_SUCCESS;IoCompleteRequest(pirp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}VOID UnDriver(PDRIVER_OBJECT driver)
{if (driver->DeviceObject){UNICODE_STRING SymbolName;RtlInitUnicodeString(&SymbolName, SYMBOLNAME);// 删除符号链接IoDeleteSymbolicLink(&SymbolName);IoDeleteDevice(driver->DeviceObject);}
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{NTSTATUS status = STATUS_SUCCESS;PDEVICE_OBJECT device = NULL;UNICODE_STRING DeviceName;DbgPrint("[LyShark] hello lyshark \n");// 初始化设备名RtlInitUnicodeString(&DeviceName, DEVICENAME);// 创建设备status = IoCreateDevice(Driver, sizeof(Driver->DriverExtension), &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);if (status == STATUS_SUCCESS){UNICODE_STRING SymbolName;RtlInitUnicodeString(&SymbolName, SYMBOLNAME);// 创建符号链接status = IoCreateSymbolicLink(&SymbolName, &DeviceName);// 失败则删除设备if (status != STATUS_SUCCESS){IoDeleteDevice(device);}}// 派遣函数初始化Driver->MajorFunction[IRP_MJ_CREATE] = DriverIrpCtl;Driver->MajorFunction[IRP_MJ_CLOSE] = DriverIrpCtl;Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIrpCtl;// 卸载驱动Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

客户端完整代码如下所示;

#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>#define READ_FILE_SIZE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define READ_FILE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)typedef struct
{DWORD size;      // 读写长度BYTE* data;      // 读写数据集
}FileData;int main(int argc, char* argv[])
{// 连接到驱动HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);FileData data;DWORD dwSize = 0;// 首先得到文件长度DeviceIoControl(handle, READ_FILE_SIZE_CODE, 0, 0, &data, sizeof(data), &dwSize, NULL);printf("%d \n", data.size);// 读取机器码到BYTE字节数组data.data = new BYTE[data.size];DeviceIoControl(handle, READ_FILE_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);for (int i = 0; i < data.size; i++){printf("0x%02X ", data.data[i]);}printf("\n");getchar();CloseHandle(handle);return 0;
}

通过驱动加载工具将WinDDK.sys拉起来,然后启动客户端进程,即可输出ntoskrnl.exe的文件数据,如下图所示;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/153731.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

本地/笔记本/纯 cpu 部署、使用类 gpt 大模型

文章目录 1. 安装 web UI1.1. 下载代码库1.2. 创建 conda 环境1.3. 安装 pytorch1.4. 安装 pip 库 2. 下载大模型3. 使用 web UI3.1. 运行 UI 界面3.2. 加载模型3.3. 进行对话 使用 web UI 大模型文件&#xff0c;即可在笔记本上部署、使用类 gpt 大模型。 1. 安装 web UI 1…

DCDC同步降压控制器SCT82A30\SCT82630

SCT82A30是一款100V电压模式控制同步降压控制器&#xff0c;具有线路前馈。40ns受控高压侧MOSFET的最小导通时间支持高转换比&#xff0c;实现从48V输入到低压轨的直接降压转换&#xff0c;降低了系统复杂性和解决方案成本。如果需要&#xff0c;在低至6V的输入电压下降期间&am…

基于可变形卷积和注意力机制的带钢表面缺陷快速检测网络DCAM-Net(论文阅读笔记)

原论文链接->DCAM-Net: A Rapid Detection Network for Strip Steel Surface Defects Based on Deformable Convolution and Attention Mechanism | IEEE Journals & Magazine | IEEE Xplore DCAM-Net: A Rapid Detection Network for Strip Steel Surface Defects Base…

uniapp - 开关按钮

目录 1.运行代码如下&#xff1a; 2.运行效果 3.switch属性 1.运行代码如下&#xff1a; <template><view class"switchBox"><switch change"switchChange" color"#F21177" :checked"form.checked" /></view&…

科大讯飞 vue.js 语音听写流式实现 全网首发

组件下载 还是最近的需求&#xff0c;页面表单输入元素过多&#xff0c;需要实现语音识别来由用户通过朗读的方式向表单中填写数据&#xff0c;尽量快的、高效的完成表单数据采集及输入。 国内科大讯飞在语音识别方面的建树还是有目共睹&#xff0c;于是还是选择了科大讯飞的平…

【AI实用技巧】GPT写sql统计语句

编写sql的统计语句是一项复杂的任务&#xff0c;特别是涉及多表的情况下。但有了GPT的帮助&#xff0c;一切变得轻松愉快。 AI7号 - 最强人工智能&#xff08;GPT&#xff09;中文版https://ai7.pro/s/9v2um 举例说明 有表结构如下&#xff1a; users(user_id, name) bills(…

改进YOLOv8:结合ConvNeXt V2骨干网络!使用MAE共同设计和扩展ConvNet

🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv8:我的专业笔记与技术总结   -- YOLOv8轻松上手, 适用技术小白,文章代码齐全,仅需 …

函数调用分析

目录 函数相关的汇编指令 JMP指令 call指令 ret指令 VS2019正向分析main函数 总结调用函数堆栈变化规律 x64dbg分析调用函数 IDA分析调用函数 函数相关的汇编指令 JMP指令 JMP 指令表示的是需要跳转到哪个内存地址&#xff0c;相当于是间接修改了 EIP 。 call指令 ca…

Windows环境VSCode配置OpenCV-项目配置(二)

修改c_cpp_properties.json {"configurations": [{"name": "windows-gcc-x64","includePath": ["${workspaceFolder}/**","D:/mingw64/mingw64/include","D:/openCV_win/build/install/include","…

系列十二、线程池

一、线程池 1.1、为什么需要线程池 10年前单核CPU电脑&#xff0c;假的多线程&#xff0c;像马戏团小丑玩多个球&#xff0c;CPU需要来回切换。现在是多核电脑&#xff0c;多个线程各自跑在独立的CPU上&#xff0c;不用切换效率高。 1.2、优势 线程池做的主要工作是控制运行的…

天猫超市电商营销系统:无代码开发实现API连接集成

无代码开发实现天猫超市与电商系统的高效连接 天猫超市&#xff0c;作为天猫推出的网络零售超市&#xff0c;为广大网购消费者提供了一站式的购物服务。而通过无代码开发的方式&#xff0c;天猫超市能够实现与各种电商系统的连接和集成&#xff0c;这种方式无需进行繁琐的API开…

ESP32 http 请求

目录 参考教程1.使用的http连接2.使用Vscode-IDF创建http_request例程3.修改http_request_example_main.c函数4.已经获取到响应的数据 参考教程 ESP-IDF HTTP获取网络时间 1.使用的http连接 http://api.m.taobao.com/rest/api3.do?apimtop.common.getTimestamp请求可以得到…

【Python测试开发】:切换窗口和表单

一、多窗口切换 浏览器打开的窗口其实会有一个叫做句柄的概念。 句柄就类似于每一个标签页的ID一样&#xff0c;具有唯一性。 1.1 语法 获取当前窗口句柄&#xff0c;注意后面没有括号哦~ driver.current_window_handle获取所有窗口句柄&#xff0c;结果以列表格式存储&am…

ModBus TCP/RTU 报文解析

Modbus Tcp https://gitee.com/szwzhsz/Modbus-TCP-client-server-DotNetty.?_fromgitee_search 固定协议格式 事务标识(2byte)&#xff1a;00 00&#xff0c;可变(递增) 协议标识(2byte)&#xff1a;00 00&#xff0c;固定 长度(2byte)&#xff1a;00 06&#xff0c;可变 单…

PySide6 Tutorials (一)表格小部件魔改

前言 Pyside6官方教程给了一个使用表格显示颜色的教程&#xff0c;原教程地址如下&#xff1a;源地址&#xff0c; 结合前面button信号的学习&#xff0c;就魔改添加了如下功能&#xff1a;增加一列按钮&#xff0c;可以修改该行的颜色值&#xff0c;通过点击按钮生成指定的颜…

深度学习之生成唐诗案例(Pytorch版)

主要思路&#xff1a; 对于唐诗生成来说&#xff0c;我们定义一个"S" 和 "E"作为开始和结束。 示例的唐诗大概有40000多首&#xff0c; 首先数据预处理&#xff0c;将唐诗加载到内存&#xff0c;生成对应的word2idx、idx2word、以及唐诗按顺序的字序列。…

万字解析设计模式之代理模式

一、代理模式 1.1概述 代理模式是一种结构型设计模式&#xff0c;它允许通过创建代理对象来控制对其他对象的访问。这种模式可以增加一些额外的逻辑来控制对原始对象的访问&#xff0c;同时还可以提供更加灵活的访问方式。 代理模式分为静态代理和动态代理两种。静态代理是在编…

Day01 嵌入式 -----流水灯

一、简单介绍 嵌入式系统中的流水灯是一种常见的示例项目&#xff0c;通常用于演示嵌入式系统的基本功能和控制能力。流水灯由多个发光二极管&#xff08;LED&#xff09;组成&#xff0c;这些LED按照一定的顺序依次点亮和熄灭&#xff0c;形成一种像水流一样的流动效果。 二、…

单/三相dq解耦控制与特定次谐波抑制

1. 单相整流器dq坐标系下建模 单相整流器的拓扑如图所示&#xff0c;可知 u a b u s − L d i s d t − R i s {u_{ab}} {u_{s}} - L\frac{{d{i_s}}}{{dt}} - R{i_s} uab​us​−Ldtdis​​−Ris​。   将电压和电流写成dq的形式。 { u s U s m sin ⁡ ( ω t ) i s I …