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層結構也做了一些概述,這些結構是解析用戶空間進程堆的關鍵。