張君濤 王軼駿 薛 質
(上海交通大學電子信息與電氣工程學院 上海 200240)
遠控木馬、僵尸網絡、蠕蟲以及近些年爆發的勒索軟件、挖礦木馬等各類惡意代碼影響著用戶對計算機的正常使用。針對特定目標的APT攻擊,采用高度定制化的惡意代碼進行魚叉攻擊,成為了國家信息安全面臨的重大威脅。因而,惡意代碼一直受到網絡安全從業人員以及研究人員的廣泛關注。
十多年前,研究者就已經采用沙箱技術來分析惡意代碼。布谷鳥(Cuckoo)是一款開源且使用廣泛的沙箱軟件,利用虛擬化技術構造出與真實環境隔離且擁有快速還原能力的系統,研究人員通過分析代碼在沙箱中的所有行為,進而判斷是否為惡意代碼。因此,針對惡意軟件的分析沙箱被認為是防御APT的最后一道防線。病毒檢測廠商比如CrowdStrike、Malwr以及國內的微步科技都利用沙箱技術提供惡意代碼在線檢測的服務。
惡意代碼開發者為了逃避沙箱檢測,防止自身暴露,會在惡意代碼中加入環境探測代碼,當發現自身運行于異常環境中時,就會停止惡意行為,運行無害代碼或者結束運行,達到逃避檢測的目的。據McAfee 2017年發布的《威脅報告》顯示,反沙箱機制超過反調試、反監視、代碼混淆等方法,成為惡意代碼使用最為廣泛的逃避檢測手段[1]。此外,Lastline實驗室在2015年指出,具有逃避檢測功能的惡意代碼的比例從2014年年初的35%上升至2014年12月的80%,并且往往結合數10種方法[2]。
本文采用angr對惡意代碼進行動態分析。angr是一款基于Python實現,可利用動態符號執行進行二進制代碼分析的調試器[3]。相比于現有的分析方法,動態符號執行可以遍歷程序不同的分支,獲得惡意代碼更多的行為特征。這能有效避免外部環境比如C&C服務器的失效對分析惡意代碼產生的影響[4]。相對的,反沙箱機制卻會導致符號執行面臨路徑爆炸的難題,特別是當符號值作為循環語句的參數時,產生大量無害分支,導致難以發現真正執行惡意行為的分支[5]。
因而本文將首先對使用最為廣泛的惡意代碼沙箱檢測機制進行分析;接著說明我們為angr制定的拓展程序,用于對抗不同類型的反沙箱機制以及原型系統的實現方法;最后通過對沙箱檢測軟件al-khaser以及paranoid fish的測試,證明我們開發的原型系統可以繞過常見的反沙箱機制,有效防止符號執行的路徑爆炸問題。
在惡意代碼分析領域,Sandboxie、布谷鳥、Anubis等是常見的沙箱軟件。然而,沙箱與真實環境存在一定的差別,沙箱為了監控惡意代碼的行為、阻止惡意代碼從沙盒逃逸,部分采用進程注入、函數掛鉤等機制監控惡意代碼的行為[6-8]。有些沙箱利用虛擬化技術構建真實操作系統,但這些虛擬機存在特定的指紋信息[9-11]。因而,惡意代碼反沙箱方法可以分為基于沙箱的檢測以及基于虛擬機的檢測。
Sandboxie與被分析的惡意代碼同處一個操作系統內,為了避免惡意代碼對真實系統產生影響,沙箱內的程序對真實系統的文件、注冊表、進程的訪問都需要獲得Sandboxie的授權。Sandboxie通過在內核驅動上掛載SbieDrv.sys實現對系統服務描述符表(SSDT)、Shadow SSDT表的掛鉤。通過將SbideDll.dll注入到進入沙盒的程序中,實現對所有用戶態API函數的掛鉤。因而惡意代碼通過檢測程序自身是否被注入SbideDll.dll,即可成功繞過Sandboxie沙箱的檢測。
布谷鳥沙盒是蜜網社區2010年谷歌編程之夏的開源項目,經過數年的開發,其功能與性能不斷升級,成為了目前較為流行的沙箱系統。布谷鳥沙箱可分為主從節點,主節點主要負責任務的分發以及對惡意代碼運行結果的分析展示。從節點為惡意代碼的動態運行提供真實的系統環境,為了能夠實現系統的快速還原,從節點一般為虛擬機。布谷鳥同樣采取dll注入的方式來監控Windows API的調用。此外,布谷鳥還采用API掛鉤的方法,因而David編寫的anti-cuckoo通過檢測ntdll.dll中的某幾個關鍵函數是否被篡改來探測布谷鳥程序。此外,anti-cuckoo通過檢查進程的內存空間中是否存在敏感詞,比如cuckoomon、HookHandle、retaddr-check等來探測是否運行于布谷鳥中。
由于虛擬機具有快速還原的特點且與真實系統隔離,不僅僅是上一節敘述的布谷鳥、Anubis等沙箱使用虛擬機作為惡意代碼分析的宿主機,反病毒研究員在日常分析惡意代碼行為時,也大都使用虛擬機作為分析系統。常用的虛擬機有VirtualBox、Vmware、KVM、Xen等。惡意代碼針對虛擬機的檢測如圖1所示。

