admin管理员组

文章数量:1530842

1. 引言

        在诸多的场景中(例如软件测试,软件安全研究等领域)经常需要分析在目标进程中        具体加载了哪些模块(DLL),以及所加载的模块的信息(如模块基地址,映射文件大小等)。获取这windows进程加载的模块信息,曾经有一个行之有效又很便捷的方法,使用windows提供PSAPI(psapi.dll,windows进程状态信息接口)提供的相关的接口就可以快捷的获取进程及进程加载的模块信息。有关PSAPI接口可以参考psapi.h或者微软的官方文档Psapi.h header - Win32 apps | Microsoft Docs。在这里笔者说一句与本文主题无关的题外话,研究windows内核尽量参考一手的windows官方文档,笔者有一个习惯,在对内核相关的研读时都是尽可能的阅读英文版的windows官方文档和使用windows的原版的工具(例如windbg),孜孜不倦,唯求其真。

 图1-PSAPI微软官方文档

        然而,在这个win64操作系统已经普及,32位程序尚未没落,依然大行其道的时代里,使用PSAPI获取进程中的模块信息已经不再那么有效。在win64的wow64环境中运行的32位程序获取64位进程中模块信息,就无比尴尬,因为windows的PSAPI是不支持的。

       我们可以在微软的官方文档中关于EnumProcessModules函数的描述(https://docs.microsoft/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules) 一探究竟,如图-2和图-3官方文档中所描述。其大意是说在64位的程序中枚举其它进程中的模块信息可以调用EnumProcessModuleEx接口。在wow64环境运行的32位程序中获取64位进程中的模块信息会返回错误代码299(GetLastError),其对应的错误信息为“仅完成部分的 ReadProcessMemory 或 WriteProcessMemory 请求。”。

 图-2 64位程序使用EnumProcessModulesEx接口

 图-3 EnumProcessModules不支持32位程序中枚举64位程序模块

        既然问题摆在我们面前,那么是否有破解之道呢?这正是笔者写这篇文章的目的,在本文中,会详细阐述一种更底层的,放之四海而皆准的,获取windows进程中模块的方法。该方法适用32为程序和64位程序枚举其它进程(包括32位和64位)中的模块。“授之于鱼不如授之于渔”,然而,笔者写本文的目的不仅仅是分享如何获取进程中的模块信息,而是尽量描述清楚解决该类问题的思路,希望能起到抛砖引玉的作用,能启发读者朋友解决类似的问题。由于笔者技术水平有限,如若有错误或不当欢迎各位业界同仁不吝赐教()。

       熟悉windows内核的朋友都知道,PEB(Process Environment Block,进程环境块)是一个重要的内核数据结构,windows的每个运行的进程中都维护一个PEB数据块其中记录着进程相关的各种信息。在PEB有一个PEB_LDR_DATA的数据,该数据记录着进程已经加载的模块信息。其实windows的PSAPI中的EnumProcessModules的底层实现也是通过PEB_LDE_DATA来遍历进程加载的模块的。“山穷水尽疑无路,柳岸花明又一村”,那么,问题的解决方案似乎近在咫尺了。

2. PEB数据结构

2.1 MSDN文档中的PEB数据结构

       PEB的数据结构会随着操作系统的版本而有差异,可以在微软官方文档(https://docs.microsoft/en-us/windows/win32/api/winternl)查看PEB极其相关的数据结构定义。

图4-PEB及相关的数据结构

    如上图所示的数据结构是在微软官方文档上摘录的,在PEB,PEB_LDR_DATA及LDR_DATA_TABLE_ENTRY结构体中有诸多的Reserved(保留)的数据项,表明这些数据项的定义可能会随着数据版本的变化而有差异,这些数据项尽量不要使用,如果在迫不得已的情况下使用,要做好操作系统不同版本间的兼容处理。

    在PEB结构体中的偏移0x0C处指向进程已加载的模块结构体PEB_LDR_DATA指针,关于PEB_LDR_DATA结构体在微软的官宣文档上是如此介绍的”Contains information about the loaded modules for the process.”。关于PEB结构体中其它数据项笔者在此不再赘述,有兴趣的朋友可自行查阅微软官宣文档,或者联系笔者做进一步的沟通交流()。

     在PEB_LDR_DATA结构体中,InMemoryOrderModuleList是一个双向链表节点(LIST_ENTRY结构体),其FLink和Blink均指向已加载的模块信息的结构体LDR_DATA_TABLE_ENTRY的头部。在微软的官方文档中关于InMemoryOrderModuleList是这样描述的“The head of a doubly-linked list that contains the loaded modules for the process. Each item in the list is a pointer to an LDR_DATA_TABLE_ENTRY structure. For more information, see Remarks.

本文标签: 内核详解模块几种方法进程