鄒圳,周安民
(四川大學電子信息學院,成都610065)
近年來,隨著移動互聯網與信息技術的不斷發展,智能手機得到了迅猛的發展和廣泛的應用。在當前智能手機市場份額中,Android 系統占據了主要部分。根據國際數據中心(IDC)2018 年第三季度的報告[1],Android 在全球市場的占比達到了86.8%。因此,針對Android 平臺的安全性研究得到了普遍的關注。
作為Android 系統的核心組件,系統服務(System Service)是指在后臺運行的系統級進程或應用程序,例如窗口管理服務WindowManager、通知管理服務NotificationManager、電源管理服務PowerManager 等。系統服務對系統功能進行了封裝,并通過接口的形式為上層的應用程序提供服務。對于應用開發者而言,只需要了解這些接口的使用方式,通過Binder 通信機制即可與系統服務進行交互,方便地獲取系統信息與進行系統控制。然而對于攻擊者而言,系統服務也成為了攻擊Android 系統的一個突破口。因此,系統服務安全的重要性不言而喻。
為了系統地對Android 平臺的各種系統服務進行安全性分析,本文在研究Binder 通信機制的基礎上提出了一種面向Android 系統服務的自動化漏洞挖掘框架。針對Android 不同版本系統的實驗結果表明,該框架能夠有效識別Android 中存在的系統服務,并構造帶有畸形參數的請求進行模糊測試,具有一定實用價值。
由于開源和開放的特點,Android 系統的安全性一直吸引著眾多研究人員的關注。在Android 漏洞挖掘技術方面,目前常用的方法主要分為靜態分析技術和動態檢測技術。靜態分析技術[2-4]主要采用代碼審計的思想,通過逆向工程等手段獲取程序源代碼,并結合程序的函數調用關系、數據流和控制流等特征對程序代碼進行分析,從中找出潛在的安全問題。靜態分析技術具有簡單高效、易于實現自動化等優點,可用于組件劫持、信息泄露、權限提升等漏洞挖掘的相關研究工作。靜態分析技術的缺點在于過于依賴漏洞特征,存在漏報和誤報問題。動態檢測技術[5]目前主要以模糊測試技術為主,該技術被廣泛地應用于Android 系統與應用程序的組件間通信機制的安全性研究中。例如Mulliner 等[6]通過構造異常輸入數據來對Android 系統的短信模塊進行模糊測試。Intent Fuzzer 及其相關改進工具[7-9]通過發送Intent 命令的方式來測試Android應用程序中Activity、Service、Broadcast Receiver 這3 類暴露組件的健壯性。DroidFuzzer[10]通過構建MIME 數據來對Android 應用程序的Activity 組件進行模糊測試。然而,上述工具大多著眼于Intent 通信機制,而且目標多為Android 平臺下的各種應用程序。
因此,本文在研究Binder 通信機制的基礎上,通過對Android 系統服務進行統計與分析,結合模糊測試技術對系統服務的安全性展開研究。
系統服務在整個Android 系統中占據著十分重要的地位,它們將Android 提供的各種功能如獲取地理位置、發送短信、檢查網絡連接等進行封裝,并以應用程序編程接口(Application Programming Interface,API)的形式為上層的應用程序提供服務[11]。Android 系統服務主要分為三類:第一類是Java 系統服務,由Java 語言編寫,通過AIDL 文件進行封裝,運行在System Server進程中,這類服務在所有系統服務中占據了絕大部分。其次是本地守護進程,在init.rc 文件中定義并在Android 系統初始化的過程中由init 進程啟動,啟動后會常駐在系統中,這類服務的數量比較少,其中包括負責apk 軟件包的安裝與卸載的installd 守護進程,負責多媒體處理的MediaServer 守護進程,等等。最后是Native 系統服務,由C 或C++語言編寫,運行在本地守護進程中,例如MediaServer 守護進程中就包括AudioFlinger、AudioPolicyService、MediaPlayerService、CameraService 等Native 服務,這類服務同樣也只占很小部分。本文主要對Java 系統服務的安全性進行研究,Android 系統上一些常用的Java 系統服務如表1 所示。

