RtlImageDirectoryEntryToData()
PVOID
RtlImageDirectoryEntryToData(
PVOID BaseAddress,
BOOLEAN MappedAsImage,
USHORT Directory,
PULONG Size);
给定内核模块的基址和数据目录中条目的索引, RtlImageDirectoryEntryToData() 返回虚拟地址和目录条目的大小。数据目录索引是 ntimage.h 中IMAGE_DIRECTORY_ENTRY_xxx常量之一。在解析 NT 图像头以进入导入描述符表、导入地址表、导出表等时,此函数非常有用。如果驱动程序只需要获取 PE 映像标头,则可以改为调用 RtlImageNtHeader[Ex]()。MSDN 上记录了此函数 ImageDirectoryEntryToData()的 Win32 版本。
RtlPcToFile[Name|Path|Header]()
NTSTATUS
RtlPcToFileName(
PVOID PcValue,
PUNICODE_STRING FileName);
NTSTATUS
RtlPcToFilePath(
PVOID PcValue,
PUNICODE_STRING FilePath);
PVOID
RtlPcToFileHeader (
PVOID PcValue,
PVOID *BaseOfImage);
RtlPcToFileName() 和 RtlPcToFilePath() 使驱动程序能够从指向内存中模块的 PE 映像的地址分别获取内核模块的名称和路径。RtlPcToFileName() 和 RtlPcToFilePath() 演练定位在 PsLoadedModuleList 的 KLDR_DATA_TABLE_ENTRY 结构列表,并确定给定地址 (Pc) 是否属于 KLDR_DATA_TABLE_ENTRY 中的盘区。Base 和 KLDR_DATA_TABLE_ENTRY。基地 + KLDR_DATA_TABLE_ENTRY。SizeOfImage,如果是这样,则返回有关模块的信息。RtlPcToFileName() 返回驱动程序表单KLDR_DATA_TABLE_ENTRY的名称。BaseDllName,而 RtlPcToFilePath() 返回 KLDR_DATA_TABLE_ENTRY 驱动程序的完整路径。FullDllName 的 DlName 中。另一方面,RtlPcToFileHeader() 使用 PsInvertedFunctionTable 查找电脑,并返回 ImageBase 并将其写入 BaseOfImage 指向的位置。
RtlQueryModuleInformation()
typedef struct _RTL_MODULE_BASIC_INFO {
PVOID ImageBase;
} RTL_MODULE_BASIC_INFO, *PRTL_MODULE_BASIC_INFO;
typedef struct _RTL_MODULE_EXTENDED_INFO {
RTL_MODULE_BASIC_INFO BasicInfo;
ULONG ImageSize;
USHORT FileNameOffset;
UCHAR FullPathName[256];
} RTL_MODULE_EXTENDED_INFO, * RTL_MODULE_EXTENDED_INFO;
NTSTATUS
RtlQueryModuleInformation (
PULONG BufferSize,
ULONG UnitSize,
PVOID Buffer);
驱动程序通常需要获取内核中的模块列表及其名称和基址。虽然驱动程序可以通过遍历定位在 PsLoadedModuleList 的 KLDR_DATA_TABLE_ENTRY 结构的链接列表来获取此列表,但 RtlQueryModuleInformation() 提供了一种更安全的方法,可以使用本机 API ZwQuerySystemInformation (SystemModuleInformation) 来实现此目的。Buffer 参数可以是 RTL_MODULE_BASIC_INFO 或 RTL_MODULE_EXTENDED_INFO 结构类型的数组。根据 Buffer 参数中传递的结构类型,UnitSize 参数可以是 sizeof(RTL_MODULE_BASIC_INFO) 或 sizeof(RTL_MODULE_EXTENDED_INFO)。如果 Buffer 为 NULL,则函数将返回 BufferSize 中所需的缓冲区长度,调用方可以使用该长度来分配缓冲区,并使用此缓冲区再次调用 RtlQueryModuleInformation() 。
FsRtlIsNameIn[UnUpcased]Expression()
BOOLEAN
FsRtlIsNameInExpression (
PUNICODE_STRING Expression,
PUNICODE_STRING Name,
BOOLEAN IgnoreCase,
PWCH UpcaseTable );
BOOLEAN
FsRtlIsNameInUnUpcasedExpression(
PUNICODE_STRING Expression,
PUNICODE_STRING Name,
BOOLEAN IgnoreCase,
PWCH UpcaseTable );
此内核函数 RtlIsNameInExpression() 导出为 FsRtlIsNameInExpression() 是 NTOSKRNL.exe 中为数不多的能够将字符串与包含通配符的模式进行比较的导出之一。此函数可用于将文件路径、注册表路径、对象路径或简单的字符串与包含通配符的给定模式进行匹配。通配符 ?和 *,还支持 MSDOS 短文件名匹配字符 <、> 和 ”。例如,表达式 1<TXT 将与名称 1.2.3.4.5.6.txt 匹配。如果 IgnoreCase 为 TRUE,则 RtlIsNameInExpression() 期望 Expression 为大写,而 FsRtlIsNameInUnUpcasedExpression() 则没有此要求。RtlIsNameInExpression() 尝试将 Name 参数中提供的字符串转换为大写,这可能会失败并引发调用方必须处理的异常。这两个函数都记录在案,它们的原型和字符 DOS_DOT、DOS_QM 和 DOS_STAR 的定义都可以在 ntifs.h 中找到。
FsRtlGetFileNameInformation()
NTSTATUS
FsRtlGetFileNameInformation(
PFILE_OBJECT FileObject,
ULONG NameOptions,
PUNICODE_STRING FileName,
PVOID *FileNameInformation );
VOID
FsRtlReleaseFileNameInformation (
PVOID FileNameInformation );
FsRtlGetFileNameInformation() 在给定文件对象的情况下检索文件的名称。NameOptions 参数可以是 fltkernel.h 中定义的FLT_FILE_NAME_OPTIONS常量之一。文件名在 FileName 中返回,FileNameInformation 接收指向结构体 FLT_FILE_NAME_INFORMATION 的指针,该结构也在 fltkernel.h 中定义。FileName.Buffer 和 FileNameInformation.Name 指向表示文件完整路径的同一字符串。FsRtlGetFileNameInformation() 仅填充 FLT_FILE_NAME_INFORMATION 结构的 Name 和 Volume 字段。要填充其余字段,即将文件路径拆分为其组件,驱动程序可以使用 MSDN 上记录的FLTMGR.sys函数 FltParseFileNameInformation()。FsRtlGetFileNameInformation() 在内部调用 FltMgr!FsRtlGetFileNameInformation(),该信息由 FltMgr.sys NTOSKRNL.exe注册。FsRtlGetFileNameInformation() 分配FLT_FILE_NAME_INFORMATION结构,该结构可通过调用 FsRtlReleaseFileNameInformation() 来释放。NTOSKRNL.exe 内部有多个组件以及使用这些功能的内核模式驱动程序(例如 CI.dll)。
RtlFindExportedRoutineByName()
PVOID
RtlFindExportedRoutineByName (
PVOID DllBase,
PCHAR RoutineName);
内核为驱动程序提供了记录的函数 MmGetSystemRoutineAddress(),以便根据导出的名称检索指向导出的指针。MmGetSystemRoutineAddress() 确实有一个限制,因为它适用于 NTOSKRNL.exe 和 HAL.dll 中的导出。MmGetSystemRoutineAddress() 在内部调用未记录的函数 RtlFindExportedRoutineByName(),其基址NTOSKRNL.exe存储在 nt 中!PsNtosImageBase 的基址为 HAL.dll 在 nt!PsHalImageBase 的事实证明,RtlFindExportedRoutineByName() 也由 NTOSKRNL.exe 导出,因此驱动程序可以直接调用以获取任何内核模块的导出,就像应用程序在任何 DLL 上调用 GetProcAddress() 一样。MmGetSystemRoutineAddress() 和 RtlFindExportedRoutineByName() 之间的显著区别是,前者将 RoutineName 作为 Unicode 字符串接收,而后者需要以 NULL 结尾的 ASCII 字符串。
SeLocateProcessImageName()
NTSTATUS
SeLocateProcessImageName (
PEPROCESS Process,
PUNICODE_STRING *ImageFileName);
给定指向 EPROCESS 结构的指针,此函数返回该进程的 PE 文件的完整路径,该路径在 EPROCESS 中可用。SeAuditProcessCreationInfo.ImageFileName.Name. 在 ImageFileName 参数中分配并返回包含完整图像路径的 UNICODE_STRING 结构。调用方负责使用 ExFreePool() 释放结构。读取 PE 文件以计算图像哈希或根据 PE 文件中的元数据实施策略时,此路径非常有用。
RtlDuplicateUnicodeString()
NTSTATUS
RtlDuplicateUnicodeString (
ULONG Flags,
PCUNICODE_STRING StringIn,
PUNICODE_STRING StringOut );
驱动程序始终可以使用 ExAllocatePollWithTag() 为 Unicode 字符串分配内存,并使用 RtlCopyUnicodeString()将现有 Unicode 字符串复制到该内存,以创建源 Unicode 字符串的深层副本。RtlDuplicateUnicodeString()提供了一种在单个函数中执行这两个步骤的便捷方法,其中包含一些自定义选项来控制目标 Unicode 字符串的创建方式。调用方可以通过在 Flags 参数中指定 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 来请求以 NULL 结尾的新字符串。如果源字符串为空,调用方可以通过指定 RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING 和 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 标志来请求创建以 NULL 结尾的空字符串。StringOut.Buffer 处的 Widechar 缓冲区是使用 PagedPool 中的 ExAllocatePoolWithTag() 和标记 Strg 分配的。调用方可以使用 RtlFreeUnicodeString() 释放此字符串。尽管 RtlDuplicateUnicodeString() 和上述标志在 WDK 标头 ntifs.h 中定义,但它们并未在 MSDN 上记录。
ObReferenceObjectByName()
NTSTATUS
ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext,
PVOID *Object );
给定对象管理器命名空间中对象的路径,ObReferenceObjectByName() 返回指向该对象的指针。如果调用方将 AccessState 参数指定为 NULL,则 ObReferenceObjectByName() 将构建自己的ACCESS_STATE。OBJECT_TYPE 参数是必需的,并且必须与正在检索的对象匹配,否则此函数将失败并显示 STATUS_OBJECT_TYPE_MISMATCH。例如,如果 ObjectName 为 \Driver\NULL,则 ObjectType 参数应为 IoDriverObjectType。最常用的对象类型变量(如 IoDriverObjectType)是从 NTOSKRNL.exe 导出的,因此驱动程序可以直接链接到这些导出,以访问对象类型。ObReferenceObjectByName()的大多数其他参数都在记录的函数变体中描述,例如 ObReferenceObjectByHandle() 和 ObReferenceObjectByPointer()。ObReferenceObjectByName() 引用 Object 中返回的对象,调用者必须通过调用 ObDereferenceObject() 来删除此引用。
IsEqualGUID()
BOOLEAN
IsEqualGUID (
REFGUID rguid1,
REFGUID rguid2);
有相当多的内核 API 允许驱动程序注册接收 GUID 作为参数之一的回调。这些 GUID 可以标识系统定义的实体,例如 PNP 事件、电源事件、设备接口、WFP 层标识符等。驱动程序可以调用 IsEqualGUID() 将回调中显示的 GUID 与常量 GUID 进行比较,以确定它们是否相同。可能需要进行此类比较的回调示例包括 IoRegisterPlugPlayNotification() 安装的设备通知回调和由 PoRegisterPowerSettingCallback()安装的电源设置更改通知回调。