找回密码
 立即注册
首页 业界区 业界 驱动开发:内核文件读写系列函数

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

奄幂牛 2025-6-6 19:52:14
在应用层下的文件操作只需要调用微软应用层下的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,此时数据就真的从硬盘里读取了出来。
创建文件或目录: 实现创建文件或目录,创建文件或目录都可调用ZwCreateFile()这个内核函数来实现,唯一不同的区别在于当用户传入参数中包含有FILE_SYNCHRONOUS_IO_NONALERT属性时则会默认创建文件,而如果包含有FILE_DIRECTORY_FILE属性则默认为创建目录,该函数的微软定义以及备注信息如下所示;
  1. NTSYSAPI NTSTATUS ZwCreateFile(
  2.   [out]          PHANDLE            FileHandle,        // 指向HANDLE变量的指针,该变量接收文件的句柄。
  3.   [in]           ACCESS_MASK        DesiredAccess,     // 指定一个ACCESS_MASK值,该值确定对对象的请求访问权限。
  4.   [in]           POBJECT_ATTRIBUTES ObjectAttributes,  // 指向OBJECT_ATTRIBUTES结构的指针,该结构指定对象名称和其他属性。
  5.   [out]          PIO_STATUS_BLOCK   IoStatusBlock,     // 指向IO_STATUS_BLOCK结构的指针,该结构接收最终完成状态和有关所请求操作的其他信息。
  6.   [in, optional] PLARGE_INTEGER     AllocationSize,    // 指向LARGE_INTEGER的指针,其中包含创建或覆盖的文件的初始分配大小(以字节为单位)。
  7.   [in]           ULONG              FileAttributes,    // 指定一个或多个FILE_ATTRIBUTE_XXX标志,这些标志表示在创建或覆盖文件时要设置的文件属性。
  8.   [in]           ULONG              ShareAccess,       // 共享访问的类型,指定为零或以下标志的任意组合。
  9.   [in]           ULONG              CreateDisposition, // 指定在文件存在或不存在时要执行的操作。
  10.   [in]           ULONG              CreateOptions,     // 指定要在驱动程序创建或打开文件时应用的选项。
  11.   [in, optional] PVOID              EaBuffer,          // 对于设备和中间驱动程序,此参数必须是NULL指针。
  12.   [in]           ULONG              EaLength           // 对于设备和中间驱动程序,此参数必须为零。
  13. );
复制代码
参数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参数即可,其微软定义如下所示;
  1. NTSYSAPI NTSTATUS ZwDeleteFile(
  2.   [in] POBJECT_ATTRIBUTES ObjectAttributes
  3. );
