翟繼強 徐曉 陳攀 楊海陸






摘要:為解決64位Windows環境中,現有工具針對含有惡意進程的轉儲文件中沒有堆棧幀指針和調試符號時,構建的堆棧取證會產生漏報問題和沒有元數據時構建的堆棧取證會產生錯報問題,提出了從內存轉儲構建堆棧跟蹤方法。從內存轉儲中檢索目標進程的用戶上下文,確定堆棧跟蹤的起始點,然后基于異常處理的元數據展開。如果元數據不可用,使用基于指令流的驗證方法生成等效數據。基于框架Volatility實現了相應插件,實驗表明,方法不依賴堆棧幀指針和調試符號,利用元數據可獲取更加完整的堆棧跟蹤;沒有元數據時,基于指令流的驗證可以極大地提高取證的精確性。
關鍵詞:內存取證;Windows堆棧;元數據;指令流;返回地址
DOI:10.15938/j.jhust.2021.05.007
中圖分類號:TP319 文獻標志碼:A 文章編號:1007-2683(2021)05-0051-09
0 引言
隨著網絡和信息技術的迅速發展和深入應用,以及軟件漏洞的不斷出現,網絡攻擊和網絡犯罪問題層出不窮,造成了嚴重的網絡信息安全威脅[1]。堆棧跟蹤是一種分析惡意軟件的內存取證方法[2]。堆棧是操作系統用于存儲關于每個程序的活動子程序信息的結構,存儲的信息包括從調用者程序傳遞到被調用程序的參數,子程序的局部變量,子程序完成后的返回的地址。堆棧跟蹤可以提取主機中程序的返回地址,從而得到調用的函數和已執行代碼的歷史操作,這些將成為取證分析人員發現事件起因的重要線索[3]。
在Windows x86環境下,可以利用傳統技術,即利用幀指針進行堆棧跟蹤[4]。由于Microsoft采用“x64軟件約定(x64 software conventions)”,根據這些約定,64位Windows中進程所有空間在函數開始處就預分配好,堆棧中不構造幀指針鏈[5]。因此基于幀指針的堆棧追蹤技術對64位應用程序無效。針對這一問題,Pshoul[6]提出基于掃描的方案并將方法實現為開源框架Volatility插件malthfind。但其方法在掃描堆棧時會錯誤地將堆棧中子函數的返回地址誤識別為最終返回地址,從而造成誤報;對內存轉儲文件中相關展開數據結構利用不充分,導致結果不完整。Otsuki等[7]在2018年DFRWS(digitalforensic research workshop,數字取證研討會)上闡述了掃描驗證的堆棧重構方法并將方法實現為開源框架 Rekall插件malinfind,提高了堆棧重構的準確性。但其方法在利用元數據時未曾考慮RIP(registerinstruction pointer,寄存器指令指針)位于函數pro-log與epilog的情況,會導致在利用相關結構時發生信息匹配錯位,從而造成結果的誤報;其不利用元數據時,未考慮相關RSP(register stack pointer,寄存器基址指針)的偏移量且程序基本執行語句僅順序執行一次,未將堆棧中人棧數據進行完全出棧,造成結果漏報問題。微軟(Microsoft)發布的調試工具WinDbg可以在x86或x64環境下找到一定的返回地址,但是WinDbg強烈依賴于調試符號(程序數據庫文件,也稱為符號文件),同時可能錯誤地檢測堆棧區域中的普通函數指針作為返回地址。而惡意軟件大多數代碼區既沒有調試符號也沒有任何元數據[8]。
為了解決上述問題,本文提出從64位Windows的內存轉儲構建堆棧跟蹤的方法,并基于Volatility內存取證框架設計和實現相應的插件。通過實驗測試評估了方法的精確性和完整性,確認可以更準確地對使用或不使用元數據的64位進程構建堆棧跟蹤。
1 Windows x64堆棧
1.1 Windows x64堆棧分配
Windows x64環境中的運行進程分為兩種類型:在本機環境中執行的64位進程;在WOW64層上執行的32位進程。內存就像是所有正在運行的進程的游戲表,要成為游戲的一部分,應該將數據帶到此表中。該數據包括但不限于進程的可執行代碼,進程訪問的數據文件,通過Web瀏覽器訪問的URL,用戶名和密碼。Windows操作系統利用虛擬內存方法管理系統內存。進程可用的所有虛擬地址集稱為虛擬地址空間,其可分為兩個范圍:用戶空間與系統空間。其中的用戶空間是用戶模式處理特定數據和用戶模式動態鏈接庫(dynamic link library,DLL)文件映射到的地址范圍;系統空間(又稱內核空間)是操作系統所在的地址范圍,只能由內核模式代碼訪問。Windows內存管理器使用虛擬地址描述符(virtual address descriptors,VADs)來描述進程在分配時使用的內存范圍[9]。當進程使用Virutal-Alloc函數分配內存時,內存管理器會在VAD樹中創建一個條目。VAD樹本身是一個自平衡二叉樹,在任何給定節點,可以在左子樹中找到低于當前節點包含的內存地址,在右邊可以找到較高的內存地址。動態內存分配使程序能夠在運行時分配內存空間,一種方法是內存池分配,其不一定是連續的,Windows以及許多其他操作系統提供可分配給進程的分頁(可以駐留在物理內存中,也可以存儲到磁盤)和非分頁內存池(駐留在物理內存中);分配動態內存的另一種方法是堆棧分配,以先進后出的方式添加和移除數據[10]。
1.2 調用堆棧和堆棧幀
調用堆棧是操作系統用于存儲關于每個程序的活動子進程信息的結構,此結構也稱為執行堆棧,控制堆棧。堆棧存儲的信息包括從主進程傳遞到被調用的子進程的參數,子進程中所需的局部變量和子進程執行結束后的返回地址。函數的堆棧幀是ESP(extended stack pointer,擴展棧指針)寄存器和EBP(extended base pointer,擴展基址指針或幀指針)寄存器中包含的地址之間的內存塊,其存儲在調用堆棧上,包含保存和恢復進程狀態所需的所有信息。
根據編譯器優化配置,可以生成不使用幀指針的函數,如在集成開發環境Visual Studio下,使用FPO(frame pointer omission,幀指針缺省)選項的函數調用堆棧中不存在EBP鏈。雖然在Windows XPSP(Service Pack,補丁包)2之后發布的所有Win-dows x86平臺都禁用了動態鏈接庫和可執行文件的FPO,但惡意軟件或第三方二進制文件可能會使用FPO。此外,還有一些函數不對所有Windows的官方二進制文件使用幀指針,例如完全內部函數。Windows x64平臺上的64位應用程序通常符合x64軟件約定,它們不會構造堆棧中的幀指針鏈,需要程序數據庫文件或其他信息才能來構建這些應用程序的堆棧跟蹤。
由于返回地址通常存儲在進程空間中,可以使用其虛擬地址進行訪問,而虛擬地址又存儲在堆棧中,從而有必要分析堆棧。
2 堆棧重構方法分析
實現堆棧跟蹤基本上需要識別堆棧所有者線程使用的活動堆棧的實際范圍,以及定位堆棧中每個返回地址的位置[11]。
2.1 具有幀指針的函數的堆棧跟蹤
Windows使用內部數據結構來管理內存中的操作和對象。Windows的EPROCESS塊包含KPRO-LESS結構,也被稱為PCB(processing control block,進程控制塊),它包含有關進程的調度信息的內核結構。KPROCESS塊中偏移量0x0050處“Thread-ListHead”的LIST_ENTRY數組的每個項包含KTHREAD結構,KTHREAD是包含有關線程調度信息的內核表示[12]。這個結構主要包含:StackLimit、KernelStack、StackBase,這些信息分別提供堆棧的最大值、堆棧指針的當前值以及堆棧的起始地址。
具有幀指針的函數通常將當前幀指針寄存器的值推送到堆棧,對應操作為push ebp。然后,該函數用堆棧指針當前的值更新它,對應操作為mov ebp,esp。由于幀指針寄存器始終指向局部緩沖區和先前存儲的幀指針之間的位置,局部變量和參數與EBP的偏移量保持不變,因此函數中的代碼能夠通過幀指針寄存器訪問存儲在堆棧中的局部變量或其函數參數。
幀指針EBP指向前一個幀指針,函數的返回地址存儲在堆棧中的幀指針之前。基于這些指針,可以構造一個在當時執行狀態下的調用堆棧,形成一個調用函數和被調用函數的正確順序列表。
2.2 沒有幀指針的堆棧跟蹤
對于Windows堆棧中不構造幀指針鏈的函數,需要Microsoft的調式符號文件或一些額外的信息才能有效地遍歷堆棧,該額外信息以編譯器和鏈接器生成的展開信息(元數據)的形式出現。若元數據不可用,也有不依賴于幀指針的方法可掃描堆棧的返回地址并基于找到的返回地址構建調用堆棧[13]。其從線程環境塊(thread environment block,TEB)收集StackBase和StackLimit作為堆棧的最高(棧底為棧的最高地址)和最低地址(棧頂為棧的最低地址),并掃描它們之間的內存區域來查找滿足以下條件的地址:指向可執行內存區的地址;指向前一條被調用指令的所在地址的地址[14]。通過了解這些值并將它們轉換為物理地址,可以到達與每個堆棧對應的區域。
2.3 現有技術存在的問題
Windows x64已經成為主流的操作系統,即沒有幀指針的函數已經非常普遍,且惡意軟件和已發布的應用程序通常沒有調試符號和元數據,此時只能使用基于掃描的技術在內存上下文中構建堆棧跟蹤。但是,這些技術會錯誤檢測到普通函數指針并將堆棧中之前函數的返回地址作為本次的返回地址。另外,雖然許多現有方法通過Windows的TEB識別堆棧區域,但可以通過讓堆棧指針指向任意內存區域來使其作為堆棧。所以必須確定用作堆棧的區域,建立一種更精確的方法來構建堆棧跟蹤。
3 Windows x64堆棧跟蹤
3.1 堆棧跟蹤方法
3.1.1 獲取x64進程的用戶上下文
操作系統通常在事件(如上下文切換、系統調用和中斷)發生時存儲上下文。Windows體系結構中,用于保存上下文的結構為TrapFrame的成員KTRAP FRAME[15]。ETHREAD的Tcb結構中KTHREAD對象保存指向線程的最后一個KTRAPFRAME結構的指針[16]。ETHREAD.Tcb.Previous-Mode成員表明TrapFrame成員是具有用戶上下文還是內核上下文。因此本文提出從ETHREAD.Tcb.TrapFrame獲取目標進程中線程的最后一個上下文,這個上下文包含寄存器的最后一個值,例如指向堆棧頂部的RSP寄存器,以及指向下一條指令的RIP寄存器。即使RSP指向StackLimit和StackBase之間,也可以將堆棧的實際范圍視為RSP寄存器指向位置的連續頁面。Windows x64通常在發生用戶到內核模式轉換時將所有寄存器保存到KTRAPFRAME。現有的在Windows x86環境下獲得最后EBP值的方法也采用這種結構。
3.1.2 利用元數據的堆棧重構
每個分配堆棧空間、調用其他函數、保存非易失性寄存器或使用展開信息進行異常處理的函數都有prolog、主體和epilog。prolog將執行函數前的參數寄存器保存在其本地地址中,將非易失性寄存器壓入堆棧,為本地和臨時對象分配堆棧,并可建立幀指針。epilog代碼在函數的每個出口,其可將堆棧修整為固定的分配大小,釋放堆棧,通過從堆棧中彈出保存的值來恢復非易失性寄存器。默認情況下,PE(portable executable,可移植可執行)32+文件具有用于結構化異常處理(structured exception handling,SEH)時堆棧展開的元數據,在內存取證中,可以基于加載到內存中的這些展開信息來獲得堆棧跟蹤。
在x64軟件約定下用于棧展開的結構為RUNT-IME_FUNCTION,其位于Exception Directory的.pdata部分[17]。該結構包含3個相對虛擬地址(relative virtual addresses,RVAs),兩個相對虛擬地址指向函數的prolog和epilog,第三個指向函數的UNWIND_INFO結構。UNWIND_INFO結構包含一個UNWIND_CODE結構數組,每個UNWIND_CODE表示函數中的一個操作,它會影響寄存器RSP或其他非易失性寄存器。其中,UWOP_SET_FPREG操作是通過將寄存器設置為當前RSP的某個偏移量來建立幀指針寄存器;SAVE_NONVOL操作是將非易失性寄存器的值進行保存;UWOP_ALLOC_SMALL和UWOP_ALLOC_LARGE操作是在堆棧上分配空間。如果函數使用幀指針在x64軟件約定下動態分配堆棧空間,則函數的UNWIND_CODE包含UWOP_SET_FPREG代碼以指定用作幀指針的寄存器。圖1展示了這些結構的基本關系。
在內存轉儲上解釋這些數據結構,并展開堆棧來獲取先前的返回地址,恢復寄存器的值,步驟如下:
1)通過進程用戶空間的VADs的進程樹獲得當前RIP指向的區域的基地址,同時檢查基地址中的PE文件標頭的簽名來確認當前RIP是否指向PE32+映像文件內部[18]。
2)獲取Exception Directory的RVA并計算該目錄的虛擬地址,可得到RUNTIME_FUNCTION結構,其范圍包含Exception Directory中的當前RIP。
3)判斷RIP位置,此時有3種情況:①檢查從RIP開始的代碼流,若該代碼流可以與epilog的結尾部分匹配,則確定它在epilog中。此時控制權離開了該函數,從UNWIND_CODE數組中向后掃描偏移量小于或等于RIP的偏移量的第一個條目,向后展開,并在處理每條指令時更新上下文記錄。在該處理之后,重復步驟2)。②從函數開始到RIP的距離小于或等于展開信息中編碼的prolog大小,則RIP在prolog內。此時指令尚未進入該函數,該函數沒有與此異常相關聯的數據。通過從UNWINDCODE數組中向前掃描偏移量小于或等于RIP的偏移量的第一個條目,向前展開,然后取消該展開代碼數組中其余項的數據,重復步驟2)。③如果RIP不在prolog或epilog之內,則直接利用展開數據數組信息,進入步驟4)。
4)計算已獲取的RUNTIME_FUNCTION的Un-windData成員指向的UNWIND_INFO的虛擬地址。
5)展開RSP信息并根據UNWIND_INFO包含的每個UNWIND_CODE恢復其他寄存器。
6)如果展開信息鏈接到其他信息,則該子方法獲取到鏈接的展開信息并從步驟2)重復直到得到最后一個鏈接的展開信息。
7)在完成所有展開操作后從堆棧中彈出前一個返回地址,并將RIP設置為該值。
編譯器和鏈接器負責在構建時將可展開信息放入可執行文件中,同時對于動態編譯的代碼也有展開信息,否則在異常之后就無法遍歷堆棧或展開堆棧。Windows的相關API應用程序編程接口支持動態生成代碼的異常處理,生成的展開信息以一個鏈接列表為根,其中頭部位于ntdll!RtlpDynamicFunc-tionTable。即如果在步驟1)中未找到PE簽名,也可以在該表格中獲得展開信息。在這種情況下,用以下過程替換步驟2):通過引用VAD樹獲取nt-dll.dll的基址,并計算RtlpDynamicFunctionTable的虛擬地址,可得到RUNTIME FUNCTION結構(此方式與從Exception Directory獲取的方式相同)。
3.1.3 基于指令流的堆棧重構
當在代碼區的頂部檢測不到PE頭同時在Rtlp-DynamicFunctionTable中沒有找到元數據,或者在獲取內存轉儲時,元數據尚未加載到內存中,則采用此方法。
當調用發生時,函數指令的執行路徑可到達當前RIP。首先使用基于掃描的技術,將檢測到所有可能的上一個返回地址標記下來作為候選地址。然后,根據調用過程形成的指令流,找到抵達當前RIP的那條指令,則該指令對應的返回地址即是正確的返回地址。以圖2為例說明該子方法的過程:
1)方法從當前RSP掃描返回地址。在圖2所示的情況下,找到Pointerl和Pointer2。
2)根據Pointer1和Pointer2獲得調用指令,分析每個調用指令要調用的函數內部的控制流。
3)找到抵達當前RIP的執行路徑,并在指向該函數的調用指令之后標記這個候選地址,路徑作為當前路徑。在圖2的情況下,Pointer2即是正確的返回地址。
4)將確認的返回地址設置為當前RIP的值,并將RSP遞增8,同時設置為指向下一個返回地址的虛擬地址。
5)重復3.1.2中步驟1)。
驗證返回地址過程如圖2中所示。
在步驟2)中,如果調用是通過寄存器的間接調用,如調用RBX(Register B Extended,擴展B寄存器),則不能對這些候選地址執行基于指令流的驗證。但是如果其他候選地址沒有到達RIP的路徑,則確定在當前RIP中候選地址為返回地址。
3.2 基于Volatility插件實現
Volatility框架是用于內存取證的開源軟件,并提供可擴展的API[19-20]。根據以上分析,利用Win-dows內核數據將本方法實現為Volatility內存分析框架的插件trastack。首先利用已經實現的插件枚舉內存轉儲文件包含的進程對象,進一步根據內核數據結構EPROCESS的Wow64Process成員檢查進程是否是64位進程,從而根據進程的不同類型選擇不同的堆棧跟蹤方法。如果Wow64Process成員持有NULL指針,則目標進程為x64進程,然后獲取進程所對應線程的KTRAP_FRAME結構得到上下文內容與堆棧范圍,利用VAD樹得到虛擬地址。由元數據是否存在分別進行堆棧返回地址定位。
插件核心代碼如下:
trapframe=thread.Tcb.TrapFrame.dereference_as(“_KTRAP_FRAME”)
if bits64 trapframe:
address_space=thread.owning_process().get_process_address_space()
if:thread_callstack.set_bits(“64”)
current_ebp=trapframe.Rbp
thread_callstack.add_callstack_item
(CallstackItem(trapframe.Rip,trapframe.Rip))
thread_callstack.eip=True
while current_ebp:
if address_space.is_valid_address(current_ebp)and address_space.is_valid_address(current_ebp+pointer_size):
call_address=struct.unpack(unpack_size,ad-dress_space.zread(current_ebp+pointer_size,point-er_size))[0]
current_ebp=struct.unpack(unpack_size,ad-dress_space.zread(current_ebp,pointer_size))[0]
if len(thread_callstack.callstack)>1:
if current_ebp==thread_callstack.callstack[-1].frame address:
break
if call_address:thread_callstack.callstack.add_
callstack_item(CallstackItem(call_
address,current_ebp))
thread_callstack.callstack=parse_callstack_i-tems address
(thread,thread_callstack.callstack,config)
return thread callstack
插件執行流程如圖3所示:
4 測試與分析
4.1 測試方法
針對普通用戶進程和系統進程使用物理機進行測試;針對攻擊進程,在VMware虛擬機監視器上運行的虛擬機(Virtual Machine,VM)中獲取內存轉儲。默認情況下,物理機和VM均具有4GB的內存,安裝了Windows 7 x64 SP1。用本文實現的volatility插件trastack與WinDbg(WinDbg對Microsoft提供的調試符號完全支持)、文[5]方法malthfind和文[6]方法malinfind執行結果作比較。每個樣本分別進行10次實驗,去掉最優結果和最劣結果后取均值得出最終結果。
4.2 評價指標
本方法可以重構堆棧中的返回地址,如果輸入的測試數據中原本的函數返回地址被正確識別時,標記為TP(True Positive);輸入的測試數據中子函數的返回地址被識別為最終返回地址(誤報)時,標記為FP(False Positive);輸入的測試數據中原本函數的返回地址被識別為普通子函數返回地址(漏報)時,標記為FN(False Negative);輸入的測試數據中子函數返回地址被正確識別時,標記為TN(TrueNegative)。則在本文中相關評價指標精確率(Preci-sion)與召回率(Recall)如下:
4.3 實驗結果分析
4.3.1 有元數據的進程測試
在此測試中,對Windows x64環境中運行有元數據的notepad.exe、explorer.exe、WINWORD.EXE、wininit.exe等進程的轉儲文件進行分析,分別執行插件trastack、WinDbg、malthfind和malinfind進行回溯棧。結果如表1所示。
結果表明,對于有元數據、支持Windows官方調試符號的典型可執行文件notepad.exe、explor-er.exe、WINWORD.EXE、wininit.exe,微軟的WinD-bg可以全面并且無誤地對堆棧進行回溯;在無法利用Windows官方調試符號的其它方法中,本文所實現的插件trastack利用元數據對堆棧展開回溯,執行結果達到了與WinDbg一樣的精確率與召回率,同時用時小于WinDbg的1/2,一定程度上提高了效率;文[5]malthfind方法由于掃描時會錯誤地將堆棧中未及時出棧的不相關返回地址識別為函數最終返回地址,造成結果精確率低同時其方法中沒有充分利用展開代碼數組(元數據)信息,造成結果缺失;文[6]malinfind方法在利用元數據時,未曾考慮RIP在函數prolog與epilog內的情況,導致展開代碼數組信息利用出錯從而造成返回地址覆蓋不全面且有誤報,使結果不具有足夠的可信性。
至此,確認本方法可以正確獲取在Windows x64上有元數據的進程的堆棧跟蹤。
4.3.2 無元數據的進程測試
在此測試中,為了模擬從沒有元數據的代碼區域獲取堆棧跟蹤的情況,WinDbg在執行k命令之前卸載了user32.dll和notepad.exe、explorer.exe、WIN-WORD.EXE、wininit.exe的調試符號,同時插件強制使用不利用元數據的子方法。依然利用上述進程,插件的調試消息表明user32.dll的.pdata部分不存在,此時元數據不可用[21],插件trastack依然能獲得完整結果,同時在時間性能上,trastack所用時間僅增加了20.46%;WinDbg僅獲得了上述正確堆棧跟蹤的前兩項,但時間上增加了74.84%;malthfind方法在無法利用元數據的情況下精確率明顯降低,但由于其方法本身不依賴于調試符號,因此精確率高于Windbg,同時由于缺失元數據,漏報情況增加;malinfind方法中可利用元數據,其準確率略高于malthfind方法,但由于其在利用子方法時,RSP偏移量不夠精確,導致結果中有誤報;沒有將返回地址循環出棧,降低了結果的召回率。實驗結果如表2所示。
至此,確認本法可以在沒有元數據的情況下獲得正確的結果并可以減少掃描技術中由于x64的軟件約定造成的堆棧返回地址的誤報。
4.3.3 不同進程類型測試
為了進一步確定方法的有效性,分別選取用戶進程、系統進程、棧溢出攻擊進程和其他類型攻擊進程對基于流的驗證方法進行堆棧回溯。其中每種進程類型分別選取5個可執行文件樣本;用戶進程的樣本分別為:explorer.exe、notepad.exe、wordpad.exe、mspaint.exe、gsview.exe,系統進程的樣本分別為:vschost.exe、wininit.exe、smss.exe、lsm.exe、alg.exe,棧溢出攻擊漏洞樣本為:WINWORD.EXE(CVE-2012-0158(Common Vulnerabilities&Expo-sures(通用漏洞披露),2012年,編號0158。下同),Microsoft Office 2010 SP1漏洞)、PCManFTPD2.exe(CVE-2013 X1.730,PCMan’s FTP Server 2.0.7漏洞)、EQNEDT32.EXE(CVE-2017-11882,MicrosoftOffice2010SP2漏洞)、EQNEDT32.EXE(CVE-2018-0802,Microsoft Office2013漏洞)、fcrip.exe(CVE-2019-9766,Free MP3 CD Ripper2.6漏洞),其它攻擊樣本:tomcat8.exe(CVE-2018-11784,Apache Tom-cat8.5.0重定向漏洞)、devenv.exe(CVE-2019-1354,Microsoft Visual Studio 15.OGit插件遠程代碼執行漏洞)、firefox.exe(CVE-2019-17026,MozillaFirefox 72.0代碼執行漏洞)、iexplore.exe(CVE-2020-0674,Internet Explorerl 1腳本引擎內存損壞漏洞)、WINWORD.EXE(CVE-2020-0852,Microsoft Of-fice 2019遠程代碼執行漏)。為減輕惡意軟件對物理機的傷害,保證實驗環境的穩定性,惡意軟件的測試在虛擬環境下執行。測試結果如表3所示。
實驗結果表明在Windows x64環境下,插件可以針對微軟官方用戶進程或非微軟用戶進程進行完整而正確的堆棧回溯;針對系統進程,由于非微軟產品無法利用Window調試符號,本方法在執行時會有少許漏報情況。在保證高召回率的情況下,針對提取到的堆棧結果,方法做到了一定的精確性;針對攻擊進程,其通常利用正常進程的漏洞,將其代碼注入相關進程的漏洞模塊中并于執行結束再次修改返回地址以達到隱藏目的。如實驗中CVE-2012-0158漏洞攻擊就是利用WINWORD.EXE進程的漏洞,其將攻擊代碼注入MSCOMCTL.OCX模塊。堆棧回溯發現漏洞函數在返回地址0x275C895C處而在其執行完后,返回地址被更改為0x41414141。這使得本方法不可避免地出現漏報和誤報情況。
4.3.4 不同操作系統版本測試
由于Windows操作系統現有版本眾多且其處于不斷更新的狀態,這要求方法應具有兼容性。實驗選取配有不同大小內存的主流64位Windows版本,對多個棧溢出攻擊漏洞樣本WINWORD.EXE(CVE-2012-0158,Microsoft Office 2010 SPl漏洞)、PCManFTPD2.exe(CVE-2013-4730,PCMan’s FTPServer 2.0.7漏洞)、EQNEDT32.EXE(CVE-2017-11882,Microsoft Office2010SP2漏洞)、EQNEDT32.EXE(CVE-2018-0802,Microsoft Office2013漏洞)、fcrip.exe(CVE-2019-9766,Free MP3 CD Ripper2.6漏洞)執行本方法進行測試,結果如表4所示。
從實驗結果整體上看,針對Windows7SP1版本的精確率和召回率最高,在Windows8.1版本出現最低精確率和召回率,但兩者召回率之差低于1%同時精確率之差低于0.2%,說明本方法具有一定的兼容性。
5 結論
本文提出并實現了一種從Windows x64環境的內存轉儲構建堆棧跟蹤的方法,從內存轉儲中獲取每個進程的用戶上下文,并基于異常處理的元數據展開堆棧,還實現了基于指令流的驗證方法。本方法可以使用或不使用元數據獲取x64進程的堆棧跟蹤,和傳統的基于掃描的技術比較,可以更準確地獲得堆棧跟蹤。
由于堆棧跟蹤技術只是在當前執行狀態下獲取調用堆棧,對于使用等待方法而不保留線程的鉤子惡意軟件,很難有效地進行取證。下一步考慮使用實時監測方法,以實時獲取內存轉儲從而進行堆棧分析來解決這個問題。
參考文獻:
[1]許洪軍,張洪,賀維.一種基于鼠標行為的云用戶異常檢測方法[J].哈爾濱理工大學學,2019,24(4):127.
[2]張瑜,劉慶中.李濤,等.內存取證研究與進展[J].軟件學報,2015,26(5):1151
[3]SATRYA G B,KURNIAWAN F.A Novel Android Memory Foren-sics for Discovering Remnant Data[J].International journal onAdvanced Science,Engineering and Information Technology,2020,10(3):1008.
[4]CHENG Y,FU X,DU X,et al.A Lightweight Live Memory Fo-rensic Approach Based on Hardware Virtualization[J].Informa-tion Sciences,2016,379(1);23.
[5]COLIN R,ANDY P,CAI S S,et al.x64-software-conventions[EB/OL].Microsoft(2018-12-17)[2020-05-15].ht-tps://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx.
[6]PSHOUL D.Volatility Foundation[EB/OL].Github(2019-05-17)[2020-05-17].https://github.com/volati lityfounda-tion/community/blob/master/DimaPshoul/malthfind.py.
[7]OTSUKI Y,KAWAKOYA Y,IWAMURA M,et al.Building StackTraces from Memory Dump of Windows x64[J].Digital Investiga-tion,2018,24(S1):101.
[8]劉劍,蘇璞睿,楊珉,等.軟件與網絡安全研究綜述[J].軟件學報,2018,29(1):42.
[9]FRANK B,ANDREAS D.Windows Memory Forensics:Detecting(Un)Intentionally Hidden Injected Code by Examining Page TableEntries[J]Digital Investigation,2019,29(S1):3.
[10]Al-SALEH M,QAWASMEH E,Al-SHARIF Z.Utilizing DebuggingInformation of Applications in Memory Forensics[J].Journal of U-niversal Computer Science,2020,28(7):805.
[11]SRIVASTAVA A,JONES J H.Detecting Code Injection by Cross-validating Stack and VAD Information in Windows Physical Memo-ry[C]//2017 IEEE Conference on Open Systems,ICOS.Miri,2017:83.
[12]BALAOURA S.Process Injection Techniques and Detection usingthe Volatility Framework[D].Piraeus:University of Piraeus,2018.25.
[13]MUTHUMANICKAM K,ILAVARASAN E.An Effective Methodfor Protecting Native API Hook Attacks in User-mode[J].Re-search Journal of Applied Sciences,Engineering and Technology,2015,9(1):33,
[14]YOSIFOVICH P,SOLOMON D A,IONESCU A.Windows Inter-nals,Part 1:System Architecture,Processes,Threads,MemoryManagement,and More[M].California:Microsoft Press,2017.
[15]NATHAN L,ANDREW C,AISHA A,et al.Memory Forensicsand the Windows Subsystem for Linux[J].Digital Investigation,2018,26(S1):3.
[16]LEE K,HWANG H,KIM K,et al.Robust Bootstrapping MemoryAnalysis Against Anti-forensics[J]Digital Investigation,2016,18(S1):23.
[17]COHEN M I.Characterization of the Windows Kernel Version Vari-ability for Accurate Memory Analysis[J].Digital Investigation,2015,12(S1):38.
[18]BRENDAN D.The VAD Tree:A Process-eye View of PhysicalMemory[J].Digital Investigation,2007,4(S1):62.
[19]RALPH P,FELIX F.Styx:Countering Robust Memory Acquisi-tion[J].Digital Investigation,2018,24(S1):18.
[20]COHEN A,NISSIM N.Trusted Detection of Ransomware in a Pri-vate Cloud Using Machine Learning Methods Leveraging Meta-fea-tures from Volatile Memory[J].Expert Systems with Applications,2018,102:158.
[21]HUDA S,MIAH S,HASSAN M M,et al.Defending Unknown At-tacks on Cyber Physical Systems by Semi-supervised Approach andAvailable Unlabeled Data[J].Information Sciences,2017,379(10):211.
(編輯:溫澤宇)
收稿日期:2020-07-22
基金項目:國家自然科學基金(61403109);黑龍江省自然科學基金(F2016024);黑龍江省教育廳科學技術研究面上項目(12531121).
作者簡介:徐曉(1994-),女,碩士研究生;陳攀(1994-),男,碩士研究生.
通信作者:翟繼強(1972-),男,博士,教授,E-mail:zaijiqiang@163.com.