问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
初探内核下的文件管理技术:内核API
前言 Windows内核下的Rootkit开发技术学习。 用户数据都以文件的形式存储在本地磁盘上,Rootkit等恶意软件想要获取用户的隐私数据就需要有操作文件的功能,包括但不限于增、删、查、改。一般有...
前言 == Windows内核下的Rootkit开发技术学习。 用户数据都以文件的形式存储在本地磁盘上,Rootkit等恶意软件想要获取用户的隐私数据就需要有操作文件的功能,包括但不限于增、删、查、改。一般有三种文件管理的方式,一是基于导出的内核API直接操作文件,二是通过程序直接构造输入输出请求包(I/O Request Package,IRP)并发送IRP来操作文件,三是根据文件系统格式(New Technology File System,NTFS)来解析硬盘上的二进制数据。本文先从相对简单的内核API实现开始学习,内核API和用户模式中的WIN32 API有一定的类似。 编程环境: - 主机 Win11 Visual Studio2019,SDK和WDK 10.0.19041.685 - 虚拟机 Win10测试模式 前置 == 内核API ----- 内核API是一组从内核组件中输出的函数,大多数函数在内核本身模块(NtOskrnl.exe)中实现,少部分在其它模块中(如hal.dll)。内核API是大量C函数的集合,其中大多数名称含有一个前缀,这个前缀指示了实现该函数的模块。 常见的一些内核API前缀: - Ex:通用执行体函数 `ExAllocatePool` - Ke:通用内核函数 `KeAcquireSpinLock` - Io:I/O管理器 `IoCompleteRequest` - Zw:原生API包装 `ZwCreateFile` Zw前缀的内核API是原生API包装的,原生API指的是前缀为Nt的一系列函数。Nt系列函数(如`NtCreateFile`)是Windows内核提供的原生API,它们直接与内核模式交互。但应用程序调用WIN32API时,这些API最终会转换成对应的Nt函数,执行实际的操作。 所谓的包装意味着Zw系列和Nt系列在实现上几乎相同,但它们通过不同的入口点进入系统。当内核模式调用Nt系列函数时,函数会检查调用上下文,以确定调用来自内核模式还是用户模式,如果来自用户模式,函数可能会进行一些安全检查或模式转换。而调用Zw系列函数,系统假设调用已经在内核模式下了,即直接将PreviousMode设置成 KernelMode(0)。 OBJECT\_ATTRIBUTES 结构 --------------------- `OBJECT_ATTRIBUTES`是Windows内核编程中用于描述操作系统对象(如文件、设备、事件、互斥体等)属性的结构体。该结构体通常在创建或打开内核对象时被使用,传递给如`ZwCreateFile`、`ZwOpenKey`等内核API。 ```c typedef struct _OBJECT_ATTRIBUTES { ULONG Length; // 结构体的大小,以字节为单位。 HANDLE RootDirectory; //表示对象名称的根目录句柄。 PUNICODE_STRING ObjectName; //指向UNICODE_STRING结构体的指针,表示对象的名称。 ULONG Attributes; //指定对象的属性。 PSECURITY_DESCRIPTOR SecurityDescriptor; //指向安全描述符的指针。常设NULL。 PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; //用于指定对象的安全服务质量属性,常设NULL。 } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; ``` 上述字段中,如果`RootDirectory`为NULL,则`ObjectName`必须是一个完整的绝对路径名。否则,`ObjectName`是相对与`RootDirectory`目录的路径名。 `Attributes`常见的属性标志: - `OBJ_CASE_INSENSITIVE`:表示对象名称比较不区分大小写。 - `OBJ_KERNEL_HANDLE`:指示句柄只能在内核模式下访问。 - `OBJ_OPENIF`:如果对象存在,则打开它,而不是失败。 - `OBJ_PERMANENT`:创建一个永久对象,不会自动删除。 InitializeObjectAttributes 宏 ---------------------------- `InitializeObjectAttributes` 宏用于初始化 `OBJECT_ATTRIBUTES` 结构体,这俩者都同属于`ntdef.h`。 宏定义如下: ```c #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; \ } ``` p 指向 `OBJECT_ATTRIBUTES`结构体的指针,字段如上述一样。 文件的创建和删除 ======== ZwCreateFile ------------ 在内核层中的病毒木马要想创建或者释放植入程序,最先的是创建一个文件。`ZwCreateFile`函数用于创建或打开文件对象,它是内核模式下访问文件系统的关键API。该函数同样可以用于创建或打开目录。 函数原型如下: ```c NTSTATUS ZwCreateFile( PHANDLE FileHandle, //指向接收文件句柄的指针。 ACCESS_MASK DesiredAccess, //指定文件访问的类型,例如读、写、执行等。 POBJECT_ATTRIBUTES ObjectAttributes,//指向已经初始化的OBJECT_ATTRIBUTES结构体. PIO_STATUS_BLOCK IoStatusBlock, //指向IO_STATUS_BLOCK结构的指针 PLARGE_INTEGER AllocationSize, //指定文件的分配大小。 ULONG FileAttributes, //指定了创建或覆盖文件时的属性 ULONG ShareAccess, //指定文件的共享模式。 ULONG CreateDisposition, //指定文件的创建方式,如文件已存在时应如何处理。 ULONG CreateOptions, //指定文件的创建方式,如文件已存在时应如何处理。 PVOID EaBuffer, //指向扩展属性(EA,Extended Attributes)缓冲区的指针,通常用于网络文件系统。 ULONG EaLength //EaBuffer的长度 ); ``` `ZwCreateFile`在使用时取决于字段`CreateOptions`,如果该字段设置了FILE\_DIRECTORY\_FILE标志,表明这是创建一个目录。 在内核模式下创建文件的步骤: - 初始化`UNICODE_STRING`结构,这是文件路径的表达形式。 - 设置`OBJECT_ATTRIBUTES`结构体,调用宏初始化。 - 调用`ZwCreateFile`函数。 - 在执行完相应操作后使用`ZwClose`关闭文件句柄,释放资源。 ### 具体代码: ```c #include void CreateFileTest() { NTSTATUS status; HANDLE hFile; OBJECT_ATTRIBUTES objAttr; IO_STATUS_BLOCK ioStatusBlock; UNICODE_STRING fileName; //初始化UNICODE_STRING RtlInitUnicodeString(&fileName, L"\\??\\C:\\Users\\example\\Desktop\\syswork\\MyFile.txt"); //初始化OBJECT_ATTRIBUTES InitializeObjectAttributes(&objAttr, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); //调用ZwCreateFile status = ZwCreateFile(&hFile, GENERIC_WRITE | GENERIC_READ, &objAttr, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); //检查 if (NT_SUCCESS(status)) { DbgPrint("File created or opened successfully.\n"); } else { DbgPrint("Failed to create or open file.Status: 0x%X\n", status); } //关闭文件句柄 if (hFile) { ZwClose(hFile); } } NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(DriverObject); UNREFERENCED_PARAMETER(RegistryPath); CreateFileTest(); return STATUS_SUCCESS; } ``` 需要注意的地方,初始化`UNICODE_STRING`时:文件路径要包含`\\??\\`,这是Windows内核中的一个符号链接,用于将内核模式的文件路径映射到用户模式的路径。 `ZwCreateFile`的`FILE_OPEN_IF`标志告诉内核,如果文件已经存在则打开它,如果文件不存在则创建它。 ### 驱动加载: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-153ecd290e3b62a733680ce87b14d33e4ee87f89.png) 在虚拟机上加载驱动程序,要打开测试模式。还有visual studio生成解决方案时要用x64生成,使用x86生成的sys在win10上可能会报签名错误。 ZwDeleteFile ------------ 删除指定文件。函数声明如下: ```c NTSTATUS ZwDeleteFile( _In_ POBJECT_ATTRIBUTES ObjectAttributes ); ``` 唯一的参数,指向一个 `OBJECT_ATTRIBUTES` 结构的指针。 如果要单独写一个删除文件的sys,步骤和`ZwCreateFile`是相同的,先初始化文件路径和`OBJECT_ATTRIBUTES`结构体,然后调用就好。这里演示,直接将ZwDeleteFile加到上述代码中。 ### 部分代码: ```c status = ZwDeleteFile(&objAttr); if (NT\_SUCCESS(status)) { DbgPrint("File deleted successfully.\\n"); } else { DbgPrint("Failed to delete file. Status: 0x%X\\n", status); } ``` 报未找到`ZwDeleteFile`的错误,可以添加一个`#include`,注意要放在ntddk前面。 ### 驱动加载: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-2689631d79c449ed42fb985caaeea51e4ea50e8a.png) 获取文件大小 ====== ZwQueryInformationFile ---------------------- 要对文件进行操作免不了要获取文件的大小,以便申请数据缓冲区。在用户层中,通过`GetFileSize`函数获取文件大小;在内核模式下,要获取文件的大小,可以通过调用 `ZwQueryInformationFile` 函数,配合 `FILE_STANDARD_INFORMATION` 结构来完成。`ZwQueryInformationFile` 函数允许我们查询文件的各种属性,其中 `FILE_STANDARD_INFORMATION` 结构包含了文件的大小信息。 函数定义: ```c NTSYSAPI NTSTATUS ZwQueryInformationFile( [in] HANDLE FileHandle, //文件或设备的句柄。 [out] PIO_STATUS_BLOCK IoStatusBlock, //指向一个 IO_STATUS_BLOCK 结构的指针,用于接收请求的状态信息和实际传输的字节数。 [out] PVOID FileInformation, //指向接收所请求信息的缓冲区。 [in] ULONG Length, //指定FileInformation缓冲区的大小(以字节为单位)。 [in] FILE_INFORMATION_CLASS FileInformationClass //一个枚举类型,指定要检索的文件信息类型。 ); ``` 其中字段`FileInformationClass`常用的类型包括: - `FileBasicInformation`: 基本文件信息,例如创建时间、上次访问时间。 - `FileStandardInformation`: 标准文件信息,包括文件大小、分配大小、文件是否为目录等。 - `FilePositionInformation`: 当前文件指针的位置。 `ZwQueryInformationFile`函数的返回值是一个`NTSTATUS`代码,表示操作的成功与否。 FILE\_STANDARD\_INFORMATION 结构 ------------------------------ `FILE_STANDARD_INFORMATION` 结构用于存储与文件或目录相关的标准信息。它是内核API`ZwQueryInformationFile`常使用的一个信息结构,当该函数的`FileInformationClass`参数设置为`FileStandarInformation`时,这个结构会返回标准文件信息,包括文件大小、分配大小等。 结构定义: ```c typedef struct _FILE_STANDARD_INFORMATION { LARGE_INTEGER AllocationSize; // 文件的分配大小 LARGE_INTEGER EndOfFile; // 实际大小 ULONG NumberOfLinks; // 指向文件的硬链接数 BOOLEAN DeletePending; // 文件是否已标记为删除 BOOLEAN Directory; // 是否是目录 } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; ``` 部分代码实现: ------- `ZwQueryInformationFile`的第一个参数是文件句柄,该句柄需要由`ZwCreateFile`或其它函数提供。因此同删除文件类似,可以对第一个具体代码进行修改,在创建或打开文件后,获取文件的大小。 变量要添加一行`FILE_STANDARD_INFORMATION fileInfo;` ```c if (NT_SUCCESS(status)) { DbgPrint("File opened successfully.\\n"); // 查询文件信息以获取文件大小 status = ZwQueryInformationFile(hFile, &ioStatusBlock, &fileInfo, sizeof(fileInfo), FileStandardInformation); if (NT_SUCCESS(status)) { // 输出文件大小信息 DbgPrint("File size: %llu bytes\\n", fileInfo.EndOfFile.QuadPart); } else { DbgPrint("Failed to query file information. Status: 0x%X\\n", status); } // 关闭文件句柄 ZwClose(hFile); } else { DbgPrint("Failed to open file. Status: 0x%X\\n", status); } ``` 驱动加载: ----- ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-b0ce4bf90cbaf8fc2b3dc582f617d1b25578f4c8.png) 读写文件 ==== 在内核模式下,读写文件操作通常使用 `ZwReadFile` 和 `ZwWriteFile` 函数。它们同用户模式的`ReadFile` 和 `WriteFile` 函数类似,但是在内核模式中使用需要处理一些额外的事项,例如对象属性、I/O状态块等。 ZwReadFile ZwWriteFile ---------------------- 函数定义: ```c NTSTATUS ZwReadFile( HANDLE FileHandle, //文件句柄。 HANDLE Event, //可选的事件句柄。 PIO_APC_ROUTINE ApcRoutine, //可选的APC例程,用于异步操作。 PVOID ApcContext, //可选的APC上下文 PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, //缓冲区。 ULONG Length, //要读取的字节数。 PLARGE_INTEGER ByteOffset, //从文件读取的位置。 PULONG Key //与文件关联的健,用于锁定操作。 ); ``` `ZwWriteFile`函数的各个参数同`ZwReadFile`相同,除了`Buffer`,对于读取操作来说,该参数是存储数据的缓冲区;对于写入操作来说,该缓冲区存储的是要被写入的数据。 这俩个函数同样需要一个文件句柄调用,该句柄一般由`ZwCreateFile`提供。 代码实现 ---- 先声明变量,`HANDLE`和`OBJECT_ATTRIBUTES`结构`IO_STATUS_BLOCK`都需要声明俩个,分别用于读取和写入操作。 ```c NTSTATUS status; HANDLE hFileRead, hFileWrite; OBJECT_ATTRIBUTES objAttrRead, objAttrWrite; IO_STATUS_BLOCK ioStatusBlockRead, ioStatusBlockWrite; UNICODE_STRING fileNameRead, fileNameWrite; LARGE_INTEGER byteOffset; CHAR buffer[1024]; // 读写缓冲区 ULONG bytesRead; ``` 同上一样需要先初始化`UNICODE_STRING`和`OBJETC_ATTRIBUTES`结构,然后调用`ZwCreateFile`打开一个文件,获得其文件句柄。再打开文件的基础下,再创建一个文件,之后从已打开的文件中读取数据写入到新的文件中。 关键代码: ```c // 读取文件数据并写入到新文件中 byteOffset.LowPart = byteOffset.HighPart = 0; while (NT\_SUCCESS(status)) { // 从读取文件中读取数据 status = ZwReadFile(hFileRead, NULL, NULL, NULL, &ioStatusBlockRead, buffer, sizeof(buffer), &byteOffset, NULL); if (status == STATUS\_END\_OF\_FILE) { DbgPrint("Reached end of file.\\n"); break; } if (NT\_SUCCESS(status)) { bytesRead = (ULONG)ioStatusBlockRead.Information; // 将读取的数据写入到写入文件中 status = ZwWriteFile(hFileWrite, NULL, NULL, NULL, &ioStatusBlockWrite, buffer, bytesRead, &byteOffset, NULL); if (!NT\_SUCCESS(status)) { DbgPrint("Failed to write data. Status: 0x%X\\n", status); break; } byteOffset.QuadPart += bytesRead; // 更新写入文件的偏移 } else { DbgPrint("Failed to read data. Status: 0x%X\\n", status); break; } ``` 驱动加载: ----- ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-f3cdd825c0f1076bbfa09d02b9c1475a99180e30.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-9a11232e05a030a7fe6bbc029a1144589b2dae31.png) 重命名文件 ===== ZwSetInformationFile -------------------- 在内核模式下,可以使用 `ZwSetInformationFile` 函数来重命名文件。相关的结构是`FILE_RENAME_INFORMATION`。 函数定义: ```c NTSTATUS ZwSetInformationFile( _In_ HANDLE FileHandle, _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_ PVOID FileInformation, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass ); ``` 重点参数 `FileInformationClass`,枚举值,指定要设置的文件信息的类型,包括: - `FileBasicInformation`:设置基本文件信息。 - `FileRenameInformation`:重命名文件。 - `FileDispositionInformation`:删除文件。 - `FilePositionInformation`:设置文件指针位置。 - `FileEndOfFileInformation`:设置文件结束位置(文件大小)。 FILE\_RENAME\_INFORMATION 结构 ---------------------------- 结构定义: ```c typedef struct _FILE_RENAME_INFORMATION { BOOLEAN ReplaceIfExists; HANDLE RootDirectory; ULONG FileNameLength; //文件名长度 WCHAR FileName[1]; //灵活的数组成员 } FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; ``` `ReplaceIfExists` 设置为 TRUE 表示如果给定名称的文件已经存在,则应使用给定文件替换该文件。设置为 FALSE 表示如果给定名称的文件已经存在,则重命名操作会失败。 该结构的第四个成员`FileName`是一个灵活的数组成员,实际大小取决于要重命名的目标文件名的长度,这意味着该`FILE_RENAME_INFORMATION`结构的大小再编译的时候是不固定的,而是运行时动态分配的。 分配的内存大小需要根据文件名长度计算:`sizeof(FILE_RENAME_INFORMATION) + FileNameLength` 这里就涉及到了函数`ExAllocatePoolWithTag`和`ExFreePoolWithTag`,分别用于内存分配和释放内存。 部分代码实现: ------- 实现步骤: - 照旧的初始化操作,`ZwCreateFile`返回一个文件句柄 - 初始化新的`UNICODE_STRING` - 计算所需的内存大小,用`ExAllocatePoolWithTag`分配 - 使用`RtlZeroMermory`函数将分配的内存空间清零 - 将`FILE_RENAME_INFORMATION`结构填满 - `ZwSetInformationFile`重命名文件 - 调用`ExFreePoolWithTag`释放内存 代码如下: ```c //变量声明: PFILE_RENAME_INFORMATION renameInfo; ULONG renameInfoSize; ...... //打开文件后 if (NT_SUCCESS(status)) { DbgPrint("File opened successfully.\n"); // 初始化新文件名路径 RtlInitUnicodeString(&newFileName, L"\\??\\C:\\syswork4\\NewFileName.txt"); // 分配重命名信息结构 renameInfoSize = sizeof(FILE_RENAME_INFORMATION) + newFileName.Length; renameInfo = (PFILE_RENAME_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, renameInfoSize, 'tag1'); if (renameInfo != NULL) { RtlZeroMemory(renameInfo, renameInfoSize); renameInfo->ReplaceIfExists = FALSE; renameInfo->RootDirectory = NULL; renameInfo->FileNameLength = newFileName.Length; RtlCopyMemory(renameInfo->FileName, newFileName.Buffer, newFileName.Length); // 重命名文件 status = ZwSetInformationFile(hFile, &ioStatusBlock, renameInfo, renameInfoSize, FileRenameInformation); if (NT_SUCCESS(status)) { DbgPrint("File renamed successfully.\n"); } else { DbgPrint("Failed to rename file. Status: 0x%X\n", status); } // 释放内存 ExFreePoolWithTag(renameInfo, 'tag1'); } else { DbgPrint("Failed to allocate memory for rename info.\n"); } // 关闭文件句柄 ZwClose(hFile); } else { DbgPrint("Failed to open file. Status: 0x%X\n", status); } ``` 驱动加载: ----- ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-71420e9bf1041c8fce7dca18a10c3e15df43d998.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-558914d2c399ad69bbe413dd67f5c84057aca12c.png) 文件遍历 ==== ZwQueryDirectoryFile -------------------- 在用户层中,是通过`FindFirstFile`和`FindNextFile`俩个函数实现文件遍历的,内核中主要由函数`ZwQueryDirectoryFile`来完成文件遍历的操作。确切的说,该函数是用于枚举文件目录内容,可以返回目录中文件的列表和每个文件的详细信息。 函数定义: ```c NTSTATUS ZwQueryDirectoryFile( HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass,//文件信息类型 BOOLEAN ReturnSingleEntry, //是否只返回一个条目(通常为 FALSE) PUNICODE_STRING FileName, //可选的文件名掩码(例如 *.txt),如果为 NULL 则返回目录中的所有文件。 BOOLEAN RestartScan //是否从目录开头重新扫描。 ); ``` 大多数上面都介绍完了类似的。 `FileInformationClass`指定返回的文件信息类型,常见的: - `FileDirectoryInformation`: 返回目录中的文件和子目录。 - `FileFullDirectoryInformation`: 类似于 `FileDirectoryInformation`,但提供更多信息 - `FileBothDirectoryInformation`: 返回目录项,并包括 8.3 格式的短文件名。 PFILE\_DIRECTORY\_INFORMATION 结构 -------------------------------- 该结构是用于存储目录项信息的数据结构,包含了有关目录中每个文件或子目录的信息,例如文件名、文件大小、时间戳等。 ```c typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; // 下一个条目的偏移量,以字节为单位。如果这是最后一个条目,则为零。 ULONG FileIndex; // 文件索引号,用于标识文件。 LARGE_INTEGER CreationTime; // 创建时间。 LARGE_INTEGER LastAccessTime; // 最后访问时间。 LARGE_INTEGER LastWriteTime; // 最后写入时间。 LARGE_INTEGER ChangeTime; // 最后修改时间。 LARGE_INTEGER EndOfFile; // 文件的结束位置,通常表示文件的大小。 LARGE_INTEGER AllocationSize; // 文件分配的大小,通常是磁盘上为文件预留的空间。 ULONG FileAttributes; // 文件的属性,例如只读、隐藏等。 ULONG FileNameLength; // 文件名的长度(以字节为单位,不包含 NULL 终止符)。 WCHAR FileName[1]; // 文件名(可变长度的字符串)。 } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; ``` 代码实现: ----- 实现和重命名文件的代码类似,初始化时要分配好内存,可以直接分配一块例如1024大小的缓冲区: ```c // 初始化文件信息缓冲区 PVOID buffer; ULONG bufferSize = 1024; buffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'tag1'); ``` 接着初始目录句柄,然后`ZwCreateFile`获取一个文件句柄。 循环调用`ZwQueryDirectoryFile`函数来获取目录中的信息,循环中还有一个do循环是用来遍历该函数获得的目录信息(即多个`FILE_DIRECTORY_INFORMATION` 结构),创建一个`UNICODE_STRING`来存储当前目录项的名称。 最后要释放内存。 ```c while (TRUE) { status = ZwQueryDirectoryFile(hDirectory, NULL, NULL, NULL, &ioStatusBlock, buffer,bufferSize,FileDirectoryInformation, FALSE, NULL, restartScan); if (status == STATUS_NO_MORE_FILES) { break; } else if (!NT_SUCCESS(status)) { DbgPrint("ZwQueryDirectoryFile failed. Status: 0x%X\n", status); break; } PFILE_DIRECTORY_INFORMATION fileInfo = (PFILE_DIRECTORY_INFORMATION)buffer; do { UNICODE_STRING currentFileName; currentFileName.Buffer = fileInfo->FileName; currentFileName.Length = (USHORT)fileInfo->FileNameLength; currentFileName.MaximumLength = (USHORT)fileInfo->FileNameLength; DbgPrint("Found file: %wZ\n", &currentFileName); fileInfo = (PFILE_DIRECTORY_INFORMATION)((PUCHAR)fileInfo + fileInfo->NextEntryOffset); } while (fileInfo->NextEntryOffset != 0); restartScan = FALSE; } ZwClose(hDirectory); } else { DbgPrint("Failed to open directory. Status: 0x%X\n", status); } ExFreePoolWithTag(buffer, 'tag1'); } ``` 驱动加载: ----- ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/08/attach-51784b1af5d71815f7a84638af2997ebf44e8d59.png) 内存分配 ==== ExAllocatePoolWithTag --------------------- ```c PVOID ExAllocatePoolWithTag( [in] __drv_strictTypeMatch(__drv_typeExpr)POOL_TYPE PoolType, [in] SIZE_T NumberOfBytes, [in] ULONG Tag ); ``` `PoolType`指定要分配的池内存类型,`NumberOfBytes`是要分配的内存数,`Tag`是用于分配内存的池标志。 关于池类型,常见的有: - `NonPagedPool`: 非分页池,分配的内存在物理内存中,不能被分页出内存。这种类型的内存在内核模式下是可以随时访问的。 - `PagedPool`: 分页池,分配的内存可以被分页到磁盘,只有在访问时才加载到内存。 - `NonPagedPoolNx`: 和 `NonPagedPool` 类似,但不允许执行代码。(安全) 不同的池类型对于**IRQL**也不同,在分配`NonPagedPool`类型的内存时,可以在任意IRQL下调用,但是分配`PagedPool`内存只能在`IRQL<=APC_LEVEL`时调用,如果在高IRQL时尝试分配分页内存,可能导致系统奔溃。 ExAllocatePool2 --------------- 在window10 2004中,`ExAllocatePoolWithTag`已经被弃用了。要编写适用于更高win版本的驱动程序,可以使用`ExAllocatePool2`。 同`ExAllocatePoolWithTag`相比,该函数除非指定了特别的标识,否则内存将被初始化为零;`ExAllocatePool2`的函数签名还包含了更多参数,新的函数签名为: ```c PVOID ExAllocatePool2( POOL_FLAGS PoolFlags, SIZE_T NumberOfBytes, ULONG Tag ); ``` `PoolFlags` 是一个新的参数,可以控制内存分配的一些额外特性,而 `ExAllocatePoolWithTag` 只支持通过 `POOL_TYPE` 指定内存池的类型。
发表于 2024-09-11 09:00:00
阅读 ( 753 )
分类:
安全开发
0 推荐
收藏
0 条评论
请先
登录
后评论
Sciurdae
12 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!