圖1 針對虛擬機檢測方法的分類
根據虛擬機操作系統的屬性進行探測是惡意代碼檢測虛擬機的各類方法中手段最為豐富的一種方法。在虛擬機操作系統內,注冊表、文件系統、進程、服務中存在各種虛擬機指紋特征。據統計,安裝在Vmware上的WindowsXP虛擬機有超過50個與“Vmware”、“vmx”有關的文件,在注冊表中有超過500項鍵或鍵值與“Vmware”有關。此外,分析人員可能為用于惡意代碼分析的虛擬機設定特別的用戶名或重新命名惡意代碼程序,因而惡意代碼可以通過將這些敏感位置的名稱與自定義的黑名單列表匹配,來檢測是否為虛擬機。
虛擬機的硬件并非真實的硬件,因而通過模擬出的硬件信息也能夠探測虛擬機。和操作系統一樣,硬件也存在多種多樣的指紋信息,其指紋檢測包括對CPU、BIOS、硬盤類型的判斷以及對CPU名稱、網卡地址、網卡名稱、可插拔硬件設備的檢測。除此之外,考慮到為虛擬機分配的硬件資源往往小于真實計算機的硬件資源,因而可以通過對CPU核心數、內存大小、硬盤容量的檢測來判斷是否為虛擬機。在虛擬機中,指令運行的速度遠遠慢于真實系統,因而部分惡意代碼通過計算指令運行的時間來檢測虛擬機。
基于進程的檢測是惡意代碼對自身運行時虛擬內存空間的探測,常見的方法是檢測中斷描述符表(IDT)、全局描述符表(GDT)以及局部描述符表(LDT)在內存中的地址。真實系統中,這些描述符表的位置基本在確定的地址附近,且完全不同于虛擬機,因而具有較高的檢測正確率。
最后,是對特殊檢測方法的匯總。反圖靈測試是主動探測人機交互的行為,在一定的時間間隔內,兩次獲取當前鼠標在屏幕上的位置坐標,比較兩次位置是否發生變化,從而檢測虛擬機。休眠是指在程序運行后,休眠一段較長的時間,再執行惡意行為,往往虛擬機在惡意代碼真正運行前已停止工作,因而可以有效地躲避分析。
angr是美國圣巴巴拉大學ShellPhish團隊研發的一款二進制分析框架,其源代碼由Python編寫,在2015年項目得到開源,目前項目已迭代至第七個版本。angr可以實現動靜態符號執行、控制流平坦化、自動化生成ROP鏈,應用于漏洞挖掘與軟件破解,同時也是CTF比賽中逆向分析的常用工具之一[3,12-13]。使用者通過導入angr庫并編寫Python腳本實現對二進制代碼的加載與分析。
angr由以下幾個子模塊組成,每個子模塊都可以獨立加載:
CLE(CLE Loads Everything)將可執行程序加載進內存空間。例如,對于EXE程序,通過解析文件頭,獲得加載地址、各段對應的文件與內存的映射關系,進而將主文件加載至虛化的內存空間。接著,遞歸加載程序需要的dll文件。
Archinfo針對不同架構的指令集,如x86、ARM、MIPS、PowerPC等,提供專有的類。每個類中都涉及寄存器類型、處理器位數、數據在內存中的存儲方式以及是否支持VEX、Capstone、Unicorn、KeyStone等特性。
PyVEX可以將機器代碼轉化為Valgrind使用的中間語言VEX。通過VEX模擬執行程序的代碼并修改內存、寄存器的值。
Claripy是一個符號執行求解器,其后端為Z3。
利用angr編寫Python腳本,對二進制程序進行分析的整體流程如圖2所示。首先,通過Project方法,加載程序至內存空間,并得到包含加載對象名稱、各段地址空間、導入函數地址空間等屬性的project對象。接著,利用State方法初始化程序堆棧以及寄存器,得到可以動態執行的對象SimState。通過SimState對象可以查看、修改、符號化內存空間以及寄存器的值,并且提供用于保存用戶自定義屬性的插件。由于動態符號執行會產生分支,得到多個Simstate對象,仿真管理器(Simulation Managers)提供了對所有分支進行訪問以及管理的功能。此外,仿真管理器在程序運行到某個地址或滿足某種條件時,停止對應分支執行或者丟棄分支。

