Linux内存取证:解析用户空间进程堆

存储 存储软件
在取证分析中,对内存的分析通常是还原事件的重要步骤。以前对内存工作的分析主要集中于那些驻留在内核空间中的信息,如进程列表、网络连接等,特别是在Microsoft Windows操作系统上,但是这项工作主要关注的是Linux用户空间进程,因为它们可能还包含有价值的调查信息。

 前言

在取证分析中,对内存的分析通常是还原事件的重要步骤。以前对内存工作的分析主要集中于那些驻留在内核空间中的信息,如进程列表、网络连接等,特别是在Microsoft Windows操作系统上,但是这项工作主要关注的是Linux用户空间进程,因为它们可能还包含有价值的调查信息。由于许多进程数据位于堆中,所以这项工作首先集中在对Glibc堆实现的分析,以及如何将堆相关信息存储在使用此实现的Linux进程的虚拟内存中。

到目前为止,从内存取证的角度来看,堆通常被认为是一个大的内聚内存区域,这使得在内部识别相关信息变得相当困难。我们为基于分析结果的内存分析框架Rekall引入了Python类,并允许访问堆中包含的所有块及其元信息。

此外,基于这个类,我们已经开发了6个插件,支持研究人员分析用户空间进程,其中有4个插件提供了常用的分析功能,比如在块中查找信息或引用,并将块转储到单独的文件中以进行进一步的调查。目前这些插件已被用于对用户空间进程中的堆内数据结构进行逆向工程,你可以点此处,查看这些插件如何简化整个分析进程。其余两个插件则是这些用户空间进程分析的结果,而且还负责提取zsh shell的命令历史记录和密码管理器KeePassX的密码输入信息。

由于内存表示运行中的系统的当前状态,它包含有关浏览器历史记录、输入命令、运行中的网络连接、加载的驱动程序以及活动进程的信息,因此我们才可以对以前的活动进行详细的分析。虽然这些信息大部分位于内核空间,可以使用Rekall和Volatility等各种现有解决框架进行取证,但也有很多信息位于用户空间,例如,用户空间进程的堆通常是各种数据的丰富来源,而根据具体应用程序的不同,它可能包含凭证、IP地址、DNS名称或命令历史。但是,在Linux环境中,这些信息还不是容易提取的。

为了有效和可靠的识别和提取这些信息,研究人员需要查看与该进程相同或至少类似的堆的视图,以此来了解数据所在的位置,比如什么样的数据类型会存储在什么位置、哪些数据占用多少内存量。否则,取证人员对数据结构不理解,只能摸着石头过河,因为所有信息都位于堆内,只能将堆作为一个大内存区域来处理,这样效率就太低了。

为什么解析用户空间进程堆