复制代码
接下来我们就封装三个函数MyCreateFile()用于创建文件,MyCreateFileFolder()用于创建目录,MyDeleteFileOrFileFolder()用于删除空目录。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <ntstrsafe.h>
  7. // 创建文件
  8. BOOLEAN MyCreateFile(UNICODE_STRING ustrFilePath)
  9. {
  10.         HANDLE hFile = NULL;
  11.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  12.         IO_STATUS_BLOCK iosb = { 0 };
  13.         NTSTATUS status = STATUS_SUCCESS;
  14.         // 初始化对象属性结构体 FILE_SYNCHRONOUS_IO_NONALERT
  15.         InitializeObjectAttributes(&objectAttributes, &ustrFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  16.         // 创建文件
  17.         status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
  18.         if (!NT_SUCCESS(status))
  19.         {
  20.                 return FALSE;
  21.         }
  22.         // 关闭句柄
  23.         ZwClose(hFile);
  24.         return TRUE;
  25. }
  26. // 创建目录
  27. BOOLEAN MyCreateFileFolder(UNICODE_STRING ustrFileFolderPath)
  28. {
  29.         HANDLE hFile = NULL;
  30.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  31.         IO_STATUS_BLOCK iosb = { 0 };
  32.         NTSTATUS status = STATUS_SUCCESS;
  33.         // 初始化对象属性结构体
  34.         InitializeObjectAttributes(&objectAttributes, &ustrFileFolderPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  35.         // 创建目录 FILE_DIRECTORY_FILE
  36.         status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_DIRECTORY_FILE, NULL, 0);
  37.         if (!NT_SUCCESS(status))
  38.         {
  39.                 return FALSE;
  40.         }
  41.         // 关闭句柄
  42.         ZwClose(hFile);
  43.         return TRUE;
  44. }
  45. // 删除文件或是空目录
  46. BOOLEAN MyDeleteFileOrFileFolder(UNICODE_STRING ustrFileName)
  47. {
  48.         NTSTATUS status = STATUS_SUCCESS;
  49.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  50.         // 初始化属性
  51.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  52.         // 执行删除操作
  53.         status = ZwDeleteFile(&objectAttributes);
  54.         if (!NT_SUCCESS(status))
  55.         {
  56.                 return FALSE;
  57.         }
  58.         return TRUE;
  59. }
  60. VOID UnDriver(PDRIVER_OBJECT driver)
  61. {
  62.         BOOLEAN ref = FALSE;
  63.         // 删除文件
  64.         UNICODE_STRING ustrDeleteFile;
  65.         RtlInitUnicodeString(&ustrDeleteFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt");
  66.         ref = MyDeleteFileOrFileFolder(ustrDeleteFile);
  67.         if (ref != FALSE)
  68.         {
  69.                 DbgPrint("[LyShark] 删除文件成功 \n");
  70.         }
  71.         else
  72.         {
  73.                 DbgPrint("[LyShark] 删除文件失败 \n");
  74.         }
  75.         // 删除空目录
  76.         UNICODE_STRING ustrDeleteFilder;
  77.         RtlInitUnicodeString(&ustrDeleteFilder, L"\\??\\C:\\LySharkFolder");
  78.         ref = MyDeleteFileOrFileFolder(ustrDeleteFilder);
  79.         if (ref != FALSE)
  80.         {
  81.                 DbgPrint("[LyShark] 删除空目录成功 \n");
  82.         }
  83.         else
  84.         {
  85.                 DbgPrint("[LyShark] 删除空目录失败 \n");
  86.         }
  87.         DbgPrint("驱动卸载 \n");
  88. }
  89. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  90. {
  91.         DbgPrint("hello lyshark.com \n");
  92.         BOOLEAN ref = FALSE;
  93.         // 创建目录
  94.         UNICODE_STRING ustrDirectory;
  95.         RtlInitUnicodeString(&ustrDirectory, L"\\??\\C:\\LySharkFolder");
  96.         ref = MyCreateFileFolder(ustrDirectory);
  97.         if (ref != FALSE)
  98.         {
  99.                 DbgPrint("[LyShark] 创建目录成功 \n");
  100.         }
  101.         else
  102.         {
  103.                 DbgPrint("[LyShark] 创建文件失败 \n");
  104.         }
  105.         // 创建文件
  106.         UNICODE_STRING ustrCreateFile;
  107.         RtlInitUnicodeString(&ustrCreateFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt");
  108.         ref = MyCreateFile(ustrCreateFile);
  109.         if (ref != FALSE)
  110.         {
  111.                 DbgPrint("[LyShark] 创建文件成功 \n");
  112.         }
  113.         else
  114.         {
  115.                 DbgPrint("[LyShark] 创建文件失败 \n");
  116.         }
  117.         Driver->DriverUnload = UnDriver;
  118.         return STATUS_SUCCESS;
  119. }
复制代码
运行如上代码,分别创建LySharkFolder目录,并在其中创建lyshark.txt最终再将其删除,输出效果如下;
1.png

重命名文件或目录: 在内核中重命名文件或目录核心功能的实现依赖于ZwSetInformationFile()这个内核函数,该函数可用于更改有关文件对象的各种信息,其微软官方定义如下;
  1. NTSYSAPI NTSTATUS ZwSetInformationFile(
  2.   [in]  HANDLE                 FileHandle,          // 文件句柄
  3.   [out] PIO_STATUS_BLOCK       IoStatusBlock,       // 指向 IO_STATUS_BLOCK 结构的指针
  4.   [in]  PVOID                  FileInformation,     // 指向缓冲区的指针,该缓冲区包含要为文件设置的信息。
  5.   [in]  ULONG                  Length,              // 缓冲区的大小(以字节为单位)
  6.   [in]  FILE_INFORMATION_CLASS FileInformationClass // 为文件设置的类型
  7. );
复制代码
这其中最重要的参数就是FileInformationClass根据该参数的不同则对文件的操作方式也就不同,如果需要重命名文件则此处应使用FileRenameInformation而如果需要修改文件的当前信息则应使用FilePositionInformation创建链接文件则使用FileLinkInformation即可,以重命名为例,首先我们需要定义一个FILE_RENAME_INFORMATION结构并按照要求填充,最后直接使用ZwSetInformationFile()并传入相关信息后即可完成修改,其完整代码流程如下;
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <ntstrsafe.h>
  7. // 重命名文件或文件夹
  8. BOOLEAN MyRename(UNICODE_STRING ustrSrcFileName, UNICODE_STRING ustrDestFileName)
  9. {
  10.         HANDLE hFile = NULL;
  11.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  12.         IO_STATUS_BLOCK iosb = { 0 };
  13.         NTSTATUS status = STATUS_SUCCESS;
  14.         PFILE_RENAME_INFORMATION pRenameInfo = NULL;
  15.         ULONG ulLength = (1024 + sizeof(FILE_RENAME_INFORMATION));
  16.         // 为PFILE_RENAME_INFORMATION结构申请内存
  17.         pRenameInfo = (PFILE_RENAME_INFORMATION)ExAllocatePool(NonPagedPool, ulLength);
  18.         if (NULL == pRenameInfo)
  19.         {
  20.                 return FALSE;
  21.         }
  22.         // 设置重命名信息
  23.         RtlZeroMemory(pRenameInfo, ulLength);
  24.         // 设置文件名长度以及文件名
  25.         pRenameInfo->FileNameLength = ustrDestFileName.Length;
  26.         wcscpy(pRenameInfo->FileName, ustrDestFileName.Buffer);
  27.         pRenameInfo->ReplaceIfExists = 0;
  28.         pRenameInfo->RootDirectory = NULL;
  29.         // 初始化结构
  30.         InitializeObjectAttributes(&objectAttributes, &ustrSrcFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  31.         // 打开文件
  32.         status = ZwCreateFile(&hFile, SYNCHRONIZE | DELETE, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING, NULL, 0);
  33.         if (!NT_SUCCESS(status))
  34.         {
  35.                 ExFreePool(pRenameInfo);
  36.                 return FALSE;
  37.         }
  38.         // 利用ZwSetInformationFile来设置文件信息
  39.         status = ZwSetInformationFile(hFile, &iosb, pRenameInfo, ulLength, FileRenameInformation);
  40.         if (!NT_SUCCESS(status))
  41.         {
  42.                 ZwClose(hFile);
  43.                 ExFreePool(pRenameInfo);
  44.                 return FALSE;
  45.         }
  46.         // 释放内存,关闭句柄
  47.         ExFreePool(pRenameInfo);
  48.         ZwClose(hFile);
  49.         return TRUE;
  50. }
  51. VOID UnDriver(PDRIVER_OBJECT driver)
  52. {
  53.         DbgPrint("驱动卸载 \n");
  54. }
  55. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  56. {
  57.         DbgPrint("hello lyshark.com \n");
  58.         // 重命名文件
  59.         UNICODE_STRING ustrOldFile, ustrNewFile;
  60.         RtlInitUnicodeString(&ustrOldFile, L"\\??\\C:\\MyCreateFolder\\lyshark.txt");
  61.         RtlInitUnicodeString(&ustrNewFile, L"\\??\\C:\\MyCreateFolder\\hello_lyshark.txt");
  62.         BOOLEAN ref = MyRename(ustrOldFile, ustrNewFile);
  63.         if (ref == TRUE)
  64.         {
  65.                 DbgPrint("已完成改名 \n");
  66.         }
  67.         Driver->DriverUnload = UnDriver;
  68.         return STATUS_SUCCESS;
  69. }
复制代码
运行后将会把C:\\MyCreateFolder\\lyshark.txt目录下的文件改名为hello_lyshark.txt,前提是该目录与该文件必须存在;
2.png

那么如果你需要将文件设置为只读模式或修改文件的创建日期,那么你就需要看一下微软的定义FILE_BASIC_INFORMATION结构,依次填充此结构体并调用ZwSetInformationFile()即可实现修改,该结构的定义如下所示;
  1. typedef struct _FILE_BASIC_INFORMATION {
  2.     LARGE_INTEGER CreationTime;
  3.     LARGE_INTEGER LastAccessTime;
  4.     LARGE_INTEGER LastWriteTime;
  5.     LARGE_INTEGER ChangeTime;
  6.     ULONG FileAttributes;
  7. } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
复制代码
当然如果你要修改日期你还需要自行填充LARGE_INTEGER结构,该结构的微软定义如下所示,分为高位和低位依次填充即可;
  1. #if defined(MIDL_PASS)
  2. typedef struct _LARGE_INTEGER {
  3. #else // MIDL_PASS
  4. typedef union _LARGE_INTEGER {
  5.     struct {
  6.         ULONG LowPart;
  7.         LONG HighPart;
  8.     } DUMMYSTRUCTNAME;
  9.     struct {
  10.         ULONG LowPart;
  11.         LONG HighPart;
  12.     } u;
  13. #endif //MIDL_PASS
  14.     LONGLONG QuadPart;
  15. } LARGE_INTEGER;
复制代码
我们就以修改文件属性为只读模式为例,其核心代码可以被描述为如下样子,相比于改名而言其唯一的变化就是更换了PFILE_BASIC_INFORMATION结构体,其他的基本一致;
  1. HANDLE hFile = NULL;
  2. OBJECT_ATTRIBUTES objectAttributes = { 0 };
  3. IO_STATUS_BLOCK iosb = { 0 };
  4. NTSTATUS status = STATUS_SUCCESS;
  5. PFILE_BASIC_INFORMATION pReplaceInfo = NULL;
  6. ULONG ulLength = (1024 + sizeof(FILE_BASIC_INFORMATION));
  7. // 为FILE_POSITION_INFORMATION结构申请内存
  8. pReplaceInfo = (PFILE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ulLength);
  9. if (NULL == pReplaceInfo)
  10. {
  11.   return FALSE;
  12. }
  13. RtlZeroMemory(pReplaceInfo, ulLength);
  14. // 设置文件基础信息,将文件设置为只读模式
  15. pReplaceInfo->FileAttributes |= FILE_ATTRIBUTE_READONLY;
复制代码
读取文件大小: 读取特定文件的所占空间,核心原理是调用了ZwQueryInformationFile()这个内核函数,该函数可以返回有关文件对象的各种信息,参数传递上与ZwSetInformationFile()很相似,其FileInformationClass都需要传入一个文件类型结构,该函数的完整定义如下;
  1. NTSYSAPI NTSTATUS ZwQueryInformationFile(
  2.   [in]  HANDLE                 FileHandle,          // 文件句柄。
  3.   [out] PIO_STATUS_BLOCK       IoStatusBlock,       // 指向接收最终完成状态和操作相关信息的 IO_STATUS_BLOCK 结构的指针。
  4.   [out] PVOID                  FileInformation,     // 指向调用方分配的缓冲区的指针,例程将请求的有关文件对象的信息写入其中。
  5.   [in]  ULONG                  Length,              // 长度。
  6.   [in]  FILE_INFORMATION_CLASS FileInformationClass // 指定要在 FileInformation 指向的缓冲区中返回的有关文件的信息类型。
  7. );
复制代码
本例中我们需要读入文件的所占字节数,那么FileInformation字段就需要传入FileStandardInformation来获取文件的基本信息,获取到的信息会被存储到FILE_STANDARD_INFORMATION结构内,用户只需要解析该结构体fsi.EndOfFile.QuadPart即可得到文件长度,其完整代码如下所示;
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <ntstrsafe.h>
  7. // 获取文件大小
  8. ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
  9. {
  10.         HANDLE hFile = NULL;
  11.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  12.         IO_STATUS_BLOCK iosb = { 0 };
  13.         NTSTATUS status = STATUS_SUCCESS;
  14.         FILE_STANDARD_INFORMATION fsi = { 0 };
  15.         // 初始化结构
  16.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  17.         // 打开文件
  18.         status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
  19.         if (!NT_SUCCESS(status))
  20.         {
  21.                 return 0;
  22.         }
  23.         // 获取文件大小信息
  24.         status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
  25.         if (!NT_SUCCESS(status))
  26.         {
  27.                 ZwClose(hFile);
  28.                 return 0;
  29.         }
  30.         return fsi.EndOfFile.QuadPart;
  31. }
  32. VOID UnDriver(PDRIVER_OBJECT driver)
  33. {
  34.         DbgPrint("驱动卸载 \n");
  35. }
  36. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  37. {
  38.         DbgPrint("hello lyshark.com \n");
  39.         // 获取文件大小
  40.         UNICODE_STRING ustrFileSize;
  41.         RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\lyshark.exe");
  42.         ULONG64 ullFileSize = MyGetFileSize(ustrFileSize);
  43.         DbgPrint("获取文件大小: %I64d Bytes \n", ullFileSize);
  44.         Driver->DriverUnload = UnDriver;
  45.         return STATUS_SUCCESS;
  46. }
复制代码
编译并运行如上程序,即可读取到C盘下的lyshark.exe程序的大小字节数,如下图所示;
3.png

内核文件读写: 内核读取文件可以使用ZwReadFile(),内核写入文件则可使用ZwWriteFile(),这两个函数的参数传递基本上一致,如下是读写两个函数的对比参数。
  1. NTSYSAPI NTSTATUS ZwReadFile(
  2.   [in]           HANDLE           FileHandle,      // 文件对象的句柄。
  3.   [in, optional] HANDLE           Event,           // (可选)事件对象的句柄,在读取操作完成后设置为信号状态。
  4.   [in, optional] PIO_APC_ROUTINE  ApcRoutine,      // 此参数为保留参数。
  5.   [in, optional] PVOID            ApcContext,      // 此参数为保留参数。
  6.   [out]          PIO_STATUS_BLOCK IoStatusBlock,   // 接收实际从文件读取的字节数。
  7.   [out]          PVOID            Buffer,          // 指向调用方分配的缓冲区的指针,该缓冲区接收从文件读取的数据。
  8.   [in]           ULONG            Length,          // 缓冲区指向的缓冲区的大小(以字节为单位)。
  9.   [in, optional] PLARGE_INTEGER   ByteOffset,      // 指定将开始读取操作的文件中的起始字节偏移量。
  10.   [in, optional] PULONG           Key
  11. );
  12. NTSYSAPI NTSTATUS ZwWriteFile(
  13.   [in]           HANDLE           FileHandle,
  14.   [in, optional] HANDLE           Event,
  15.   [in, optional] PIO_APC_ROUTINE  ApcRoutine,
  16.   [in, optional] PVOID            ApcContext,
  17.   [out]          PIO_STATUS_BLOCK IoStatusBlock,
  18.   [in]           PVOID            Buffer,
  19.   [in]           ULONG            Length,
  20.   [in, optional] PLARGE_INTEGER   ByteOffset,
  21.   [in, optional] PULONG           Key
  22. );
复制代码
读取文件的代码如下所示,分配非分页pBuffer内存,然后调用MyReadFile()函数,将数据读入到pBuffer并输出,完整代码如下所示;
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <ntstrsafe.h>
  7. // 读取文件数据
  8. BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
  9. {
  10.         HANDLE hFile = NULL;
  11.         IO_STATUS_BLOCK iosb = { 0 };
  12.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  13.         NTSTATUS status = STATUS_SUCCESS;
  14.         // 初始化结构
  15.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  16.         // 打开文件
  17.         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);
  18.         if (!NT_SUCCESS(status))
  19.         {
  20.                 return FALSE;
  21.         }
  22.         // 初始化
  23.         RtlZeroMemory(&iosb, sizeof(iosb));
  24.         // 读入文件
  25.         status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);
  26.         if (!NT_SUCCESS(status))
  27.         {
  28.                 *pulReadDataSize = iosb.Information;
  29.                 ZwClose(hFile);
  30.                 return FALSE;
  31.         }
  32.         // 获取实际读取的数据
  33.         *pulReadDataSize = iosb.Information;
  34.         // 关闭句柄
  35.         ZwClose(hFile);
  36.         return TRUE;
  37. }
  38. VOID UnDriver(PDRIVER_OBJECT driver)
  39. {
  40.         DbgPrint("驱动卸载 \n");
  41. }
  42. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  43. {
  44.         DbgPrint("hello lyshark.com \n");
  45.         UNICODE_STRING ustrScrFile;
  46.         ULONG ulBufferSize = 40960;
  47.         LARGE_INTEGER liOffset = { 0 };
  48.         // 初始化需要读取的文件名
  49.         RtlInitUnicodeString(&ustrScrFile, L"\\??\\C:\\lyshark.exe");
  50.         // 分配非分页内存
  51.         PUCHAR pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);
  52.         // 读取文件
  53.         MyReadFile(ustrScrFile, liOffset, pBuffer, &ulBufferSize);
  54.         // 输出文件前16个字节
  55.         for (size_t i = 0; i < 16; i++)
  56.         {
  57.                 DbgPrint("%02X \n", pBuffer[i]);
  58.         }
  59.         Driver->DriverUnload = UnDriver;
  60.         return STATUS_SUCCESS;
  61. }
复制代码
编译并运行这段代码,并循环输出lyshark.exe文件的头16个字节的数据,效果图如下所示;
4.png

文件写入MyWriteFile()与读取类似,如下通过运用文件读写实现了文件拷贝功能,实现完整代码如下所示;
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <ntstrsafe.h>
  7. // 读取文件数据
  8. BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
  9. {
  10.         HANDLE hFile = NULL;
  11.         IO_STATUS_BLOCK iosb = { 0 };
  12.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  13.         NTSTATUS status = STATUS_SUCCESS;
  14.         // 初始化结构
  15.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  16.         // 打开文件
  17.         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);
  18.         if (!NT_SUCCESS(status))
  19.         {
  20.                 return FALSE;
  21.         }
  22.         // 初始化
  23.         RtlZeroMemory(&iosb, sizeof(iosb));
  24.         // 读入文件
  25.         status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);
  26.         if (!NT_SUCCESS(status))
  27.         {
  28.                 *pulReadDataSize = iosb.Information;
  29.                 ZwClose(hFile);
  30.                 return FALSE;
  31.         }
  32.         // 获取实际读取的数据
  33.         *pulReadDataSize = iosb.Information;
  34.         // 关闭句柄
  35.         ZwClose(hFile);
  36.         return TRUE;
  37. }
  38. // 向文件写入数据
  39. BOOLEAN MyWriteFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pWriteData, PULONG pulWriteDataSize)
  40. {
  41.         HANDLE hFile = NULL;
  42.         IO_STATUS_BLOCK iosb = { 0 };
  43.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  44.         NTSTATUS status = STATUS_SUCCESS;
  45.         // 初始化结构
  46.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  47.         // 打开文件
  48.         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);
  49.         if (!NT_SUCCESS(status))
  50.         {
  51.                 return FALSE;
  52.         }
  53.         // 初始化
  54.         RtlZeroMemory(&iosb, sizeof(iosb));
  55.         // 写出文件
  56.         status = ZwWriteFile(hFile, NULL, NULL, NULL, &iosb, pWriteData, *pulWriteDataSize, &liOffset, NULL);
  57.         if (!NT_SUCCESS(status))
  58.         {
  59.                 *pulWriteDataSize = iosb.Information;
  60.                 ZwClose(hFile);
  61.                 return FALSE;
  62.         }
  63.         // 获取实际写入的数据
  64.         *pulWriteDataSize = iosb.Information;
  65.        
  66.         // 关闭句柄
  67.         ZwClose(hFile);
  68.         return TRUE;
  69. }
  70. VOID UnDriver(PDRIVER_OBJECT driver)
  71. {
  72.         DbgPrint("驱动卸载 \n");
  73. }
  74. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  75. {
  76.         DbgPrint("hello lyshark.com \n");
  77.         // 文件读写
  78.         UNICODE_STRING ustrScrFile, ustrDestFile;
  79.         RtlInitUnicodeString(&ustrScrFile, L"\\??\\C:\\lyshark.exe");
  80.         RtlInitUnicodeString(&ustrDestFile, L"\\??\\C:\\LyShark\\new_lyshark.exe");
  81.         ULONG ulBufferSize = 40960;
  82.         ULONG ulReadDataSize = ulBufferSize;
  83.         LARGE_INTEGER liOffset = { 0 };
  84.         // 分配非分页内存
  85.         PUCHAR pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);
  86.         do
  87.         {
  88.                 // 读取文件
  89.                 ulReadDataSize = ulBufferSize;
  90.                 MyReadFile(ustrScrFile, liOffset, pBuffer, &ulReadDataSize);
  91.                 // 数据为空则读取结束
  92.                 if (0 >= ulReadDataSize)
  93.                 {
  94.                         break;
  95.                 }
  96.                 // 写入文件
  97.                 MyWriteFile(ustrDestFile, liOffset, pBuffer, &ulReadDataSize);
  98.                 // 更新偏移
  99.                 liOffset.QuadPart = liOffset.QuadPart + ulReadDataSize;
  100.                 DbgPrint("[+] 更新偏移: %d \n", liOffset.QuadPart);
  101.         } while (TRUE);
  102.         // 释放内存
  103.         ExFreePool(pBuffer);
  104.         DbgPrint("[*] 已将文件复制到新目录 \n");
  105.         Driver->DriverUnload = UnDriver;
  106.         return STATUS_SUCCESS;
  107. }
