使用Speakeasy Emulation Framework快速分析惡意程序
Andrew Davis最近發(fā)布了他的新Windows模擬框架,名為Speakeasy。本文將強調(diào)該框架的另一個強大用途:自動大規(guī)模分析惡意程序。我將通過代碼示例演示如何通過編程方式使用Speakeasy:
- 繞過不受支持的Windows API以繼續(xù)模擬和分析;
- 使用API掛鉤保存動態(tài)分配的代碼的虛擬地址;
- 使用代碼掛鉤以外科方式直接執(zhí)行代碼的關(guān)鍵區(qū)域;
- 從模擬器內(nèi)存中轉(zhuǎn)儲分析的PE并修復(fù)其節(jié)頭;
- 通過查詢Speakeasy以獲得符號信息來幫助重建導入表;
初始設(shè)置
與Speakeasy交互的一種方法是創(chuàng)建Speakeasy類的子類。圖1顯示了Python代碼段,它設(shè)置了一個類,這個類將在后面的示例中擴展。
創(chuàng)建一個Speakeasy子類
圖1中的代碼接受一個Speakeasy配置字典,該字典可用于覆蓋默認配置。 Speakeasy附帶了幾個配置文件。 Speakeasy類是基礎(chǔ)模擬器類的壓縮器類。根據(jù)二進制文件的PE標頭加載二進制文件或?qū)⑵渲付閟hellcode時,會自動選擇模擬器類。子類化Speakeasy使訪問,擴展或修改接口變得容易,它還有助于在模擬期間讀取和寫入狀態(tài)數(shù)據(jù)。
模擬二進制
圖2顯示了如何將二進制文件加載到Speakeasy模擬器中。
將二進制文件加載到模擬器中
load_module函數(shù)為磁盤上提供的二進制文件返回PeFile對象。它是在speakeasy/windows/common.py中定義的PeFile類的實例,該類是pefile的PE類的子類,或者,你可以使用data參數(shù)而不是指定文件名來提供二進制字節(jié)。圖3顯示了如何模擬已加載的二進制文件。
啟動模擬
API掛鉤
Speakeasy框架提供了對數(shù)百個Windows API的支持,并經(jīng)常添加更多的API。這是通過定義在speakeasy/winenv/ API目錄的適當文件中的Python API處理程序來實現(xiàn)的。當在模擬過程中調(diào)用特定的API時,可以安裝API掛鉤以執(zhí)行自己的代碼??梢詾槿魏蜛PI安裝它們,而不管是否存在處理程序。API掛鉤可用于覆蓋現(xiàn)有處理程序,并且可以選擇從你的掛鉤中調(diào)用該處理程序。 Speakeasy中的API掛鉤機制提供了靈活性和對模擬的控制。讓我們研究一下在模擬分析代碼以檢索分析的有效載荷的上下文中API掛鉤的幾種用法。
繞過不受支持的API
當Speakeasy遇到不受支持的Windows API調(diào)用時,它將停止模擬并提供不受支持的API函數(shù)的名稱。如果所討論的API函數(shù)對于分析二進制文件不是至關(guān)重要的,則可以添加一個API掛鉤,該掛鉤僅返回允許執(zhí)行繼續(xù)的值。例如,最近的樣本的分析代碼包含對分析過程沒有影響的API調(diào)用。這樣的API調(diào)用之一就是GetSysColor,為了繞過此調(diào)用并允許執(zhí)行繼續(xù),可以添加一個API掛鉤,如圖4所示。
添加的API掛鉤
根據(jù)MSDN,此函數(shù)采用1參數(shù),并返回表示為DWORD的RGB顏色值。如果要掛接的API函數(shù)的調(diào)用約定不是stdcall,則可以在可選的call_conv參數(shù)中指定調(diào)用約定。調(diào)用約定常量在speakeasy/common/arch.py文件中定義。因為GetSysColor返回值不會影響分析過程,所以我們可以簡單地返回0。圖5顯示了圖4中指定的getsyscolor_hook函數(shù)的定義。
GetSysColor掛鉤返回0
如果API函數(shù)需要更精細的處理,則可以實現(xiàn)更具體,更有意義的掛鉤,以滿足你的需要。如果你的掛鉤實現(xiàn)足夠穩(wěn)定,則可以考慮將其作為API處理程序添加到Speakeasy項目!
添加API處理程序
在speakeasy / winenv / api目錄中,你將找到usermode和kernelmode子目錄,這些子目錄包含對應(yīng)二進制模塊的Python文件。這些文件包含每個模塊的API處理程序。在usermode / kernel32.py中,我們看到為SetEnvironmentVariable定義的處理程序,如圖6所示。
SetEnvironmentVariable的API處理程序
處理程序以函數(shù)修飾符(第1行)開頭,該修飾符定義API的名稱及其接受的參數(shù)數(shù)量。在處理程序開始時,最好的做法是將MSDN記錄在案的原型作為注釋(第3-8行)。
處理程序的代碼首先將argv參數(shù)的元素存儲在以相應(yīng)API參數(shù)(第9行)命名的變量中,處理程序的ctx參數(shù)是一個字典,其中包含有關(guān)API調(diào)用的上下文信息。對于以“A”或“W”結(jié)尾的API函數(shù)(例如,CreateFileA),可以通過將ctx參數(shù)傳遞給get_char_width函數(shù)(第10行)來檢索字符寬度。然后可以將此寬度值傳遞給read_mem_string之類的調(diào)用(第12和13行),該調(diào)用在給定地址讀取模擬器的內(nèi)存并返回一個字符串。
最好的作法是用相應(yīng)的字符串值(第14和15行)覆蓋argv參數(shù)中的字符串指針值。這使Speakeasy能夠在其API日志中顯示字符串值而不是指針值。為了說明更新argv值的影響,請檢查圖7中所示的Speakeasy輸出。在VirtualAlloc條目中,符號常量字符串PAGE_EXECUTE_READWRITE替換值0x40。在GetModuleFileNameA和CreateFileA條目中,指針值被替換為文件路徑。
Speakeasy API日志
保存分析的代碼地址
壓縮樣本通常使用VirtualAlloc之類的函數(shù)來分配用于存儲分析樣本的內(nèi)存。捕獲分析后的代碼的位置和大小的有效方法是首先掛接分析存根使用的內(nèi)存分配函數(shù)。圖8顯示了掛鉤VirtualAlloc以捕獲虛擬地址和API調(diào)用分配的內(nèi)存量的示例。
VirtualAlloc掛鉤可以保存內(nèi)存轉(zhuǎn)儲信息
圖8中的掛鉤在第12行調(diào)用Speakeasy的VirtualAlloc的API處理程序,以分配內(nèi)存。 API處理程序返回的虛擬地址將保存到名為rv的變量。由于VirtualAlloc可用于分配與分析過程無關(guān)的內(nèi)存,因此在第13行使用附加檢查以確認截獲的VirtualAlloc調(diào)用是分析代碼中使用的調(diào)用。根據(jù)先前的分析,我們正在尋找一個VirtualAlloc調(diào)用,該調(diào)用將接收lpAddress值0和flProtect值PAGE_EXECUTE_READWRITE(0x40)。如果存在這些自變量,則虛擬地址和指定的大小將存儲在第15行和第16行,因此在分析代碼完成后,可以將它們用于從內(nèi)存中提取分析的有效載荷。最后,在第17行,該掛鉤返回VirtualAlloc處理程序的返回值。
使用API和代碼掛鉤的外科式代碼模擬
Speakeasy是一個強大的模擬框架,但是,你可能會遇到包含大量有問題代碼的二進制文件。例如,一個示例可能會調(diào)用許多不受支持的API,或者只是花費太長時間而無法進行模擬。在以下情形中描述了克服這兩個挑戰(zhàn)的示例。
取消隱藏在MFC項目中的存根
一種用于掩蓋惡意有效載荷的流行技術(shù)涉及將它們隱藏在大型的開源MFC項目中。 MFC是Microsoft Foundation Class的縮寫,它是用于構(gòu)建Windows桌面應(yīng)用程序的流行庫。這些MFC項目通常是從流行的網(wǎng)站(例如Code Project)中任意選擇的,盡管MFC庫使創(chuàng)建桌面應(yīng)用程序變得容易,但是MFC應(yīng)用程序由于其大小和復(fù)雜性而難以進行反向工程。由于它們調(diào)用許多不同的Windows API的大型初始化例程,因此特別難以模擬。以下是對我使用Speakeasy編寫Python腳本以自動分析自定義壓縮程序的經(jīng)驗的描述,該自定義壓縮程序?qū)⑵浞治龃娓[藏在MFC項目中。
對壓縮程序進行反向工程后發(fā)現(xiàn),在CWinApp對象的初始化過程中最終會調(diào)用分析存根,該過程在C運行時和MFC初始化之后發(fā)生。嘗試繞過不受支持的API之后,我意識到,即使成功,模擬也將花費太長時間而無法實現(xiàn)。我考慮過完全跳過初始化代碼,然后直接跳轉(zhuǎn)到分析存根。不幸的是,為了成功模擬分析存根,需要執(zhí)行C運行時初始化代碼。
我的解決方案是在代碼中確定在C運行時初始化后落在MFC初始化例程中的早期位置,檢查完圖9所示的Speakeasy API日志后,很容易發(fā)現(xiàn)該位置。與圖形相關(guān)的API函數(shù)GetDeviceCaps在MFC初始化例程的早期被調(diào)用。這是基于以下事實推論得出的:1.MFC是與圖形相關(guān)的框架,2.在C運行時初始化期間不太可能調(diào)用GetDeviceCaps。
在Speakeasy API日志中標識MFC代碼的開頭
為了在此階段攔截執(zhí)行,我為GetDeviceCaps創(chuàng)建了一個API掛鉤,如圖10所示。該掛鉤確認該函數(shù)在第2行中首次被調(diào)用。
GetDeviceCaps的API掛鉤集
第4行顯示了使用Speakeasy類的add_code_hook函數(shù)創(chuàng)建代碼掛鉤,代碼掛鉤允許你指定在模擬每個指令之前調(diào)用的回調(diào)函數(shù)。 Speakeasy還允許你通過指定begin和end參數(shù)來指定代碼掛鉤對其有效的地址范圍。
在第4行上添加代碼掛鉤之后,GetDeviceCaps掛鉤完成,并且在執(zhí)行示例的下一條指令之前,將調(diào)用start_unpack_func_hook函數(shù)。此函數(shù)如圖11所示。
更改指令指針的代碼掛鉤
代碼掛鉤接收模擬器對象,當前指令的地址和大小以及上下文字典(第1行)。在第2行,代碼掛鉤將自身禁用。因為代碼掛鉤是與每個指令一起執(zhí)行的,所以這會大大降低模擬速度。因此,應(yīng)謹慎使用并盡快將其禁用。在第3行,該掛鉤計算了分析函數(shù)的虛擬地址。使用正則表達式定位用于執(zhí)行此計算的偏移量。為了簡潔起見,省略了該示例。
self.module屬性先前是在圖2所示的示例代碼中設(shè)置的。它是從pefile的PE類中繼承而來的,它使我們可以在第3行訪問有用的函數(shù),例如get_rva_from_offset()。該行還包括一個使用self的示例。 .module.get_base()以檢索模塊的基本虛擬地址。
最后,在第4行,使用set_pc函數(shù)更改了指令指針,并在分析代碼處繼續(xù)進行模擬。圖10和圖11中的代碼段使我們能夠在C運行時初始化完成后將執(zhí)行重定向到分析代碼,并避免使用MFC初始化代碼。
發(fā)布和修復(fù)未壓縮的PE
一旦模擬達到了分析樣本的原始入口點,就該刪除PE并對其進行修復(fù)了。通常,一個掛鉤會將分析的PE的基址保存在該類的屬性中,如圖8的第15行所示。如果分析的PE在其PE標頭中未包含正確的入口點,則真實入口點也可能需要在模擬過程中捕獲。圖12顯示了如何將模擬器內(nèi)存轉(zhuǎn)儲到文件的示例。
轉(zhuǎn)儲未壓縮的PE
如果要轉(zhuǎn)儲已加載到內(nèi)存中的PE,則由于節(jié)對齊方式的不同,其布局將與磁盤上的布局不同。因此,可能需要修改轉(zhuǎn)儲的PE頭。一種方法是修改每個部分的PointerToRawData值以匹配其VirtualAddress字段。為了符合PE可選標頭中指定的FileAlignment值,可能需要填充每個部分的SizeOfRawData值。請記住,生成的PE不太可能成功執(zhí)行。但是,這些努力將使大多數(shù)靜態(tài)分析工具能夠正確運行。
修復(fù)轉(zhuǎn)儲的PE的最后一步是修復(fù)其導入表,這是一個很復(fù)雜的任務(wù),在此不詳細討論。但是,第一步涉及在模擬器內(nèi)存中收集庫函數(shù)名稱及其地址的列表。如果知道分析程序存根使用GetProcAddress API來解析分析的PE的導入,則可以調(diào)用get_dyn_imports函數(shù),如圖13所示。
查看動態(tài)導入
否則,你可以通過調(diào)用get_symbols函數(shù)來查詢模擬器類以檢索其符號信息,如圖14所示。
從模擬器類檢索符號信息
此數(shù)據(jù)可用于發(fā)現(xiàn)未壓縮PE的IAT并修復(fù)或重建其導入相關(guān)表。
總結(jié)
編寫Speakeasy腳本來分析惡意程序樣本可以分為以下步驟:
(1) 對分析存根進行反向工程,以識別:
- 分析的代碼將駐留在哪里或分配其內(nèi)存的位置;
- 執(zhí)行轉(zhuǎn)移到分析的代碼的位置;
- 任何可能引入問題的有問題代碼,例如不受支持的API,緩慢模擬或反分析檢查。
(2) 如有必要,請設(shè)置掛鉤以繞過有問題的代碼;
(3) 設(shè)置一個掛鉤以標識虛擬地址,以及可選的分析二進制文件的大小;
(4) 設(shè)置一個掛鉤,以在分析代碼的原始入口點執(zhí)行時或之后停止模擬;
(5) 收集Windows API的虛擬地址并重建PE的導入表;
(6) 修復(fù)PE的標頭,并將字節(jié)寫入文件中以進行進一步分析;
(7) 有關(guān)分析UPX樣本的腳本的示例,請在Speakeasy存儲庫中查看UPX分析腳本。
Speakeasy框架提供了一個易于使用、靈活且函數(shù)強大的編程界面,使分析人員能夠解決諸如分析惡意程序之類的復(fù)雜問題。使用Speakeasy自動化這些解決方案可以使它們大規(guī)模執(zhí)行。
本文翻譯自:
https://www.fireeye.com/blog/threat-research/2020/12/using-speakeasy-emulation-framework-programmatically-to-unpack-malware.html