首页 > 其他 > 详细

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

时间:2015-01-05 12:24:34      阅读:461      评论:0      收藏:0      [点我收藏+]

原帖地址: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() 函数。

NTSTATUS
NtQuerySystemInformation (
    __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() 函数的实现:

NTSTATUS
ExpQueryModuleInformation (
    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的分析(加载模块)

原文:http://www.cnblogs.com/Lthis/p/4202987.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!