表1 一些常用的系統服務
當上層的應用程序通過API 調用系統服務的時候,實際上是在通過Binder 通信的方式與系統服務進行進程間通信(Inter Process Communication,IPC)。系統服務等待著上層應用程序發出請求,接收到請求后進行響應同時將結果返回給應用程序。如果惡意的應用程序向系統服務傳遞了包含畸形參數的外部輸入數據,可能導致系統服務發生崩潰、設備重啟等拒絕服務的情況,甚至可能造成任意代碼執行的嚴重后果。
為了保護應用和系統免受惡意應用的攻擊,Android 采用了基于用戶的Linux 保護機制,為每個Android 應用設置一個內核級應用沙盒。默認情況下,應用不能彼此交互,而且對操作系統的訪問權限會受到限制。在應用間需要交互的情況下,需要使用IPC 進程間通信方式[12]。與Linux 中常用的Pipe(管道)、Signal(信號)、Message(消息隊列)、Shared Memory(共享內存)、Semaphore(信號量)、Socket(套接字)等基于文件系統的IPC 方式不同,Android 系統選擇了采用C/S 架構、基于權限機制的Binder 通信作為主要IPC 方式[13]。
Android 系統的Binder 通信模型由四大部分構成,分 別 是Client、Server、Service Manager、Binder Driver。其中,Client、Server、Service Manager 運行在用戶空間,Binder Driver 運行在內核空間。Binder Driver 提供了設備文件/dev/binder,用戶空間可通過open、ioctl 等方式與其進行交互;Service Manager 的作用為對系統中的服務進行管理,Server 通過addService()方法將自己注冊到Service Manager,Client 則通過getService()方法獲取Server 信息;Client 和Server 采用了典型的Proxy-Stub 設計模式,由AIDL 工具生成代理Proxy 和Stub 對象,兩端各自調用其中的transact()和onTransact()方法實現對待傳送的Parcel 數據的發送與接收,Client 和Server 之間的IPC 通信是通過底層的驅動程序來間接實現的。整個Binder 通信機制的系統架構如圖1所示。

圖1 Binder通信架構圖
AIDL(Android Interface Definition Language),是Android 系統使用的一種接口定義語言。與其他IDL語言類似,AIDL 主要用于定義Android 系統中Client和Server 端在進行基于Binder 的IPC 進程間通信時雙方都認可的接口規范。在AIDL 機制中,Android 系統會提供一系列的工具將開發者定義的.aidl 文件編譯生成Client 端與Server 端的相關代碼。由于AIDL 對Binder 機制的封裝,使得開發者能夠更簡單方便地實現IPC 通信,而不必關心Binder 的具體細節。
圖2 展示了一個.aidl 文件示例。其中定義了get-Pid 和basicTypes 兩個接口方法,getPid 不帶任何參數,basicTypes 則接收六個不同數據類型的參數。

