朱曉宇,牛少彰
(北京郵電大學 計算機學院,北京 100876)
現如今 Android平臺上的很多研究都是基于應用程序在網絡通信過程中的交互數據而開展的。網絡通信數據一旦離開設備發送到網絡上,很難保證這些數據在傳輸過程中會發生什么。研究人員進行網絡數據分析的目的主要有檢測隱私泄露[1]、檢測惡意軟件[2-4]和對惡意加密流量進行標注[5]等。他們進行 Android應用程序網絡流量數據采集的方式主要是通過第三方工具,常用的工具有tcpdump、Whistle、Fiddler等。本文先是分析了在 Android平臺上使用這些工具進行網絡數據采集的局限性。然后提出為了避免對移動終端設備進行root操作,本文將在Android安全容器[6-7]的基礎上進行研究。接著根據安卓軟件棧[8]的架構層級順序依次分析,得到 Android應用程序在不同的層級上與網絡通信相關的代碼實現方式,在目標代碼入口處使用Hook[9]的方式得到在移動終端設備網絡通信過程中的數據采集結果,這有效地避免了之前數據采集不完整的局限性。最終本文將通過分析與比較,設計出一個具備實時性、準確性和完整性特點的網絡數據采集系統。
目前研究人員對 Android平臺上大量的應用程序進行網絡數據采集主要是通過第三方工具。從實現原理上來看,它們可歸結為兩類,分別是以Fiddler為代表的基于代理服務器原理的工具和以tcpdump為代表的基于底層驅動原理的工具。
本文將以目前使用最為廣泛的Fiddler來進行實驗。實驗結果顯示,正常情況下用戶的移動終端設備可以進行 HTTPS網絡請求,但是在使用Fiddler工具時,終端設備進行 HTTPS請求是失敗的。這是因為 Fiddler工具相當于一個 HTTP/HTTPS代理服務器(如圖 1所示),其證書不在用戶的安卓終端設備的受信任證書列表中(實驗中進行網絡數據采集的應用程序為 Android移動終端設備上任意可運行的應用程序)。

圖1 Fiddler原理Fig.1 Fiddler Principle
在 Android平臺上,基于底層驅動的網絡數據采集方式中最有代表性的是tcpdump工具。該工具相當于非圖形界面的WireShark,其目的是監視流經網卡的數據,所以該工具可以捕獲到除HTTP協議以外的其他類型的數據包。使用tcpdump工具的前提是安卓終端設備需要處于root的狀態。實驗結果表明,Http類型的數據包中的請求和響應數據是可以看到的,而 Https的數據包中的數據是經過加密的。
綜上所述,現有的 Android平臺上的網絡數據采集方式的局限性包括兩點,一是數據采集不完整,即采集不到 Https的數據;二是使用基于底層驅動的網絡數據采集方式時,移動終端設備需要root權限。
Android軟件棧可分為 Application層、Framework層、Native層和Linux Kernel層。在本節中將根據 Android軟件棧自下而上的順序,結合對 Android客戶端網絡通信原理的分析來找到可利用的 Hook點。在本文中實現的網絡數據采集系統是建立在Android安全容器的基礎上的,在安全容器中對應用程序進行網絡數據采集可以避免對手機進行root這一操作。
本文中的Android安全容器使用了VirtualApp框架,該框架基于插件化[7]的思想,利用了大量的反射和動態代理的技術。僅使用VirtualApp很難實現我們的目的,所以本網絡數據采集系統在VirtualApp的基礎上結合了Hook技術。我們可以在VirtualApp中安裝將要進行網絡數據采集的目標應用程序,然后通過在VirtualApp中使用Hook的方式攔截目標函數,實現打印通信過程中網絡數據的目的。
目前,Android客戶端進行網絡通信可使用4G和Wifi兩種方式。4G網絡與客戶端中的調制解調器有關,其放置在基帶芯片中,基帶芯片是合成基帶信號或對基帶信號解碼的硬件芯片。所以,4G網絡是通過客戶端中的基帶模塊實現無線網絡通信。Wifi網絡需借助路由器通過以太網實現通信,即Wifi網絡是通過客戶端中的以太網卡模塊實現無線網絡通信。這兩種方式使用的鏈路層協議是不同的,但是與用戶層的通信都要通過Socket接口。
如圖 2所示,無論是客戶端還是服務器端,Socket通信過程中的通信數據對應的字節數組都會經過Socket的讀寫到緩沖區的操作。本文將利用 OkHttp中 okio.Okio類的 sink方法來觀察Socket讀寫到緩沖區操作的函數。通過分別測試HTTP和HTTPS通信并且結合分析JDK的源碼得知,在網絡通信的數據寫入過程中,HTTP底部調用的為java.net.SocketOutputStream的socketWrite方法,HTTPS底部調用的為com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputS-tream的 write方法。實驗結果表明,通過 Hook這兩個方法得到的HTTPS數據是密文。