圖2 基于angr的程序分析流程
具有沙箱檢測功能的惡意代碼,大部分結合了十種以上的檢測機制,基于符號執行的特點,會帶來路徑爆炸的問題,淹沒惡意行為的分支。由于angr動態的模擬程序運行,可以方便地對Win32 API函數掛鉤、重寫VEX指令、完善內存結構。因而,針對于各種惡意代碼對沙箱的探測,都可以采取措施進行對抗。
2.2.1 Win32 API掛鉤
惡意代碼與普通EXE程序一樣,在運行時動態鏈接系統提供的庫函數。angr在模擬程序動態運行時,提供了兩種方法實現Win32 API函數。由于CLE Loader已經加載主程序以及其導入的dll文件至相應的地址空間,因而可以直接運行API函數對應的代碼。對于大小寫變換、字符串比較型的函數,比如tolower()、wcsstr()、lstrcmpiA(),這種方法可以達到很好的效果。但是,對于大多數需要系統信息或者進行內核函數調用的函數,比如CreateFileA()、GetModuleHandleA(),往往會產生眾多分支,導致路徑爆炸甚至出現分支異常終止。第二種方法是對Win32 API函數掛鉤。首先,通過構建SimProcedure類,并在類中定義與實際API函數具有相同參數數量以及類型的run函數模擬API的實現。在程序加載后,對project對象使用hook_symbol方法替換Win32 API函數。在程序執行時,當指令指針指向Win32 API函數的首地址時,實際執行的是run函數中的Python代碼。
注冊表、文件、進程、服務的指紋特征是惡意代碼檢測沙箱最為常見的方法。表1展示了沙箱檢測方法對應的API函數調用序列。掛鉤這些函數并修改返回參數以及返回值即可繞過沙箱檢測。

表1 沙箱檢測方法調用的API函數序列
以RegOpenKeyExA函數為例,查閱微軟Win32 API手冊,其參數分別為主鍵(hKey)、子鍵(lpSubKey)、ulOptions、訪問權限(samDesired)、注冊表句柄(phkResult)。如圖3所示。

