徐玄驥,張智斌
(昆明理工大學,云南 昆明 650500)
Android 操作系統是使用人數最多的移動操作系統。統計信息顯示,截至2020 年12 月,Google Play 共有超過350 萬款應用可供下載,每月平均下載人次超過500 萬。然而,廣闊的使用場景也為惡意軟件發展提供了溫床。另有統計顯示,2020 年1—3 月間,共發現Android 惡意軟件超過48 萬款,其中木馬為最主要的類型。種種數據都表明,惡意軟件檢測是一個重要的亟待解決的問題。然而,隨著時間的推移,Android 惡意軟件也展現出了更強的偽裝性,傳統檢測手段是否奏效存疑。
本文針對上述提到的問題,提出了一套基于多維度特征的Android 惡意軟件檢測方法。通過反編譯Android 應用程序、抽取權限、使用應用程序編程接口(Application Programming Interface,API)、網絡證書時效性、代碼混淆程度等不同特征并將其組成向量,之后使用隨機森林與支持向量機算法訓練分類器,來實現對Android 惡意代碼的自動化識別。
目前,關于Android 惡意代碼檢測的文獻較多,主要思路可歸結為基于權限與特征API、基于程序控制流與數據流以及基于動態行為分析3 種。文獻[1]考慮了不同權限之間的組合與惡意代碼的關系,提出了一種基于隨機森林的靜態惡意代碼檢測方法。代碼控制流信息也常常被用來進行惡意代碼檢測。文獻[2]提出了一種Dalvik 字節碼插樁方案,通過對控制流的監控實現對應用惡意行為的識別。相比于靜態分析,動態分析對軟件的運行狀態有更強的捕捉能力,因此也經常被應用于惡意代碼檢測。文獻[3]使用污點分析技術追蹤多個敏感數據源的數據流動,從而判斷應用是否存在威脅用戶隱私的操作,但污點分析開銷大,運行效率較低。文獻[4]將靜態反編譯特征與動態運行特征相結合,使用卷積神經網絡與長短期記憶網絡混合的方法對惡意應用進行檢測。
由于Java 語言易于逆向的特征,代碼混淆技術被廣泛應用于Android 程序開發的過程中。代碼混淆通過去除代碼中的語義信息,用等效的代碼替換原有代碼結構等方式,使得混淆后的代碼難以理解,從而增加逆向的成本。文獻[5]通過對Google官方市場,中國第三方應用市場以及惡意軟件等3個數據源的10 余萬應用程序包(Android application package,APK)進行大規模掃描,統計了常見的3 種代碼混淆技術的使用情況。統計結果顯示,Android 惡意軟件對于代碼混淆技術的使用與普通應用程序有較大差別。
1.1.1 標識符重命名
在軟件開發過程中,開發者通常賦予變量名較多的語義信息,以保證程序的較高可讀性。然而,充足的語義也為逆向者提供了方便,在變量名的幫助下,逆向者可以輕易理解原作者的意圖,竊取其中重要的實現,從而對開發者的知識產權產生威脅。
標識符重命名通過將變量中的語義信息抹除,可以有效增加逆向者的攻擊成本,從而被廣泛使用。根據文獻[5]所述,有超過70%的中國開發者和超過60%的惡意軟件開發者會使用標識符重命名的方式進行混淆。但是二者的混淆實現存在較大差異。普通開發者通常使用Android Studio(Google 官方推薦的Android 開發集成環境)中內置的proguard 工具進行標識符重命名,然而其策略較為簡單,通常只按照字典順序將標識符重命名為“a”“b”“aa”“ab”……,惡意軟件開發者則會使用更具有迷惑性的策略,如形狀相近的字母組合,如“Ill1I1ll”或“00OoOO0”等,甚至出現英語以外的字符(Unicode)。除此之外,惡意軟件還經常將重載特性與標識符重命名結合起來,比如將某些敏感函數重命名為Android 軟件開發工具包(Software Development Kit,SDK)中定義的一些關鍵API,由于參數不同,不會影響程序的正常運行,但往往會誤導逆向工程師。
1.1.2 字符串加密
由于應用中的字符串包含了非常多的語義信息,逆向者通常會將其作為理解程序語義的突破口。下面的程序片段表明,即便混淆了程序中出現的標識符名稱,有經驗的逆向者仍然可以依靠字符串來猜測函數的功能。