圖2 Linux內核中Socket通信過程中數據的傳輸流程Fig. 2 Data Transmission Process in the Socket Communication Process in the Linux Kernel
Native層與網絡通信最為相關的模塊為類庫Libraries中的SSL模塊。客戶端與服務器在SSL連接的過程中協商的對稱密鑰將對通信數據進行加密和解密操作。在LeakDoctor[10]中提出對Android加密的API進行Hook,經分析可得知,客戶端發送明文數據給服務器端時調用 libssl.so的 SSL_write方法,服務器端發送密文數據給客戶端時調用 libssl.so的 SSL_read方法,所以需要分別在SSL_write方法執行之前和在SSL_read方法執行之后 Hook對應的方法得到數據信息。獲取到的數據由于Content-Type的不同分別是不同形式編碼的,圖3是攔截SSL_write方法得到的部分數據,該數據片段是使用URL編碼的。

圖3 Native層獲取到的數據片段Fig. 3 Data Fragments obtained by the Native layer
由于 Framework層沒有針對網絡通信的模塊,所以跳過該層,接下來進行Application層網絡通信分析。
本節提出了兩種基于 Android容器的獲取網絡數據的方式,一種是將應用程序中證書檢測的部分忽略掉,另一種對網絡通信框架添加攔截器。
在客戶端與服務器端的HTTPS通信過程中,對證書的校驗首先是確認 CA身份的可信性,在確認了 CA身份的可信性后,客戶端將通過對證書的驗證[11]來確認服務器端是否合法。在這一部分包括對證書域名的匹配性驗證和證書釘扎驗證。如果在客戶端的應用程序中沒有使用證書釘扎驗證的話,Hook掉應用程序代碼中校驗證書的部分,即可使用第三方工具進行數據包的捕獲。通過攔com.android.org.conscrypt.ConscryptFileD-escriptorSocket類的verifyCertificateChain方法,使其不執行,這樣我們就可以跳過證書驗證的部分,通過Fiddler來抓包。實驗表明,這時在Fiddler中展示的HTTPS數據是可見的明文數據。
在實際的業務場景中,針對不同的API規范需要進行不同形式的HTTP調用。從移動客戶端開發人員的角度來看,如果手動寫HTTP調用框架的話,不僅需要考慮到不同的API規范,而且需要考慮到實現HTTP協議的各個方面。所以大部分安卓客戶端應用程序都會選擇使用優秀的網絡開源框架來實現網絡通信。表1中展示了目前移動開發領域幾種較為廣泛使用的網絡開源框架。其中OkHttp是目前使用最為廣泛的框架,且其提供了攔截器的思想,我們可以通過在安全容器中對目標應用程序注入攔截器的方法來獲取網絡通信過程中客戶端與服務器端的交互數據。

表1 網絡開源框架Tab. 1 Network Open Source Framework
根據第2節的分析可知,在Linux Kernel層與網絡通信相關的上層接口的輸入輸出流的數據是經過加密的;在Native層的SSL模塊中,攔截Native層的代碼從而得到明文數據的方式可以在安全容器中使用,但是有些數據需要進一步分析;在Application層,我們通過分析HTTPS的連接過程得到兩種采集數據的方式,但是將應用程序中證書檢測的部分替換掉的方式自動化程度不高,對框架進行攔截的方式較為可行。所以,本文中的網絡數據采集系統將結合攔截 Native層libssl.so和攔截 Application層網絡通信框架這兩種方式來實現。本文提到的網絡數據采集系統的設計流程圖如圖4所示。

