初探内核下的文件管理技术:内核API
字数 2223 2025-08-20 18:18:17

Windows内核下的文件管理技术:内核API实现

前言

Windows内核下的Rootkit开发技术学习。用户数据通常以文件形式存储在本地磁盘上,Rootkit等恶意软件需要操作文件的功能(增、删、查、改)。文件管理主要有三种方式:

  1. 基于导出的内核API直接操作文件
  2. 通过构造I/O请求包(IRP)发送IRP操作文件
  3. 根据文件系统格式(NTFS)解析硬盘二进制数据

本文重点介绍第一种方式——使用内核API实现文件操作。

内核API概述

内核API是一组从内核组件中输出的函数,主要实现于:

  • 内核本身模块(NtOskrnl.exe)
  • 其他模块(如hal.dll)

常见内核API前缀:

  • Ex: 通用执行体函数(如ExAllocatePool)
  • Ke: 通用内核函数(如KeAcquireSpinLock)
  • Io: I/O管理器函数(如IoCompleteRequest)
  • Zw: 原生API包装(如ZwCreateFile)

Zw与Nt系列函数

  • Nt系列函数(如NtCreateFile)是Windows内核提供的原生API,直接与内核模式交互
  • Zw系列函数是原生API的包装,实现几乎相同但通过不同入口点进入系统
  • 内核模式调用Nt函数会检查调用上下文(用户/内核模式)
  • 调用Zw函数则直接设置PreviousModeKernelMode

关键数据结构

OBJECT_ATTRIBUTES结构

用于描述操作系统对象(文件、设备、事件等)属性,传递给如ZwCreateFileZwOpenKey等API。

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;

常用属性标志:

  • OBJ_CASE_INSENSITIVE: 名称比较不区分大小写
  • OBJ_KERNEL_HANDLE: 句柄仅内核模式可访问
  • OBJ_OPENIF: 对象存在则打开,否则不失败
  • OBJ_PERMANENT: 创建永久对象(不自动删除)

InitializeObjectAttributes宏

用于初始化OBJECT_ATTRIBUTES结构体:

#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; \
}

文件操作API详解

1. 文件创建(ZwCreateFile)

用于创建或打开文件对象,也可用于目录操作。

NTSTATUS ZwCreateFile(
    PHANDLE FileHandle,              // 接收文件句柄的指针
    ACCESS_MASK DesiredAccess,       // 访问类型(//执行等)
    POBJECT_ATTRIBUTES ObjectAttributes, // 已初始化的OBJECT_ATTRIBUTES
    PIO_STATUS_BLOCK IoStatusBlock,  // I/O状态块指针
    PLARGE_INTEGER AllocationSize,   // 文件分配大小
    ULONG FileAttributes,            // 文件属性
    ULONG ShareAccess,               // 共享模式
    ULONG CreateDisposition,         // 创建方式
    ULONG CreateOptions,             // 创建选项
    PVOID EaBuffer,                  // 扩展属性缓冲区(通常NULL)
    ULONG EaLength                   // EaBuffer长度
);

内核模式创建文件步骤

  1. 初始化UNICODE_STRING结构(文件路径)
  2. 设置OBJECT_ATTRIBUTES结构体
  3. 调用ZwCreateFile
  4. 操作完成后用ZwClose关闭句柄

示例代码:

void CreateFileTest() {
    NTSTATUS status;
    HANDLE hFile;
    OBJECT_ATTRIBUTES objAttr;
    IO_STATUS_BLOCK ioStatusBlock;
    UNICODE_STRING fileName;
    
    // 初始化UNICODE_STRING
    RtlInitUnicodeString(&fileName, L"\\??\\C:\\path\\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/opened successfully.\n");
        ZwClose(hFile);
    } else {
        DbgPrint("Failed to create/open file. Status: 0x%X\n", status);
    }
}

注意事项:

  • 文件路径需包含\\??\\前缀(内核符号链接)
  • FILE_OPEN_IF标志:存在则打开,不存在则创建
  • 驱动需在测试模式下加载,VS需用x64生成

2. 文件删除(ZwDeleteFile)

删除指定文件:

NTSTATUS ZwDeleteFile(
    _In_ POBJECT_ATTRIBUTES ObjectAttributes
);

3. 获取文件信息(ZwQueryInformationFile)

获取文件大小等信息:

NTSTATUS ZwQueryInformationFile(
    [in] HANDLE FileHandle,                  // 文件句柄
    [out] PIO_STATUS_BLOCK IoStatusBlock,    // I/O状态块
    [out] PVOID FileInformation,             // 接收信息的缓冲区
    [in] ULONG Length,                       // 缓冲区大小
    [in] FILE_INFORMATION_CLASS FileInformationClass // 信息类型
);

常用信息类型:

  • FileBasicInformation: 基本文件信息
  • FileStandardInformation: 标准文件信息(含大小)
  • FilePositionInformation: 文件指针位置