圖3 RegOpenKeyExA函數的掛鉤流程
函數存在以下三種情況:
(1) 首先定義敏感鍵列表,包括vm、vmware、virtualbox、qemu等對注冊表檢測的核心詞語。接著,傳入的參數子鍵與敏感鍵列表匹配,如果匹配成功,則函數返回值為2(ERROR_FILE_NOT_FOUND),表示注冊表項不存在。這種方式,可以成功對抗惡意代碼對注冊表特定鍵值的檢測。如果匹配失敗,則執行第二種情況。
(2) 首先定義敏感鍵值字典,字典中的鍵表示注冊表的子鍵,值表示注冊表的鍵名和鍵值,比如“HARDWAREDEVICEMAPSCSISCSI PORT 0SCSI BUS 0TARGET ID 0LOGICAL UNIT ID 0′: {′IDENTIFIER′: ′INTEL′}”。接著,比較傳入參數的子鍵是否等于敏感鍵值字典中的鍵,如果相等,則隨機產生注冊表句柄值并且函數返回值為0,表示注冊表存在并且成功打開。
(3) 如果前兩種情況都不滿足,根據符號執行的特點,將程序分支并符號化輸出。對于RegOpenKeyExA函數,存在打開文件成功并寫入注冊表句柄以及打開文件未能成功兩種情況。前一種情況,符號化句柄且函數返回值為0;對于后一種情況,無需對句柄進行賦值,只需符號化返回值。
如表1所示,打開注冊表鍵值需要兩個函數,對于后序的RegQueryValueEx函數,其傳入的注冊表句柄參數是一個隨機化的數值。為了獲得其對應的子鍵名稱,進而結合鍵名、鍵值兩個參數與定義的敏感鍵值字典進行匹配,需要在RegOpenKeyExA函數中映射注冊表句柄與子鍵名稱。利用angr提供的自定義插件功能,可以為當前SimState增加屬性,從而實現多個函數之間的狀態共享。
2.2.2 VEX指令修補
angr在模擬程序動態執行時依據中間語言VEX完成各項操作。VEX兼容大部分x86指令,實現對寄存器、內存的修改,但是對于某些特殊指令,如表所示,當程序分支執行這些指令時,該分支的狀態變為錯誤并且停止運行。雖然angr對極少部分VEX指令,比如CPUID進行了修補,但不能滿足實際惡意代碼分析的需要,因而,我們對表2中的VEX指令都進行修補。

表2 x86特殊指令與VEX指令的對照表
以指令CPUID為例,如圖4所示。eax寄存器是指令的輸入參數,eax、ebx、ecx、edx寄存器保存指令執行后返回信息,根據輸入參數不同,可以獲得CPU類型、制造商、商標、序列號、緩存等一系列信息。在沙箱檢測中,當輸入參數為0x1時,往往是對返回值中ecx寄存器的最高位:hypervisor CPU位的檢測,此時將ecx置為0,其他寄存器符號化。當輸入參數為0x40000000時,執行指令獲得CPU型號并保存在ebx、ecx、edx寄存器中。每個寄存器代表4個字符,共同構成12字符的型號名稱。通過與″KVMKVMKVM″、″VMwareVMware″、″XenVMMXenVMM″等字符比較,進而判斷是否為虛擬機環境。當輸入參數分別為0x80000002、0x80000003、0x80000004且執行三次CPUID指令,可以獲得共48個字符的CPU名稱。通過賦值普通字符,比如″Intel(R) Core(TM)2 CPU 6600 @2.4 GHz″可以對抗檢測。

圖4 CPUID指令執行流程
2.2.3 內存結構完善
angr利用state方法初始化程序,設定寄存器以及內存空間的值,雖然可以實現代碼執行,但不同于正常操作系統,沒有在內存中設置完整的PEB、TEB、TLS等結構體。由于惡意代碼通過讀取結構體對應位置的值獲取硬件信息,比如PEB進程環境塊的0x64字節代表CPU數量,進而判斷是否運行在虛擬機中。因而需要對angr的初始化文件打補丁,完善程序內存中的結構體。
2.2.4 angr特性
angr自身的特性同樣能夠對抗惡意代碼對沙箱的部分檢測方法。對于dll注入的檢測,由于angr從外部實現對惡意代碼執行流程的監控,其內存空間中不存在附加的dll函數。同時,angr采用的Win32 API掛鉤并沒有改變內存空間中API函數的內容,從而繞過將API函數第一個操作碼與跳轉指令對應的操作碼比較的檢測方法。
通過上文所述的各種方法,可以對抗大多數惡意代碼對沙箱的檢測。對于未知的檢測手段,基于angr動態符號執行的特點,需要做到在其所有分支路徑中包含惡意行為的分支并且盡可能減少產生的分支數目。考慮到執行Win32 API函數內部的代碼會造成路徑爆炸,因而掛鉤所有Win32 API函數(除2.2.1節描述的字符串變換、比較型的函數),且在函數體內不執行任何操作,僅僅將函數的返回值設定為符號值。并且由于angr將未賦值的內存空間作為符號值代入動態執行,雖然可能增加路徑分支的數目,卻可以確保惡意行為分支不被遺漏。
基于angr對抗惡意代碼沙箱檢測的原型系統的實現如圖5所示。首先,在程序加載時,將auto_load_libs以及except_missing_libs兩個參數設置為真,并指定加載dll文件的路徑;其次,利用analyses對象中提供的CalleeCleanupFinder方法,對所有導入的API函數掛鉤,掛鉤的函數體內不執行任何操作;接著,將Win32_API_Hooking.py文件中定義的函數掛鉤程序,對于同一個API函數,這一步的掛鉤將覆蓋CalleeCleanupFinder中的掛鉤;之后,利用angr提供的SimStatePlugin對象,注冊可以增加SimState屬性的插件。最后,使用動態符號執行模擬程序的運行。