圖4 網絡數據采集系統的設計流程圖Fig.4 Design Flow Chart of Network Data Collection System
在安全容器中運行目標應用程序,當目標應用程序啟動時,可以得到該應用程序對應的類加載器。獲取到類加載器之后,我們就可以利用反射的思想來獲取到該目標應用程序中相關的類、方法以及屬性字段。查找到這些類之后,首先,攔截構造OkHttpClient對象的函數,攔截構造函數的目的是為了給其添加日志攔截器。其次,在OkHttpClient中,添加的攔截器都會存儲到 Interceptor類型的List列表中,所以將我們初始化的日志攔截器通過反射的方式添加到這個List列表中即可。如果目標應用程序中沒有日志攔截器相關類,將把攔截器類合并到目標應用程序的 dex文件中。如果目標應用程序沒有使用網絡通信框架,那么我們將在安全容器中導入編譯好的對native層函數進行攔截的so庫,接著調用相應的native層函數。
在本文中,首先需要獲取到該目標應用程序中所有包含okhttp和okio的類名,得到對應的類,然后將這些類添加到列表中。接著初始化列表中的這些類,因為Class對象是JVM在加載類時自動構造的,所以可以通過Class.forName來得到在目標應用程序中想要的 Class對象。在這一過程中,考慮到在目標應用程序中沒有找到目標Class對象這一現象,這說明該目標應用程序對OkHttp相關的代碼進行了混淆或者該應用在進行網絡請求時沒有使用OkHttp框架。如果經過了混淆,那么可以通過相應類的代碼特性(比如說被聲明為final類型的屬性的數量等)來找到該應用中對應的類。在這一步中我們查找的為OkHttpClient類、Builder類、Interceptor類、HttpLoggingInterceptor類以及HttpLoggingInterceptor$Logger類。
當一個應用使用square公司在開源代碼中提供的攔截器時,需要該應用程序的項目代碼具有該攔截器代碼的壓縮包,這個一般是通過 gradle下載的。所以在這里需要分兩種情況,第一種情況是目標應用程序中具有該攔截器代碼相應的壓縮包,這時我們就可以直接得到該類的 Class對象;第二種情況是目標應用程序中不具有該攔截器代碼相應的壓縮包,此時就需要考慮在本地計算機中下載該壓縮包對應的jar包,接著將該jar包轉換為 dex文件(使用本地計算機的 SDK的dx工具),然后將該dex文件注入到目標應用程序所在apk的dex文件中,這樣在該目標應用程序啟動時,就會加載我們想要的攔截器的代碼文件。
在上述的第二種情況中,我們下載到本地計算機中的壓縮包為okio-1.15.0.jar和logging-interceptor-3.12.0.jar,轉換后的 dex文件對應的分別為 logging.jar和 okio.jar。
接下來會詳細敘述一下針對第二種情況的操作步驟。第一步,將logging.jar和okio.jar放置到安全容器所在程序的Assets目錄下,然后通過代碼將其賦值到手機的“/data/data/安全容器包名/app_文件夾名/logging.dex3.jar”中(注意,此時的logging.dex3.jar本質上為一個dex文件)。第二步,在安全容器中從目標應用程序的ClassLoader對象中拿到屬性 pathList,該屬性是 DexPathList類型的,是從其父類BaseClassLoader中繼承而來的,表示需要加載的dex列表。第三步,拿到第二步中得到的pathList屬性所對應的DexPathList對象的Element類型的數組對象dexElements,即dex列表相當于是存儲在數組dexElements中。第四步,使用DexClassLoader動態加載“/data/data/安全容器包名/app_文件夾名/logging.dex3.jar”路徑中的dex文件,類似于步驟二和步驟三的操作,得到該ClassLoader中的Element類型的數組。然后將第三步和第四步中拿到的兩個數組合并,最后將合并完的數組,通過反射設置到該目標應用程序的 ClassLoader對應的數組中去,這樣就完成了合并dex的工作。本文中合并dex文件的入口點是在安全容器中安裝的目標應用程序的首頁啟動時,在完成dex合并之后,我們會通過該目標應用程序的classLoader手動地去加載與攔截器相關的類。
經過上述步驟后,就可以在目標應用程序中初始化一個日志攔截器,來對網絡交互的數據進行打印。
實驗過程中分批從安智市場下載了閱讀類、音樂類、旅游出行類、社交類和辦公類等近百個應用,為了避免混淆,在我們的網絡數據采集系統中每次只對一個目標應用程序進行測試。在實驗過程中發現,大部分應用都采用了OkHttp框架進行網絡通信。圖5展示的是通過我們的攔截器框架拿到的某購書類應用程序的部分 HTTPS網絡數據交互的一部分數據片段。實驗表明,我們的網絡數據采集系統具有實時性、準確性和完整性的特點。
本文按照 Android軟件棧的層次對涉及到的網絡通信技術依次進行分析,進而提出一個基于Android安全容器的網絡數據采集系統。本論文的研究對 Android平臺上的應用程序行為分析具有重要的意義,同時也將引起人們對網絡通信過程中數據安全性的重視。