為了防止程序中的明文對信息的泄漏,字符串加密被作為一種有效的混淆手段。在Android 應用開發的過程中,字符串加密可應用在多個階段,包括Java 代碼編譯階段、Java 字節碼轉Dex 文件階段等。
對于惡意代碼開發者來說,字符串加密能夠有效地抹除程序語義信息,抵御部分基于硬編碼特征的掃描工具的檢測,假如加密算法實現得足夠復雜,還能夠有效地增加逆向過程的時間成本。根據文獻[5]所述,普通軟件開發者極少使用字符串加密。
1.1.3 Java 反射
反射是Java 語言的一種高級用法,提供了一種靈活的交互方式,使得開發者能夠在程序運行時了解類、方法和變量的信息,甚至動態地創建類的實例或調用方法。在Android 程序開發領域,Java 通常被用來調用Android SDK 中的非公開方法(標注有hidden 注解的API)。以下代碼展示了如何通過反射,調用“android.telephony.TelephonyManager”類的“getNetworkTypeName”方法。

反射在普通程序與惡意程序中都會被使用,但是二者對反射的使用模式差異較大。普通軟件通常會利用反射進行JNI 調用或后向兼容性的檢測,然而惡意軟件則會利用反射去隱藏控制流,從而抵抗靜態分析工具的檢測,或使用反射將原本正常的函數調用變得十分臃腫,加強對逆向者的干擾。
1.2.1 權限特征選擇及獲取
Android 權限機制提供了對應用的訪問控制。目前,Android 系統提供了超過170 種權限,覆蓋應用行為的方方面面。然而,其中一部分權限可能會對用戶的隱私或設備的運行狀態產生影響,這部分權限被Android 標記為“危險”,需要開發者與用戶謹慎對待。由于惡意應用往往覬覦用戶的隱私或企圖掌握對設備的全面控制,危險權限的出現頻率通常高于普通應用。除此之外,還發現,部分惡意軟件傾向于定義較多的自定義權限。基于以上兩種觀察,從Android 應用的清單文件中抽取權限列表,并統計其中危險權限以及自定義權限的出現次數,作為應用權限方面的特征。所選擇的部分高危權限如表1 所示。
1.2.2 證書特征選擇及獲取
在Android 應用開發的過程中,需要對應用進行簽名,簽名后將會產生一個證書文件,通常以.RSA作為后綴。當應用被上傳到應用市場上進行審核時,證書將會被用來證明程序的正當性。然而,某些惡意軟件不能在正規市場中流通,他們所使用的證書也存在各種問題。在本文中,抽取了應用中的證書文件,并以其是否過期作為特征。

表1 所選擇的部分高危權限
1.2.3 敏感API 調用特征選擇及獲取
Android SDK 提供了豐富的API 供開發者實現對應功能。與權限類似,部分API 的濫用也可能造成用戶隱私被竊取或設備系統異常,這部分API 被稱為敏感API,開發者在使用時需要特別注意。通常來講,惡意軟件對敏感API 的使用更加頻繁,基于此觀察,選擇敏感API 的調用次數作為特征之一。除此之外,相比于普通軟件,惡意軟件對敏感API的調用方式通常較為單一,調用過程也更容易被觸發,因此,選擇敏感API 調用路徑的平均長度作為特征之二。敏感API 調用路徑平均長度的計算方法如下偽代碼所示:


選擇的部分敏感API 如表2 所示。

表2 選擇的部分敏感API
1.2.4 程序混淆信息選擇及獲取
如第1.1 小節所示,代碼混淆技術在正常應用與惡意應用的使用有較大差異。基于此,選擇如下特征表示程序的混淆信息:
(1)應用程序中標識符名稱的平均長度。由于Android 惡意軟件傾向于對標識符進行復雜地重命名,其標識符名稱的分布將不同于普通應用。除了標識符的平均長度以外,還可以使用信息熵來表征標識符名稱上的差異。
(2)應用程序中字符串的信息熵。信息熵用于量化表示信息的不確定性,當某條信息被加密后,其信息熵將會增大。信息熵的通用計算公式如下:

式中,N表示事件的個數,Pi表示事件i的發生概率。在本系統的實現中,抽取一個Android 應用中出現的所有字符串并將其合并。假設合并后的字符串為S中出現的字符種類共為N,Pi表示第i個字符Xi出現的概率,則,其中S.count(Xi)表示Xi在S中出現的次數,S.length表示字符串的長度。最終的信息熵可以根據公式(1)計算出來。
(3)應用程序中Java 反射API 調用次數。根據文獻[5],雖然Java 反射在Android 普通軟件與惡意軟件中均被廣泛使用,但惡意軟件對其的使用頻率遠遠高于普通軟件。遍歷應用中的每個函數,恢復其smali 格式的代碼,并與Java 反射API 作對比,統計其被調用的次數。選擇的smali 格式的Java 反射API 如表3 所示。