在Linux进程的上下文中,以前对用户空间分析的重点仅限于在整个进程内存或整个堆或堆栈中搜索特定模式。例如,为了重建Linux bash shell的命令历史记录,Rekall的bash 7插件在堆中搜索一个主题标签,后跟一个字符串格式的Unix时间戳(例如#1471572423)。但是,如果感兴趣的信息没有以易于检测的方式进行标记,则简单的模式匹配将失败。

当研究者在内存中标识某个字符串并试图标识对该字符串的引用时,即使存在间接引用,这种模式匹配搜索也可能失败。如果字符串是结构或对象的一部分,并且不在开头,而感兴趣的指针直接引用结构而不是字符串,则可能出现这种情况。为了能够找到这些引用,你就必须知道该结构的开头,这就需要了解其字段的大小以及字符串在结构中的位置。但是,这些细节通常在"黑箱分析"中不可用。

在本文中,我们实现解析用户空间进程堆的步骤分为5步:

1.通过分析Glibc堆的实现,总结一些可以让调查人员能够执行手动堆分析的信息,其中重要的信息是关于堆结构的排列方式以及它们通常位于内存中的位置;

2.证明一些块可能隐藏在内存中的某个位置,为此我们提出了一种检索它们的算法;

3.这些算法已被用于开发一个名为HeapAnalysis的Python类,它可用于实现特定的堆分析插件。基于这个类,我们开发了支持取证人员分析堆及其块的插件;

4.我们会解释如何通过应用这些插件收集相关信息来分析用户空间进程的数据,类似于我们对Windows用户空间进程的分析;

5.对结果的进一步分析是两个插件,***个插件从zsh shell(版本5.2)进程的堆中收集所有已执行的命令,第二个插件从堆中提取所有可检索密码条目的标题、用户名、URL和注释字段的密码管理器KeePassX(版本0.4.3)。

HeapAnalysis类和所有提到的插件都支持×86和×64架构。

除了本文之外,我们还发布了一份技术报告,其中包含更多技术细节和代码列表。

本文的文章目录结构:

1.Glibc分析章节详细介绍了使用Glibc堆实现的用户空间进程的堆;

2.在Plugin实现章节中,我们对开发的堆分析插件进行了概述;

3.插件的评估章节;

4.实践应用章节提供了详细的应用分析;

5.总结章节;

对用户空间进程堆进行解析的研究历史

到目前为止,对用户空间应用的分析还没有得到足够的重视,这正是我们此次研究的动机,另外关于该主题的文献目前还非常少,Linux内存取证arena的现有文献主要涉及内核主题,如Urrea, 2006, Case et al., 2010 和 Ligh et al. (2014). 。

少数例外是Leppert(2012) 和Macht(2013)的研究,他们都专注于基于Linux的移动设备的Android操作系统,并分析应用程序和它们的堆数据。但是,他们的分析主要集中在堆中包含的序列化Java对象上,而不是堆对象的管理方式上。除此之外,还有对插件cmdscan 和bash 的研究,它们分别从Windows的cmd和Linux的bash shell中提取命令历史记录。然而,这些插件的使用都是基于以下的事实:在这些情况下,只需将堆视为一个大内存区域就可以识别信息了,另一项相关工作是对记事本堆的分析。据我们所知,这是唯一使用任何堆细节的示例,而且与大多数先前的工作一样,它也与Windows相关。

如果不考虑取证,仅对堆进行的基础研究,特别是对Linux进程堆及其管理方式的研究,仅有Ferguson(2007) 对Glibc的堆实现进行了研究。然而,以前的研究更多的关注于如何利用堆,因此目前还没有足够的信息来让我们在内存取证场景中从堆中收集所有相关信息。

Cohen(2015)的研究是***个通过一组Windows操作系统分析工具来解决这一问题的项目。虽然Windows的堆实现与来自Glibc的堆实现之间存在一些相似之处,例如,在两种情况下,分配的块前面都有一个至少包含块大小的结构,但它们在细节上有所不同。

Glibc分析

在本章节中,我们从内存取证的角度介绍了我们对Glibc堆实现的最重要分析结果,更详细的信息可以从我们的技术报告中获得。

不同的堆实现

我们的堆实现对象是Glibc 2.23版,它基于Wolfram Gloger的ptmalloc2 ,不过除此之外还有其他各种堆实现,其中大多数都是在某个操作系统或应用程序的上下文中使用的。应用程序开发人员还可以决定是实现自己的堆还是使用其他现有堆实现,例如Firefox等Mozilla产品。然而,这样的进程可能无法使用本文介绍的信息或工具进行分析。

Glibc堆概述

本节会介绍Glibc堆实现中使用到的最重要的对象和结构。图1显示了一个正在运行的进程的堆布局,重点关注一下各个元素之间的引用。从***层和最重要的层级开始,块包含实际的用户或进程数据。这些数据是通过malloc调用显式分配的,或者通过类的实例化上下文中的新调用隐式分配的。这些块位于特定的内存区域,除了分配表示当前正在使用的块之外,还会分配一些正在使用但将来不再使用的释放块。当一个块被释放时,块本身或至少其数据在大多数场景中仍保持与之前相同的位置,并且其数据(除了稍后要解释的一些修改之外)也不会被删除或覆盖。

Glibc堆概述

***层级是arena,Ptmalloc2通过几种数据结构来进行管理,主要有arena,heap,chunk三种层级。

Arena

arena对于32位系统,数量最多为核心数量2倍,64位则最多为核心数量8倍,可以用来保证多线程的堆空间分配的高效性。主要存储了较高层次的一些信息。有一个main_arena,是由主线程创建的,thread_arena则为各线程创建的,当arena满了之后就不再创建而是与其他arena共享一个arena,方法为依次给各个arena上锁(查看是否有其他线程正在使用该arena),如果上锁成功(没有其他线程正在使用),则使用该arena,之后一直使用这个arena,如果无法使用则阻塞等待。简而言之,arena是由malloc_state结构描述的,从本质上讲,它是属于一个或多个线程的堆空间,而每个arena都有自己的内存区域,其中包含来自相关线程的已分配和释放的块。包含在Glibc库中的arena被称为main_arena,因为它被***个或主要线程使用。虽然arena没有直接链接到每个内存区域或分配的块(参见图1),但还有其他连接,如指向释放块的指针,下一个arena和顶部块。这个顶部块表示给定arena的剩余可用空间,用于创建新的块并位于arena的末端。

heap_info

arena下面的一层是heap_info结构,heap的等级就比arena要低一些了,一个arena可以有多个heap,也是存储了堆相关的信息。尽管它们的名称类似,但它们并不描述进程的整个堆或与线程关联的堆,而只描述它们所属的映射内存区域(由vm_area_struct结构描述)的那部分。更具体地说,每个映射到arena(main_arena除外)的内存区域至少在内存区域的开头都包含heap_info结构的一个实例,它保存了该内存区域中当前堆部分的大小。

除了大小之外,每个heap_info结构都包含一个指向相关arena(malloc_state结构)的指针和一个指向同一arena内的前一个heap_info结构的指针(请参见图3)。这样,所有这些都链接在一起。arena指针存储在于ar_ptr构建以及对prev构建中的前一个heap_info的引用中。与arena相比,heap_info结构不是循环链接,因为***个heap_info的prev字段是null。

不包括arena和heap_info区域的是mmapping块,如图1所示,没有从MMAPPED块到任何其他结构或从堆结构到它们的链接。这些块通常是在分配请求超过给定阈值(通常为128*1024字节)时创建的,在这种情况下,块不包括在main heap或属于另一个arena的任何内存区域中,但是要求操作系统仅为该块(通过mmap API调用)提供专用内存区域,以在此区域中放置块。当mmap API调用以页面的形式返回内存空间时, MMAPPED块的最小大小为一个页面(至少为4096字节或其倍数),并且可以被一个页面大小整除。释放MMAPPED块时,它正在分配的整个内存空间将从进程空间中删除并返回给操作系统。

Chunk(块)

chunk为分配给用户的内存的一个单位,每当我们分配一段内存的时候其实就是分配得到了一个chunk,我们就可以在chunk当中进行一定的操作了。不过为了进行动态分配,chunk本身也有一些数据(元数据),是用来表示其分配等等的数据。

本文我们对解析用户空间进程堆的动机和历史,做了一个简要的概述。另外,我们Glibc堆的3层结构也做了一些概述,这些结构是解析用户空间进程堆的关键。

责任编辑:武晓燕 来源: 嘶吼专业版
相关推荐

2021-05-26 14:05:44

开源工具Linux内存取证

2018-05-18 08:43:27

Linux内存空间

2024-01-08 08:42:43

2012-05-03 08:27:20

Linux进程

2022-12-26 14:41:38

Linux内存

2017-03-01 10:45:39

Linux驱动技术内存申请

2012-05-21 17:02:19

Linux审计

2021-01-08 05:59:39

Linux应用程序Linux系统

2009-09-07 09:20:34

2021-03-17 21:34:44

Linux内存管理

2020-02-04 13:50:09

Linux进程内存使用

2022-02-18 00:15:58

Linux指令CPU

2021-07-06 21:30:06

Linux进程通信

2018-11-01 10:59:52

Linux内存进程

2020-12-09 05:25:23

Linux内存进程

2018-01-12 14:35:00

Linux进程共享内存

2010-02-04 09:26:34

Linux vmsta

2021-11-29 20:44:31

Linux内存进程

2011-10-28 15:50:45

C程序

2009-12-07 09:31:23

Linux系统调用表地址
点赞
收藏

51CTO技术栈公众号