复制代码
编译并运行这段程序,则自动将C:\\lyshark.exe盘符下的文件拷贝到C:\\LyShark\\new_lyshark.exe目录下,实现效果图如下所示;
5.png

实现文件读写传递: 通过如上学习相信你已经掌握了如何使用文件读写系列函数了,接下来将封装一个文件读写驱动,应用层接收,驱动层读取;
此驱动部分完整代码如下所示;
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <windef.h>
  7. #define READ_FILE_SIZE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
  8. #define READ_FILE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
  9. #define DEVICENAME L"\\Device\\ReadWriteDevice"
  10. #define SYMBOLNAME L"\\??\\ReadWriteSymbolName"
  11. typedef struct
  12. {
  13.         ULONG64 size;      // 读写长度
  14.         BYTE* data;        // 读写数据集
  15. }FileData;
  16. // 获取文件大小
  17. ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
  18. {
  19.         HANDLE hFile = NULL;
  20.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  21.         IO_STATUS_BLOCK iosb = { 0 };
  22.         NTSTATUS status = STATUS_SUCCESS;
  23.         FILE_STANDARD_INFORMATION fsi = { 0 };
  24.         // 初始化结构
  25.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  26.         // 打开文件
  27.         status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
  28.         if (!NT_SUCCESS(status))
  29.         {
  30.                 return 0;
  31.         }
  32.         // 获取文件大小信息
  33.         status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
  34.         if (!NT_SUCCESS(status))
  35.         {
  36.                 ZwClose(hFile);
  37.                 return 0;
  38.         }
  39.         return fsi.EndOfFile.QuadPart;
  40. }
  41. // 读取文件数据
  42. BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
  43. {
  44.         HANDLE hFile = NULL;
  45.         IO_STATUS_BLOCK iosb = { 0 };
  46.         OBJECT_ATTRIBUTES objectAttributes = { 0 };
  47.         NTSTATUS status = STATUS_SUCCESS;
  48.         // 初始化结构
  49.         InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
  50.         // 打开文件
  51.         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);
  52.         if (!NT_SUCCESS(status))
  53.         {
  54.                 return FALSE;
  55.         }
  56.         // 初始化
  57.         RtlZeroMemory(&iosb, sizeof(iosb));
  58.         // 读入文件
  59.         status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);
  60.         if (!NT_SUCCESS(status))
  61.         {
  62.                 *pulReadDataSize = iosb.Information;
  63.                 ZwClose(hFile);
  64.                 return FALSE;
  65.         }
  66.         // 获取实际读取的数据
  67.         *pulReadDataSize = iosb.Information;
  68.         // 关闭句柄
  69.         ZwClose(hFile);
  70.         return TRUE;
  71. }
  72. NTSTATUS DriverIrpCtl(PDEVICE_OBJECT device, PIRP pirp)
  73. {
  74.         PIO_STACK_LOCATION stack;
  75.         stack = IoGetCurrentIrpStackLocation(pirp);
  76.         FileData* FileDataPtr;
  77.         switch (stack->MajorFunction)
  78.         {
  79.         case IRP_MJ_CREATE:
  80.         {
  81.                 break;
  82.         }
  83.         case IRP_MJ_CLOSE:
  84.         {
  85.                 break;
  86.         }
  87.         case IRP_MJ_DEVICE_CONTROL:
  88.         {
  89.                 // 获取应用层传值
  90.                 FileDataPtr = pirp->AssociatedIrp.SystemBuffer;
  91.                 switch (stack->Parameters.DeviceIoControl.IoControlCode)
  92.                 {
  93.                         // 读取内存函数
  94.                 case READ_FILE_SIZE_CODE:
  95.                 {
  96.                         LARGE_INTEGER liOffset = { 0 };
  97.                         UNICODE_STRING ustrFileSize;
  98.                         RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\Windows\\system32\\ntoskrnl.exe");
  99.                         // 获取文件长度
  100.                         ULONG64 ulBufferSize = MyGetFileSize(ustrFileSize);
  101.                         DbgPrint("获取文件大小: %I64d Bytes \n", ulBufferSize);
  102.                         // 将长度返回应用层
  103.                         FileDataPtr->size = ulBufferSize;
  104.                         break;
  105.                 }
  106.                 // 读取文件
  107.                 case READ_FILE_CODE:
  108.                 {
  109.                         FileData ptr;
  110.                         LARGE_INTEGER liOffset = { 0 };
  111.                         UNICODE_STRING ustrFileSize;
  112.                         RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\Windows\\system32\\ntoskrnl.exe");
  113.                         // 获取文件长度
  114.                         ULONG64 ulBufferSize = MyGetFileSize(ustrFileSize);
  115.                         DbgPrint("获取文件大小: %I64d Bytes \n", ulBufferSize);
  116.                         // 读取内存到缓冲区
  117.                         BYTE* pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);
  118.                         MyReadFile(ustrFileSize, liOffset, pBuffer, &ulBufferSize);
  119.                         // 返回数据
  120.                         FileDataPtr->size = ulBufferSize;
  121.                         RtlCopyMemory(FileDataPtr->data, pBuffer, FileDataPtr->size);
  122.                         break;
  123.                 }
  124.                 }
  125.                 pirp->IoStatus.Information = sizeof(FileDataPtr);
  126.                 break;
  127.         }
  128.         }
  129.         pirp->IoStatus.Status = STATUS_SUCCESS;
  130.         IoCompleteRequest(pirp, IO_NO_INCREMENT);
  131.         return STATUS_SUCCESS;
  132. }
  133. VOID UnDriver(PDRIVER_OBJECT driver)
  134. {
  135.         if (driver->DeviceObject)
  136.         {
  137.                 UNICODE_STRING SymbolName;
  138.                 RtlInitUnicodeString(&SymbolName, SYMBOLNAME);
  139.                 // 删除符号链接
  140.                 IoDeleteSymbolicLink(&SymbolName);
  141.                 IoDeleteDevice(driver->DeviceObject);
  142.         }
  143. }
  144. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  145. {
  146.         NTSTATUS status = STATUS_SUCCESS;
  147.         PDEVICE_OBJECT device = NULL;
  148.         UNICODE_STRING DeviceName;
  149.         DbgPrint("[LyShark] hello lyshark.com \n");
  150.         // 初始化设备名
  151.         RtlInitUnicodeString(&DeviceName, DEVICENAME);
  152.         // 创建设备
  153.         status = IoCreateDevice(Driver, sizeof(Driver->DriverExtension), &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);
  154.         if (status == STATUS_SUCCESS)
  155.         {
  156.                 UNICODE_STRING SymbolName;
  157.                 RtlInitUnicodeString(&SymbolName, SYMBOLNAME);
  158.                 // 创建符号链接
  159.                 status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
  160.                 // 失败则删除设备
  161.                 if (status != STATUS_SUCCESS)
  162.                 {
  163.                         IoDeleteDevice(device);
  164.                 }
  165.         }
  166.         // 派遣函数初始化
  167.         Driver->MajorFunction[IRP_MJ_CREATE] = DriverIrpCtl;
  168.         Driver->MajorFunction[IRP_MJ_CLOSE] = DriverIrpCtl;
  169.         Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIrpCtl;
  170.         // 卸载驱动
  171.         Driver->DriverUnload = UnDriver;
  172.         return STATUS_SUCCESS;
  173. }