表3 選擇的Java 反射API 列表
1.2.5 分類算法
在從App 中提取特征之后,需要使用機器學習算法對特征進行學習,從而實現對Android 惡意代碼的自動化鑒別。機器學習模型種類繁多,各具特點。經過調研,決定采用有監督二分類模型來進行實驗,根據以往文獻的經驗,發現支持向量機與隨機森林模型具有訓練速度快、準確性高的優點,故針對兩者進行了實驗驗證。
本文采用androguard 作為Android 程序分析框架。Androguard 提供了豐富的功能與Python 開發接口,能夠從Android 程序中自動提取出類、方法、成員等信息,并生成對象間的函數調用圖。使用Androguard 抽取出每個Android 程序的七個特征組成向量,并使用scikit learn 庫來對支持向量機模型與隨機森林模型進行訓練和預測。
為了使實驗結果更具有說服力,采用文獻中提到的數據源。這些正常的Android 程序均是從國內第三方市場以及Google Play 上下載,惡意程序主要獲取于VirusShare 網站以及文獻[6]從該數據源中隨機抽取了2 000 個正常的Android 程序與2 000 個惡意的Android 組成了本實驗的數據集。
為了驗證本文所選擇的特征的有效性,將特征分成了4 類。
(1)權限類特征:Android 程序中申請的高危權限數量以及自定義權限數量。
(2)證書類特征:證書是否已經過期。
(3)API 特征:敏感API 被調用的平均次數以及調用路徑的長度。
(4)混淆特征:Android 程序中標識符平均長度、字符串的信息熵以及Java 反射的使用次數。
將數據集按照8:2 的比例拆分為訓練集和測試集,并進行5 次的交叉檢驗。對于機器學習模型,均采用了scikit learn 庫中提供的默認參數,具體的參數信息如表4 所示。
實驗結果表明,當使用全部特征時,隨機森林模型可以達到95.3%的準確率,94.4%的精確率以及98.0%的召回率,相比之下,支持向量機擁有62.6%的準確率,62.0%的精確率與100%的召回率。可以看出,雖然支持向量機能夠將測試集中的全部惡意應用找出,但是同樣存在非常多的誤報,使得最終分類效果不理想。因此實際選擇了隨機森林作為本文的分類模型。確定分類模型之后,與只使用高危權限作為特征的傳統方案進行對比實驗。兩種方案均采用隨機森林作為分類模型,實驗結果如表5 所示。

表4 分類模型默認參數

表5 在選用樣本上的實驗結果
可以看出,相對于傳統方案,本方案的準確率、精確率以及召回率的提升分別為19.4%、16.5%以及13.3%,從而證明了特征與分類模型選擇的有效性。為了更好地理解各個特征對分類結果的貢獻度,輸出了scikit learn 庫中隨機森林的特征重要性,結果顯示,各個特征的權重如表6 所示。

表6 實驗中各個特征的特征重要性
可以看出,權限特征、API 特征以及混淆特征擁有較高的貢獻度,混淆特征的貢獻度甚至超過50%,證明了混淆方式的異同確實能夠有效地鑒別一個應用程序的惡意性。但是標志符的平均長度未能表現出任何區分性,原因可能是測試集中使用復雜重命名方式的惡意應用數量過少,同時普通應用開發者更加注重混淆保護,使得二者的重命名模式難以區分。
在本文中,提出了一種基于多種特征的Android惡意軟件檢測算法。與其他文章相比,在特征中融入了軟件的代碼混淆信息,實驗結果表明:與傳統只使用權限與API 特征的方法相比,代碼混淆信息能夠有效地提升檢測算法的性能,其中準確率提升19.4%,精確率提升16.5%,召回率提升13.4%。在本文所使用的3 種代碼混淆特征中,標識符的長度未能有效區分正常應用與惡意應用,這與越來越多的應用開始使用自動化工具(如proguard)進行標識符重命名有關。相比支持向量機算法,隨機森林算法在Android 惡意軟件的檢測中表現出了更好的性能,精確率與召回率非常接近。
下一步的實驗中,將會著重剖析Android 惡意軟件在代碼混淆上的使用,通過提取更多特征,來多維度地建立Android 惡意軟件的畫像,包括控制流混淆情況、虛假重載等。