圖2 IRemoteService.aidl文件示例
當開發者在.aidl 文件中聲明完相關接口和方法的定義后,Android SDK 工具會自動生成與.aidl 文件同名的.java 接口文件,該文件的核心為其中的stub 內部類,該類中包含了.aidl 文件中定義的方法聲明,并且為定義的每個方法分配了一個整型的唯一標識code,以及供Client 端調用的代理類Proxy,和用于接收Binder 通信數據的onTransact()方法。
本文在Binder 通信機制的基礎上提出了一種針對Android 系統服務的漏洞挖掘框架。其基本思路是:首先對Android 系統服務展開統計和分析,獲取系統服務和接口函數的相關信息,為后續的模糊測試用例生成提供指導依據。然后根據一定的測試用例生成策略構造請求數據,通過Binder 驅動將請求數據發送給系統服務。最后通過Android 的logcat 日志系統對系統服務的運行狀態進行監控,記錄進程異常、崩潰、死亡等信息與相應的請求數據。
在對系統服務進行模糊測試之前,首先需要獲取系統服務以及每個服務內定義的所有接口函數的相關信息,包括系統服務名、類名,接口函數的參數個數、參數類型,等等。通過分析這些信息有利于針對性地指導測試用例的生成,大大減少無用的測試用例,從而提高fuzzer 的效率。
(1)獲取服務名稱及句柄
本文采用Java 反射機制來獲取系統服務列表。在Binder 通信機制中,由Service Manager 負責對Android系統中所有的系統服務進行管理。作為一個特殊的系統服務,Service Manager 維持了一個服務列表,其中記錄了所有的系統服務名稱以及句柄。其他服務在被使用之前,需要通過add_service 方法向Service Manager進行注冊,登記自己的服務名以及句柄。同樣的,客戶端在調用系統服務之前,也需要通過check_service 方法向Service Manager 查詢,通過目標系統服務的名稱獲取到相關聯的句柄,并以該句柄作為目的地址發起通信請求。由于Service Manager 具有@hide 隱藏屬性,因此利用Java 的反射機制獲取android.os.ServiceManager 實例,獲取其中的listServices 方法,該方法將返回一個字符串列表,其中即包含需要的所有系統服務信息。
(2)獲取定義方法信息
系統服務信息獲取完畢后,可以類似地利用Java的反射機制嘗試獲取單個系統服務中定義的可供外界調用的所有方法信息。通過向Class.forName()方法傳入系統服務類名,可以獲取到該系統服務的實例對象,然后通過調用getDeclaredMethods()方法將返回一個Method 對象的數組,數組中存儲了該系統服務類中定義的所有方法對象,包括方法名以及對應的參數信息。
此外,系統服務中定義的所有方法都將分配一個方法號,方法號從1 開始逐次遞增,單個服務中定義的N 個方法的方法號即為1 到N。此方法號即為通過transact()函數調用Binder 服務端時填充的第一個參數code。由AIDL 機制的特點可知,可以通過反射獲取系統服務類中的stub 子類對象,從中獲取到每個方法對應的不同code 值。
獲取這些系統服務的相關信息主要有以下兩個作用:①確定模糊測試的測試目標;②在測試用例生成階段使用,根據方法參數信息指導測試用例數據的生成。
基于之前獲取的系統服務信息,針對Binder 通信中通常攜帶的各種數據類型,本文采用一定的生成策略對不同數據類型的數值進行填充,如表2 所示。