圖5 對抗惡意代碼沙箱檢測的原型系統
對于2.2節中提到的方法,在原型系統實現時,對于Win32 API函數掛鉤,只需要在Win32_API_Hooking.py文件中創建與API函數名稱一致的類并實現函數執行的方法,就可以通過angr提供的接口函數hook_symbol將API函數掛鉤。而對于VEX指令修補以及內存結構完善兩種方法,需要通過修改angr的源代碼,從而實現功能,對應修改的文件分別是angr/engines/vex/dirty.py以及angr/simos/windows.py。
表3列舉了原型系統對抗沙箱檢測各類方法時采取的應對措施。考慮到不同指令、函數功能上的差異,相應的修補方式也不盡相同,修補代碼的實現遵循這兩條原則:對于單指令、單函數的修補,首先判斷輸入的參數與沙箱檢測時使用的參數是否一致,如果能夠匹配,使用對抗沙箱檢測的方法,否則符號化函數的輸出參數以及返回值;對于序列化的函數,采用的修補方式不僅需要滿足單函數修補原則,并且利用插件功能記錄程序執行的狀態,后序函數實現時需要考慮前序函數及其參數是否已經滿足沙箱檢測的條件。

表3 對抗沙箱檢測的方法

續表3
基于angr對抗沙箱檢測方法的原型系統使用最新的angr7,并且通過OSX Sierra系統下Python2.7中的測試。用于angr加載惡意代碼的dll文件拷貝于32位Windows XP SP3系統中的system32文件夾。al-khaser以及paranoid fish是兩款用于對沙箱、虛擬機進行檢測的開源項目,均使用C++編程實現,其搜集并總結了122類惡意代碼使用的方法,并且al-khaser仍處于不斷更新的狀態。為了便于分析,我們使用32位編譯器生成用于分析的EXE文件。
微步科技提供的微步云沙箱,內核集成了人機交互、反沙箱檢測、內核級分析、網絡模擬等多個高級分析模塊。CrowdStrike公司的VxStream沙箱是基于機器學習的下一代惡意軟件掃描器。值得一提的是,可以在其掃描配置中將反沙箱檢測設為強等級。對于al-khaser以及paranoid fish,我們分別使用布谷鳥沙箱2.0.4版本、微步云沙箱以及強反沙箱檢測等級的VxStream進行掃描,其分析環境均為Windows 7,與原型系統分析比較的結果如表4所示,其中T代表惡意代碼檢測出沙箱環境、F代表未能檢測出沙箱環境。為了簡化表格,表4中沒有列出四款系統均能識別的沙箱檢測技巧以及paranoid fish與al-khaser中重復的檢測技巧。

表4 針對al-khaser以及paranoid fish的檢測結果

續表4
從表4的結果可知,相比于開源的布谷鳥平臺,微步云沙箱以及VxStream沙箱在對抗惡意代碼檢測沙箱環境方面均有一定的提升,特別是設置了強反沙箱等級的VxStream檢出率僅僅為7/122。但是對于由匯編代碼直接獲取系統信息的檢測方法,比如cpuid、rdtsc,這幾種沙箱都未能成功對抗。本文設計的原型系統能夠成功繞過大部分檢測方法,因而顯著優于現有的在線沙箱檢測平臺。
本文基于angr提出了一種能夠對抗惡意代碼針對沙箱環境進行探測的二進制代碼分析方法。原型系統采用Win32 API函數掛鉤、VEX指令修補以及內存結構完善三種方法,針對常見的惡意代碼反沙箱機制采取相應的修補措施。實驗表明,本文提出的方法在對抗惡意代碼沙箱檢測方面,優于其他傳統的方法。因此,在本文的基礎上,利用angr動態符號執行的特點,自動化地捕獲惡意代碼的行為特征是我們下一步的研究工作。