首页 > 代码库 > 【转】ZwQuerySystemInformation的分析(加载模块)

【转】ZwQuerySystemInformation的分析(加载模块)

原帖地址:http://www.mouseos.com/windows/kernel/ZwQuerySystemInformation.html

内核模块可以使有 ZwQuerySystemInformation() 函数来获取已加载模块的信息,这个 routine 的原型定义为:

NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation (
    __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __out_bcount_opt(SystemInformationLength) PVOID SystemInformation,
    __in ULONG SystemInformationLength,
    __out_opt PULONG ReturnLength
    );

参数说明:

  • SystemInformationClass: 提供查询信息的类型值。如果提供的是 SystemModuleInformation ,则查询系统已加载的模块信息。
  • SystemInformation:提供接收信息的 buffer,这个是可选项。
  • SystemInformationLength:提供接收 buffer 的长度值。
  • ReturnLength:提供一个 ULONG 值用来接收所需的长度值,这个是可选项。

由于 SystemInformation 是可选项。因此,最初发起 ZwQuerySystemInformation() 调用时,可以提供一个 NULL 值,并且在 ReturnLength 提供一个 ULONG 值。如下用法:

   ULONG RequiredLength = 0;                                       // 用来接收所需长度        ... ...        Status = ZwQuerySystemInformation(                                SystemModuleInformation,                // 查询模块信息                                NULL,                                   // 接收 buffer 为 NULL                                0,                                      // buffer 长度为 0                                &RequiredLength);                       // 接收所需的长度        if (NT_SUCCESS(Status) == STATUS_INFO_LENGTH_MISMATCH)        {                //                // 如果 buffer 长度不满足条件,则返回 STATUS_INFO_LENGTH_MISMATCH 状态                // caller 则根据返回的所需长度值分配内存                //                ModuleInformation = ExAllocatePoolWithTag(                                                NonPagedPool,                                                RequiredLength,                                                fnim);                if (ModuleInforation != NULL)                {                        //                        // 再次调用 ZwQuerySystemInformation()                        //                        Status = ZwQuerySystemInformation(                                                SystemModuleInformation,                                                ModuleInformation,                      // 提供接收 buffer                                                RequiredLength,                         // 所需长度                                                NULL);                                  // 不用接收返回长度                        ... ...                }        }

第1次 ZwQuerySystemInformation() 调用目的是探测所需的 buffer 空间,第2次调用是获取模块信息。

在内核模块里调用 ZwQuerySystemInformation() 函数,将通过系统调用转而调用 NtQuerySystemInformation() 函数。

NTSTATUSNtQuerySystemInformation (    __in SYSTEM_INFORMATION_CLASS SystemInformationClass,    __out_bcount_opt(SystemInformationLength) PVOID SystemInformation,    __in ULONG SystemInformationLength,    __out_opt PULONG ReturnLength    ){        ... ...        case SystemModuleInformation:            KeEnterCriticalRegion();            ExAcquireResourceExclusiveLite( &PsLoadedModuleResource, TRUE );            try {                Status = ExpQueryModuleInformation( &PsLoadedModuleList,                                                    &MmLoadedUserImageList,                                                    (PRTL_PROCESS_MODULES)SystemInformation,                                                    SystemInformationLength,                                                    ReturnLength                                                );            } except(EXCEPTION_EXECUTE_HANDLER) {                Status = GetExceptionCode();            }            ExReleaseResourceLite (&PsLoadedModuleResource);            KeLeaveCriticalRegion();            break;        ... ..}

NtQuerySystemInformation() 将调用内部的 ExpQueryModuleInformation() 函数来完成这个工作。实际上,它调用 ExpQueryModuleInformation() 来遍历 PsLoadedModuleList 与MmLoadedUserImageList 这两个链表。下面是 ExpQueryModuleInformation() 函数的实现:

NTSTATUSExpQueryModuleInformation (    IN PLIST_ENTRY LoadOrderListHead,    IN PLIST_ENTRY UserModeLoadOrderListHead,    OUT PRTL_PROCESS_MODULES ModuleInformation,    IN ULONG ModuleInformationLength,    OUT PULONG ReturnLength OPTIONAL    )/*++    input:        LoadOrderListHead - 提供 PsLoadedModuleList 链表        UserModeLoadOrderListHead - 提供 MmLoadedUserImageList 链表        ModuleInformation - caller 传过来接收信息的 buffer        ModuleInformationLength - buffer 长度        ReturnLength - 返回长度            output:        ModuleInformation - 用来接收模块信息        ReturnLength - 用来接收所需长度        如果提供的 buffer 长度不能满足所需长度时,routine 返回 STATUS_INFO_LENGTH_MISMATCH 状态--*/{    NTSTATUS Status;    ULONG RequiredLength;    PLIST_ENTRY Next;    PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;    PKLDR_DATA_TABLE_ENTRY LdrDataTableEntry;    ANSI_STRING AnsiString;    PCHAR s;    ULONG NumberOfModules;    NumberOfModules = 0;    Status = STATUS_SUCCESS;    RequiredLength = FIELD_OFFSET( RTL_PROCESS_MODULES, Modules );    ModuleInfo = &ModuleInformation->Modules[ 0 ];    //    // 遍历 kernel 的 PsLoadedModuleList 链表    //    Next = LoadOrderListHead->Flink;    while ( Next != LoadOrderListHead ) {        LdrDataTableEntry = CONTAINING_RECORD( Next,                                               KLDR_DATA_TABLE_ENTRY,                                               InLoadOrderLinks                                             );        //        // 每个 loaded 模块信息保存在一个 RTL_PROCESS_MODULE_INFORMATION 结构里        // RequiredLength 累加得到所有模块信息所需要的长度。        //        RequiredLength += sizeof( RTL_PROCESS_MODULE_INFORMATION );                //        // 如果提供的 buffer 长度小于必需的长度,记录为 STATUS_INFO_LENGTH_MISMATCH 状态。        // 否则保存模块信息        //        if (ModuleInformationLength < RequiredLength) {            Status = STATUS_INFO_LENGTH_MISMATCH;        }        else {                        //            // 记录模块信息            //            ModuleInfo->MappedBase = NULL;            ModuleInfo->ImageBase = LdrDataTableEntry->DllBase;            ModuleInfo->ImageSize = LdrDataTableEntry->SizeOfImage;            ModuleInfo->Flags = LdrDataTableEntry->Flags;            ModuleInfo->LoadCount = LdrDataTableEntry->LoadCount;                        //            // LoadOrderIndex 指示模块在 PsLoadedModuleList 链中的 index 值            // InitOrderIndex 指示 index 值从 0 开始。            //            ModuleInfo->LoadOrderIndex = (USHORT)(NumberOfModules);                 ModuleInfo->InitOrderIndex = 0;                        //                            // RTL_PROCESS_MODULE_INFORMATION 结构模块信息中的 Name 为 ANSI 串,            // 而 PKLDR_DATA_TABLE_ENTRY 结构中的 Name 为 UNICODE 串。因此,将 UNICODE 串转换为 ANSI 串。            //            AnsiString.Buffer = (PCHAR) ModuleInfo->FullPathName;            AnsiString.Length = 0;            AnsiString.MaximumLength = sizeof( ModuleInfo->FullPathName );            Status = RtlUnicodeStringToAnsiString( &AnsiString,                                          &LdrDataTableEntry->FullDllName,                                          FALSE                                        );            if (NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW)) {                //                // 从 FullPathName 属部开始向前查找 ‘\‘ 字符,                // 用来分割出模块的 BaseName                //                s = AnsiString.Buffer + AnsiString.Length;                while (s > AnsiString.Buffer && *--s) {                    if (*s == (UCHAR)OBJ_NAME_PATH_SEPARATOR) {                        s += 1;                        break;                    }                }                ModuleInfo->OffsetToFileName = (USHORT)(s - AnsiString.Buffer);         // BaseName 在 FullPathName 的偏移量            } else {                //                // 如果 UNICODE 串转换为 ANSI 串失败,则将 FullPathName 清空。                //                ModuleInfo->FullPathName[0] = \0;                ModuleInfo->OffsetToFileName = 0;            }                        ModuleInfo += 1;        }        NumberOfModules += 1;        Next = Next->Flink;    }    //    // 如果提供了 MmLoadedUserImageList,则在 MmLoadedUserImageList 链表上做相同的操作    //    if (ARGUMENT_PRESENT( UserModeLoadOrderListHead )) {        Next = UserModeLoadOrderListHead->Flink;        while ( Next != UserModeLoadOrderListHead ) {            LdrDataTableEntry = CONTAINING_RECORD( Next,                                                   KLDR_DATA_TABLE_ENTRY,                                                   InLoadOrderLinks                                                 );            RequiredLength += sizeof( RTL_PROCESS_MODULE_INFORMATION );            if (ModuleInformationLength < RequiredLength) {                Status = STATUS_INFO_LENGTH_MISMATCH;            }            else {                ModuleInfo->MappedBase = NULL;                ModuleInfo->ImageBase = LdrDataTableEntry->DllBase;                ModuleInfo->ImageSize = LdrDataTableEntry->SizeOfImage;                ModuleInfo->Flags = LdrDataTableEntry->Flags;                ModuleInfo->LoadCount = LdrDataTableEntry->LoadCount;                ModuleInfo->LoadOrderIndex = (USHORT)(NumberOfModules);                ModuleInfo->InitOrderIndex = ModuleInfo->LoadOrderIndex;                                    AnsiString.Buffer = (PCHAR) ModuleInfo->FullPathName;                AnsiString.Length = 0;                AnsiString.MaximumLength = sizeof( ModuleInfo->FullPathName );                Status = RtlUnicodeStringToAnsiString( &AnsiString,                                              &LdrDataTableEntry->FullDllName,                                              FALSE                                            );                if (NT_SUCCESS (Status) || (Status == STATUS_BUFFER_OVERFLOW)) {                    s = AnsiString.Buffer + AnsiString.Length;                    while (s > AnsiString.Buffer && *--s) {                        if (*s == (UCHAR)OBJ_NAME_PATH_SEPARATOR) {                            s += 1;                            break;                        }                    }                    ModuleInfo->OffsetToFileName = (USHORT)(s - AnsiString.Buffer);                } else {                    ModuleInfo->FullPathName[0] = \0;                    ModuleInfo->OffsetToFileName = 0;                }                ModuleInfo += 1;            }            NumberOfModules += 1;            Next = Next->Flink;        }    }        //    // 如果需要返回长度,则返回所需长度    //    if (ARGUMENT_PRESENT(ReturnLength)) {        *ReturnLength = RequiredLength;    }    if (ModuleInformationLength >= FIELD_OFFSET( RTL_PROCESS_MODULES, Modules )) {        ModuleInformation->NumberOfModules = NumberOfModules;    } else {        Status = STATUS_INFO_LENGTH_MISMATCH;    }    return Status;}

这个函数的实现很简单,就是在 PsLoadedModuleList 与 MmLoadedUserImageList 链表里读取模块的信息,写入提供的 buffer 里。

接收的 buffer 实际上是一个 RTL_PROCESS_MODULES 结构,它的尾部是一个可变的 PRTL_PROCESS_MODULE_INFORMATION 结构数组,用来保存若干个实际的模块信息。

版权所有 ©2009 - 2014 邓志

 

【转】ZwQuerySystemInformation的分析(加载模块)