FILE_STANDARD_INFORMATION结构

typedef struct _FILE_STANDARD_INFORMATION {
    LARGE_INTEGER AllocationSize;  // 分配大小
    LARGE_INTEGER EndOfFile;       // 实际大小
    ULONG NumberOfLinks;           // 硬链接数
    BOOLEAN DeletePending;         // 是否标记为删除
    BOOLEAN Directory;             // 是否为目录
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;

4. 文件读写(ZwReadFile/ZwWriteFile)

NTSTATUS ZwReadFile(
    HANDLE FileHandle,             // 文件句柄
    HANDLE Event,                  // 可选事件句柄
    PIO_APC_ROUTINE ApcRoutine,    // 可选APC例程
    PVOID ApcContext,              // 可选APC上下文
    PIO_STATUS_BLOCK IoStatusBlock,// I/O状态块
    PVOID Buffer,                  // 数据缓冲区(读取)
    ULONG Length,                  // 要读取的字节数
    PLARGE_INTEGER ByteOffset,     // 读取位置
    PULONG Key                     // 关联键(通常NULL)
);

NTSTATUS ZwWriteFile(
    // 参数同ZwReadFile,但Buffer为要写入的数据
);

5. 文件重命名(ZwSetInformationFile)

NTSTATUS ZwSetInformationFile(
    _In_ HANDLE FileHandle,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_ PVOID FileInformation,
    _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass
);

使用FileRenameInformation信息类型和FILE_RENAME_INFORMATION结构:

typedef struct _FILE_RENAME_INFORMATION {
    BOOLEAN ReplaceIfExists;  // 存在是否替换
    HANDLE RootDirectory;      // 根目录句柄
    ULONG FileNameLength;      // 文件名长度
    WCHAR FileName[1];         // 文件名(灵活数组成员)
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;

内存分配:

  • 使用ExAllocatePoolWithTag动态分配内存
  • 内存大小计算:sizeof(FILE_RENAME_INFORMATION) + FileNameLength
  • 完成后用ExFreePoolWithTag释放

6. 文件遍历(ZwQueryDirectoryFile)

枚举目录内容:

NTSTATUS ZwQueryDirectoryFile(
    HANDLE FileHandle,             // 目录句柄
    HANDLE Event,                  // 可选事件
    PIO_APC_ROUTINE ApcRoutine,    // 可选APC例程
    PVOID ApcContext,              // 可选APC上下文
    PIO_STATUS_BLOCK IoStatusBlock,// I/O状态块
    PVOID FileInformation,         // 文件信息缓冲区
    ULONG Length,                  // 缓冲区长度
    FILE_INFORMATION_CLASS FileInformationClass, // 信息类型
    BOOLEAN ReturnSingleEntry,     // 是否只返回单个条目
    PUNICODE_STRING FileName,      // 文件名掩码(*.txt)
    BOOLEAN RestartScan            // 是否从头重新扫描
);

常用信息类型:

  • FileDirectoryInformation: 基本目录信息
  • FileFullDirectoryInformation: 扩展目录信息
  • FileBothDirectoryInformation: 包含短文件名

FILE_DIRECTORY_INFORMATION结构

typedef struct _FILE_DIRECTORY_INFORMATION {
    ULONG NextEntryOffset;        // 下一条目偏移(0表示最后)
    ULONG FileIndex;              // 文件索引号
    LARGE_INTEGER CreationTime;   // 创建时间
    LARGE_INTEGER LastAccessTime; // 最后访问时间
    LARGE_INTEGER LastWriteTime;  // 最后写入时间
    LARGE_INTEGER ChangeTime;     // 最后修改时间
    LARGE_INTEGER EndOfFile;      // 文件大小
    LARGE_INTEGER AllocationSize; // 分配大小
    ULONG FileAttributes;         // 文件属性
    ULONG FileNameLength;         // 文件名长度
    WCHAR FileName[1];            // 文件名(灵活数组成员)
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;

内存分配API

ExAllocatePoolWithTag

PVOID ExAllocatePoolWithTag(
    [in] POOL_TYPE PoolType,    // 池类型
    [in] SIZE_T NumberOfBytes,  // 字节数
    [in] ULONG Tag             // 池标记
);

常用池类型:

  • NonPagedPool: 非分页池(任意IRQL可访问)
  • PagedPool: 分页池(IRQL<=APC_LEVEL)
  • NonPagedPoolNx: 非分页池(禁止执行代码)

ExAllocatePool2 (Win10 2004+)

新版本替代函数:

PVOID ExAllocatePool2(
    POOL_FLAGS PoolFlags,  // 池标志
    SIZE_T NumberOfBytes,  // 字节数
    ULONG Tag              // 池标记
);

特点:

  • 默认初始化内存为零
  • 更丰富的参数控制

总结

本文详细介绍了Windows内核模式下使用内核API进行文件操作的技术,包括:

  1. 文件创建/打开(ZwCreateFile)
  2. 文件删除(ZwDeleteFile)
  3. 文件信息查询(ZwQueryInformationFile)
  4. 文件读写(ZwReadFile/ZwWriteFile)
  5. 文件重命名(ZwSetInformationFile)
  6. 目录遍历(ZwQueryDirectoryFile)

这些技术是Rootkit和内核驱动开发中文件操作的基础,理解这些API和数据结构对于开发内核级文件管理功能至关重要。

Windows内核下的文件管理技术:内核API实现 前言 Windows内核下的Rootkit开发技术学习。用户数据通常以文件形式存储在本地磁盘上,Rootkit等恶意软件需要操作文件的功能(增、删、查、改)。文件管理主要有三种方式: 基于导出的内核API直接操作文件 通过构造I/O请求包(IRP)发送IRP操作文件 根据文件系统格式(NTFS)解析硬盘二进制数据 本文重点介绍第一种方式——使用内核API实现文件操作。 内核API概述 内核API是一组从内核组件中输出的函数,主要实现于: 内核本身模块(NtOskrnl.exe) 其他模块(如hal.dll) 常见内核API前缀: Ex : 通用执行体函数(如 ExAllocatePool ) Ke : 通用内核函数(如 KeAcquireSpinLock ) Io : I/O管理器函数(如 IoCompleteRequest ) Zw : 原生API包装(如 ZwCreateFile ) Zw与Nt系列函数 Nt 系列函数(如 NtCreateFile )是Windows内核提供的原生API,直接与内核模式交互 Zw 系列函数是原生API的包装,实现几乎相同但通过不同入口点进入系统 内核模式调用 Nt 函数会检查调用上下文(用户/内核模式) 调用 Zw 函数则直接设置 PreviousMode 为 KernelMode 关键数据结构 OBJECT_ ATTRIBUTES结构 用于描述操作系统对象(文件、设备、事件等)属性,传递给如 ZwCreateFile 、 ZwOpenKey 等API。 常用属性标志: OBJ_CASE_INSENSITIVE : 名称比较不区分大小写 OBJ_KERNEL_HANDLE : 句柄仅内核模式可访问 OBJ_OPENIF : 对象存在则打开,否则不失败 OBJ_PERMANENT : 创建永久对象(不自动删除) InitializeObjectAttributes宏 用于初始化 OBJECT_ATTRIBUTES 结构体: 文件操作API详解 1. 文件创建(ZwCreateFile) 用于创建或打开文件对象,也可用于目录操作。 内核模式创建文件步骤 初始化 UNICODE_STRING 结构(文件路径) 设置 OBJECT_ATTRIBUTES 结构体 调用 ZwCreateFile 操作完成后用 ZwClose 关闭句柄 示例代码: 注意事项: 文件路径需包含 \\??\\ 前缀(内核符号链接) FILE_OPEN_IF 标志:存在则打开,不存在则创建 驱动需在测试模式下加载,VS需用x64生成 2. 文件删除(ZwDeleteFile) 删除指定文件: 3. 获取文件信息(ZwQueryInformationFile) 获取文件大小等信息: 常用信息类型: FileBasicInformation : 基本文件信息 FileStandardInformation : 标准文件信息(含大小) FilePositionInformation : 文件指针位置 FILE_ STANDARD_ INFORMATION结构 4. 文件读写(ZwReadFile/ZwWriteFile) 5. 文件重命名(ZwSetInformationFile) 使用 FileRenameInformation 信息类型和 FILE_RENAME_INFORMATION 结构: 内存分配: 使用 ExAllocatePoolWithTag 动态分配内存 内存大小计算: sizeof(FILE_RENAME_INFORMATION) + FileNameLength 完成后用 ExFreePoolWithTag 释放 6. 文件遍历(ZwQueryDirectoryFile) 枚举目录内容: 常用信息类型: FileDirectoryInformation : 基本目录信息 FileFullDirectoryInformation : 扩展目录信息 FileBothDirectoryInformation : 包含短文件名 FILE_ DIRECTORY_ INFORMATION结构 内存分配API ExAllocatePoolWithTag 常用池类型: NonPagedPool : 非分页池(任意IRQL可访问) PagedPool : 分页池(IRQL<=APC_ LEVEL) NonPagedPoolNx : 非分页池(禁止执行代码) ExAllocatePool2 (Win10 2004+) 新版本替代函数: 特点: 默认初始化内存为零 更丰富的参数控制 总结 本文详细介绍了Windows内核模式下使用内核API进行文件操作的技术,包括: 文件创建/打开( ZwCreateFile ) 文件删除( ZwDeleteFile ) 文件信息查询( ZwQueryInformationFile ) 文件读写( ZwReadFile / ZwWriteFile ) 文件重命名( ZwSetInformationFile ) 目录遍历( ZwQueryDirectoryFile ) 这些技术是Rootkit和内核驱动开发中文件操作的基础,理解这些API和数据结构对于开发内核级文件管理功能至关重要。