表2 數據類型填充模板
除了這些常見的數據類型以外,有時還會出現某些特殊的數據類型導致無法直接構造測試用例的情況,例如Bundle、Interface 作為方法調用的參數等,本文統一使用null 對該類型的數據進行填充。
通過以上的測試用例生成策略,可以生成與系統服務的目標測試方法相對應的測試用例,以便通過目標方法的初始參數檢查,提高模糊測試的效率。
為了能夠將測試用例成功發送給系統服務,本文實現了一個第三方應用程序作為Binder 通信的客戶端。考慮到在Android 系統中對某些系統服務的調用需要一定的權限,因此在該應用程序的AndroidManifest.xml 文件中預先請求了第三方應用程序可以獲取的所有權限。該應用程序通過使用Java 的反射技術來獲取系統服務的句柄,進而調用相應的系統服務,因此具有良好的系統兼容性,可以在不同版本的Android 系統上使用。
應用程序首先嘗試獲取所有可以獲取的系統權限,然后通過反射獲得目標系統服務的句柄IBinder 對象,并且使用IBinder 對象調用transact()函數,該函數最終將觸發系統服務端的onTransact()函數,將測試用例原樣地傳送給系統服務的目標方法。根據transact(int code,Parcel data,Parcel reply,int flags)的函數原型可知,需要對其中的各個參數進行相應的填充。參數code 表示目標方法號,在系統服務信息收集階段已獲取;參數data 表示寫入了各種數據類型的Parcel 對象,等待發往Binder 通信的服務端,這里將依據待測試方法的參數類型寫入對應的測試用例;參數reply 則為空的Parcel 對象,等待存儲系統服務的返回數據;最后的參數flags 則表示Binder 通信的服務端在執行完相應操作后是否會返回數據,這里采用默認值0 進行填充,即等待系統服務返回數據。填充完畢后即可通過transact()函數發起對系統服務的請求調用。
在測試用例發送給目標系統服務之后,需要對系統服務的運行狀態進行監控。本文借助adb 的Logcat工具實現在模糊測試期間的日志監控。記錄的日志信息主要包括以下兩類:
(1)E/F 級別日志:該類別的日志表明系統服務產生了錯誤(error)或者嚴重錯誤(fatal),時常伴隨著系統服務進程的崩潰;
(2)包含有“exception”、“crash”字符串的日志:“exception”表示系統服務拋出了捕獲或者未捕獲的異常,未捕獲的異常可能意味著更為嚴重的安全問題;“crash”則表示系統服務發生崩潰,有的crash 甚至會導致system_process 進程崩潰,造成設備重啟的嚴重情況。
日志監控部分的偽代碼如下:
輸入:測試用例testcases
輸出:系統服務產生崩潰時的日志以及相應的輸入測試用例
Outputs out=executeCMD("adb logcat");
if("E"or"F"in out.logs.level)or("exception"or"crash"in out.logs.message)then
saveFile(out);
saveFile(testcases);
end if
通過對系統服務的異常運行狀態進行監控,并記錄產生異常的系統服務與接口方法,以及造成異常的相應測試用例,為后續定位漏洞以及對源代碼的安全審計提供了可能。
為了驗證系統的有效性,本文分別在Android 7.0、Android 8.0、Android 9.0 三個不同AOSP 版本的系統虛擬機上進行了實驗,發現了系統服務中的一些接口方法由于對接收的Binder 通信數據校驗不嚴格,結果造成自身進程甚至system_process 進程的崩潰,導致系統拒絕服務(DoS)的后果。
系統的實驗環境為安裝有Genymotion Android Emulator 軟件的64 位Windows 10 系統計算機,Intel Xeon E3-1231 v3 CPU,內存8GB,如表3 所示。

表3 實驗環境
對Android 7.0、Android 8.0、Android 9.0 三個版本的系統進行測試后發現,Android 7.0 上擁有117 個系統服務,總共包含2312 個接口方法,經過測試后發現其中102 個接口方法會拋出異常;Android 8.0 上擁有126 個系統服務,總共包含2688 個接口方法,經過測試后發現其中181 個接口方法會拋出異常;Android 9.0上擁有140 個系統服務,總共包含3106 個接口方法,經過測試后發現其中166 個接口方法會拋出異常。測試結果如表4 所示。

表4 測試結果
在拋出異常之后,如果系統服務內部存在相應的異常處理機制,那么正常情況下無法造成嚴重的后果。如果系統服務只是對異常進行簡單的捕獲,而沒有進行相應的處理,或者沒有捕獲到異常,則有可能導致系統服務進程的崩潰。由于Android 系統中的所有系統服務均運行在名為system_server 的系統進程中,某些情況下系統服務的崩潰甚至會導致system_server進程的死亡,進而導致整個Android 系統的重啟。在測試流程中發現某些系統服務的崩潰可以造成整個設備重啟,如表5 所示。

表5 造成Android 系統重啟的服務與方法
由上述的實驗結果可知,本文的設計方案可以很好地應用于Android 系統服務的安全性研究中,挖掘其中可能存在的漏洞,從而提升Android 系統的安全性。
本文提出了一種針對Android 系統服務的漏洞挖掘技術方案。該方法通過獲取系統服務和接口方法的相關信息,基于此基礎上構建覆蓋范圍廣泛的測試用例,并以Binder 通信的方式將測試用例發送給目標服務進行測試,最后的實驗結果表明了技術方案的可行性和測試工具的可用性。
本文的研究工作尚存在一定的局限性,未來可通過結合符號執行等方法對系統服務的程序執行路徑展開分析,借此改進測試用例的生成方案,以提高模糊測試效率。