复制代码
客户端完整代码如下所示;
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #define _CRT_SECURE_NO_WARNINGS
  6. #include <Windows.h>
  7. #include <iostream>
  8. #define READ_FILE_SIZE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
  9. #define READ_FILE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
  10. typedef struct
  11. {
  12.         DWORD size;      // 读写长度
  13.         BYTE* data;      // 读写数据集
  14. }FileData;
  15. int main(int argc, char* argv[])
  16. {
  17.         // 连接到驱动
  18.         HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  19.         FileData data;
  20.         DWORD dwSize = 0;
  21.         // 首先得到文件长度
  22.         DeviceIoControl(handle, READ_FILE_SIZE_CODE, 0, 0, &data, sizeof(data), &dwSize, NULL);
  23.         printf("%d \n", data.size);
  24.         // 读取机器码到BYTE字节数组
  25.         data.data = new BYTE[data.size];
  26.         DeviceIoControl(handle, READ_FILE_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);
  27.         for (int i = 0; i < data.size; i++)
  28.         {
  29.                 printf("0x%02X ", data.data[i]);
  30.         }
  31.         printf("\n");
  32.         getchar();
  33.         CloseHandle(handle);
  34.         return 0;
  35. }
复制代码
通过驱动加载工具将WinDDK.sys拉起来,然后启动客户端进程,即可输出ntoskrnl.exe的文件数据,如下图所示;
6.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册