朱曉宇,牛少彰
(北京郵電大學(xué) 計(jì)算機(jī)學(xué)院,北京 100876)
現(xiàn)如今 Android平臺(tái)上的很多研究都是基于應(yīng)用程序在網(wǎng)絡(luò)通信過(guò)程中的交互數(shù)據(jù)而開(kāi)展的。網(wǎng)絡(luò)通信數(shù)據(jù)一旦離開(kāi)設(shè)備發(fā)送到網(wǎng)絡(luò)上,很難保證這些數(shù)據(jù)在傳輸過(guò)程中會(huì)發(fā)生什么。研究人員進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)分析的目的主要有檢測(cè)隱私泄露[1]、檢測(cè)惡意軟件[2-4]和對(duì)惡意加密流量進(jìn)行標(biāo)注[5]等。他們進(jìn)行 Android應(yīng)用程序網(wǎng)絡(luò)流量數(shù)據(jù)采集的方式主要是通過(guò)第三方工具,常用的工具有tcpdump、Whistle、Fiddler等。本文先是分析了在 Android平臺(tái)上使用這些工具進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)采集的局限性。然后提出為了避免對(duì)移動(dòng)終端設(shè)備進(jìn)行root操作,本文將在Android安全容器[6-7]的基礎(chǔ)上進(jìn)行研究。接著根據(jù)安卓軟件棧[8]的架構(gòu)層級(jí)順序依次分析,得到 Android應(yīng)用程序在不同的層級(jí)上與網(wǎng)絡(luò)通信相關(guān)的代碼實(shí)現(xiàn)方式,在目標(biāo)代碼入口處使用Hook[9]的方式得到在移動(dòng)終端設(shè)備網(wǎng)絡(luò)通信過(guò)程中的數(shù)據(jù)采集結(jié)果,這有效地避免了之前數(shù)據(jù)采集不完整的局限性。最終本文將通過(guò)分析與比較,設(shè)計(jì)出一個(gè)具備實(shí)時(shí)性、準(zhǔn)確性和完整性特點(diǎn)的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)。
目前研究人員對(duì) Android平臺(tái)上大量的應(yīng)用程序進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)采集主要是通過(guò)第三方工具。從實(shí)現(xiàn)原理上來(lái)看,它們可歸結(jié)為兩類,分別是以Fiddler為代表的基于代理服務(wù)器原理的工具和以tcpdump為代表的基于底層驅(qū)動(dòng)原理的工具。
本文將以目前使用最為廣泛的Fiddler來(lái)進(jìn)行實(shí)驗(yàn)。實(shí)驗(yàn)結(jié)果顯示,正常情況下用戶的移動(dòng)終端設(shè)備可以進(jìn)行 HTTPS網(wǎng)絡(luò)請(qǐng)求,但是在使用Fiddler工具時(shí),終端設(shè)備進(jìn)行 HTTPS請(qǐng)求是失敗的。這是因?yàn)?Fiddler工具相當(dāng)于一個(gè) HTTP/HTTPS代理服務(wù)器(如圖 1所示),其證書不在用戶的安卓終端設(shè)備的受信任證書列表中(實(shí)驗(yàn)中進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)采集的應(yīng)用程序?yàn)?Android移動(dòng)終端設(shè)備上任意可運(yùn)行的應(yīng)用程序)。
圖1 Fiddler原理Fig.1 Fiddler Principle
在 Android平臺(tái)上,基于底層驅(qū)動(dòng)的網(wǎng)絡(luò)數(shù)據(jù)采集方式中最有代表性的是tcpdump工具。該工具相當(dāng)于非圖形界面的WireShark,其目的是監(jiān)視流經(jīng)網(wǎng)卡的數(shù)據(jù),所以該工具可以捕獲到除HTTP協(xié)議以外的其他類型的數(shù)據(jù)包。使用tcpdump工具的前提是安卓終端設(shè)備需要處于root的狀態(tài)。實(shí)驗(yàn)結(jié)果表明,Http類型的數(shù)據(jù)包中的請(qǐng)求和響應(yīng)數(shù)據(jù)是可以看到的,而 Https的數(shù)據(jù)包中的數(shù)據(jù)是經(jīng)過(guò)加密的。
綜上所述,現(xiàn)有的 Android平臺(tái)上的網(wǎng)絡(luò)數(shù)據(jù)采集方式的局限性包括兩點(diǎn),一是數(shù)據(jù)采集不完整,即采集不到 Https的數(shù)據(jù);二是使用基于底層驅(qū)動(dòng)的網(wǎng)絡(luò)數(shù)據(jù)采集方式時(shí),移動(dòng)終端設(shè)備需要root權(quán)限。
Android軟件??煞譃?Application層、Framework層、Native層和Linux Kernel層。在本節(jié)中將根據(jù) Android軟件棧自下而上的順序,結(jié)合對(duì) Android客戶端網(wǎng)絡(luò)通信原理的分析來(lái)找到可利用的 Hook點(diǎn)。在本文中實(shí)現(xiàn)的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)是建立在Android安全容器的基礎(chǔ)上的,在安全容器中對(duì)應(yīng)用程序進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)采集可以避免對(duì)手機(jī)進(jìn)行root這一操作。
本文中的Android安全容器使用了VirtualApp框架,該框架基于插件化[7]的思想,利用了大量的反射和動(dòng)態(tài)代理的技術(shù)。僅使用VirtualApp很難實(shí)現(xiàn)我們的目的,所以本網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)在VirtualApp的基礎(chǔ)上結(jié)合了Hook技術(shù)。我們可以在VirtualApp中安裝將要進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)采集的目標(biāo)應(yīng)用程序,然后通過(guò)在VirtualApp中使用Hook的方式攔截目標(biāo)函數(shù),實(shí)現(xiàn)打印通信過(guò)程中網(wǎng)絡(luò)數(shù)據(jù)的目的。
目前,Android客戶端進(jìn)行網(wǎng)絡(luò)通信可使用4G和Wifi兩種方式。4G網(wǎng)絡(luò)與客戶端中的調(diào)制解調(diào)器有關(guān),其放置在基帶芯片中,基帶芯片是合成基帶信號(hào)或?qū)鶐盘?hào)解碼的硬件芯片。所以,4G網(wǎng)絡(luò)是通過(guò)客戶端中的基帶模塊實(shí)現(xiàn)無(wú)線網(wǎng)絡(luò)通信。Wifi網(wǎng)絡(luò)需借助路由器通過(guò)以太網(wǎng)實(shí)現(xiàn)通信,即Wifi網(wǎng)絡(luò)是通過(guò)客戶端中的以太網(wǎng)卡模塊實(shí)現(xiàn)無(wú)線網(wǎng)絡(luò)通信。這兩種方式使用的鏈路層協(xié)議是不同的,但是與用戶層的通信都要通過(guò)Socket接口。
如圖 2所示,無(wú)論是客戶端還是服務(wù)器端,Socket通信過(guò)程中的通信數(shù)據(jù)對(duì)應(yīng)的字節(jié)數(shù)組都會(huì)經(jīng)過(guò)Socket的讀寫到緩沖區(qū)的操作。本文將利用 OkHttp中 okio.Okio類的 sink方法來(lái)觀察Socket讀寫到緩沖區(qū)操作的函數(shù)。通過(guò)分別測(cè)試HTTP和HTTPS通信并且結(jié)合分析JDK的源碼得知,在網(wǎng)絡(luò)通信的數(shù)據(jù)寫入過(guò)程中,HTTP底部調(diào)用的為java.net.SocketOutputStream的socketWrite方法,HTTPS底部調(diào)用的為com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputS-tream的 write方法。實(shí)驗(yàn)結(jié)果表明,通過(guò) Hook這兩個(gè)方法得到的HTTPS數(shù)據(jù)是密文。
圖2 Linux內(nèi)核中Socket通信過(guò)程中數(shù)據(jù)的傳輸流程Fig. 2 Data Transmission Process in the Socket Communication Process in the Linux Kernel
Native層與網(wǎng)絡(luò)通信最為相關(guān)的模塊為類庫(kù)Libraries中的SSL模塊??蛻舳伺c服務(wù)器在SSL連接的過(guò)程中協(xié)商的對(duì)稱密鑰將對(duì)通信數(shù)據(jù)進(jìn)行加密和解密操作。在LeakDoctor[10]中提出對(duì)Android加密的API進(jìn)行Hook,經(jīng)分析可得知,客戶端發(fā)送明文數(shù)據(jù)給服務(wù)器端時(shí)調(diào)用 libssl.so的 SSL_write方法,服務(wù)器端發(fā)送密文數(shù)據(jù)給客戶端時(shí)調(diào)用 libssl.so的 SSL_read方法,所以需要分別在SSL_write方法執(zhí)行之前和在SSL_read方法執(zhí)行之后 Hook對(duì)應(yīng)的方法得到數(shù)據(jù)信息。獲取到的數(shù)據(jù)由于Content-Type的不同分別是不同形式編碼的,圖3是攔截SSL_write方法得到的部分?jǐn)?shù)據(jù),該數(shù)據(jù)片段是使用URL編碼的。
圖3 Native層獲取到的數(shù)據(jù)片段Fig. 3 Data Fragments obtained by the Native layer
由于 Framework層沒(méi)有針對(duì)網(wǎng)絡(luò)通信的模塊,所以跳過(guò)該層,接下來(lái)進(jìn)行Application層網(wǎng)絡(luò)通信分析。
本節(jié)提出了兩種基于 Android容器的獲取網(wǎng)絡(luò)數(shù)據(jù)的方式,一種是將應(yīng)用程序中證書檢測(cè)的部分忽略掉,另一種對(duì)網(wǎng)絡(luò)通信框架添加攔截器。
在客戶端與服務(wù)器端的HTTPS通信過(guò)程中,對(duì)證書的校驗(yàn)首先是確認(rèn) CA身份的可信性,在確認(rèn)了 CA身份的可信性后,客戶端將通過(guò)對(duì)證書的驗(yàn)證[11]來(lái)確認(rèn)服務(wù)器端是否合法。在這一部分包括對(duì)證書域名的匹配性驗(yàn)證和證書釘扎驗(yàn)證。如果在客戶端的應(yīng)用程序中沒(méi)有使用證書釘扎驗(yàn)證的話,Hook掉應(yīng)用程序代碼中校驗(yàn)證書的部分,即可使用第三方工具進(jìn)行數(shù)據(jù)包的捕獲。通過(guò)攔com.android.org.conscrypt.ConscryptFileD-escriptorSocket類的verifyCertificateChain方法,使其不執(zhí)行,這樣我們就可以跳過(guò)證書驗(yàn)證的部分,通過(guò)Fiddler來(lái)抓包。實(shí)驗(yàn)表明,這時(shí)在Fiddler中展示的HTTPS數(shù)據(jù)是可見(jiàn)的明文數(shù)據(jù)。
在實(shí)際的業(yè)務(wù)場(chǎng)景中,針對(duì)不同的API規(guī)范需要進(jìn)行不同形式的HTTP調(diào)用。從移動(dòng)客戶端開(kāi)發(fā)人員的角度來(lái)看,如果手動(dòng)寫HTTP調(diào)用框架的話,不僅需要考慮到不同的API規(guī)范,而且需要考慮到實(shí)現(xiàn)HTTP協(xié)議的各個(gè)方面。所以大部分安卓客戶端應(yīng)用程序都會(huì)選擇使用優(yōu)秀的網(wǎng)絡(luò)開(kāi)源框架來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)通信。表1中展示了目前移動(dòng)開(kāi)發(fā)領(lǐng)域幾種較為廣泛使用的網(wǎng)絡(luò)開(kāi)源框架。其中OkHttp是目前使用最為廣泛的框架,且其提供了攔截器的思想,我們可以通過(guò)在安全容器中對(duì)目標(biāo)應(yīng)用程序注入攔截器的方法來(lái)獲取網(wǎng)絡(luò)通信過(guò)程中客戶端與服務(wù)器端的交互數(shù)據(jù)。
表1 網(wǎng)絡(luò)開(kāi)源框架Tab. 1 Network Open Source Framework
根據(jù)第2節(jié)的分析可知,在Linux Kernel層與網(wǎng)絡(luò)通信相關(guān)的上層接口的輸入輸出流的數(shù)據(jù)是經(jīng)過(guò)加密的;在Native層的SSL模塊中,攔截Native層的代碼從而得到明文數(shù)據(jù)的方式可以在安全容器中使用,但是有些數(shù)據(jù)需要進(jìn)一步分析;在Application層,我們通過(guò)分析HTTPS的連接過(guò)程得到兩種采集數(shù)據(jù)的方式,但是將應(yīng)用程序中證書檢測(cè)的部分替換掉的方式自動(dòng)化程度不高,對(duì)框架進(jìn)行攔截的方式較為可行。所以,本文中的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)將結(jié)合攔截 Native層libssl.so和攔截 Application層網(wǎng)絡(luò)通信框架這兩種方式來(lái)實(shí)現(xiàn)。本文提到的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)的設(shè)計(jì)流程圖如圖4所示。
圖4 網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)的設(shè)計(jì)流程圖Fig.4 Design Flow Chart of Network Data Collection System
在安全容器中運(yùn)行目標(biāo)應(yīng)用程序,當(dāng)目標(biāo)應(yīng)用程序啟動(dòng)時(shí),可以得到該應(yīng)用程序?qū)?yīng)的類加載器。獲取到類加載器之后,我們就可以利用反射的思想來(lái)獲取到該目標(biāo)應(yīng)用程序中相關(guān)的類、方法以及屬性字段。查找到這些類之后,首先,攔截構(gòu)造OkHttpClient對(duì)象的函數(shù),攔截構(gòu)造函數(shù)的目的是為了給其添加日志攔截器。其次,在OkHttpClient中,添加的攔截器都會(huì)存儲(chǔ)到 Interceptor類型的List列表中,所以將我們初始化的日志攔截器通過(guò)反射的方式添加到這個(gè)List列表中即可。如果目標(biāo)應(yīng)用程序中沒(méi)有日志攔截器相關(guān)類,將把攔截器類合并到目標(biāo)應(yīng)用程序的 dex文件中。如果目標(biāo)應(yīng)用程序沒(méi)有使用網(wǎng)絡(luò)通信框架,那么我們將在安全容器中導(dǎo)入編譯好的對(duì)native層函數(shù)進(jìn)行攔截的so庫(kù),接著調(diào)用相應(yīng)的native層函數(shù)。
在本文中,首先需要獲取到該目標(biāo)應(yīng)用程序中所有包含okhttp和okio的類名,得到對(duì)應(yīng)的類,然后將這些類添加到列表中。接著初始化列表中的這些類,因?yàn)镃lass對(duì)象是JVM在加載類時(shí)自動(dòng)構(gòu)造的,所以可以通過(guò)Class.forName來(lái)得到在目標(biāo)應(yīng)用程序中想要的 Class對(duì)象。在這一過(guò)程中,考慮到在目標(biāo)應(yīng)用程序中沒(méi)有找到目標(biāo)Class對(duì)象這一現(xiàn)象,這說(shuō)明該目標(biāo)應(yīng)用程序?qū)kHttp相關(guān)的代碼進(jìn)行了混淆或者該應(yīng)用在進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)沒(méi)有使用OkHttp框架。如果經(jīng)過(guò)了混淆,那么可以通過(guò)相應(yīng)類的代碼特性(比如說(shuō)被聲明為final類型的屬性的數(shù)量等)來(lái)找到該應(yīng)用中對(duì)應(yīng)的類。在這一步中我們查找的為OkHttpClient類、Builder類、Interceptor類、HttpLoggingInterceptor類以及HttpLoggingInterceptor$Logger類。
當(dāng)一個(gè)應(yīng)用使用square公司在開(kāi)源代碼中提供的攔截器時(shí),需要該應(yīng)用程序的項(xiàng)目代碼具有該攔截器代碼的壓縮包,這個(gè)一般是通過(guò) gradle下載的。所以在這里需要分兩種情況,第一種情況是目標(biāo)應(yīng)用程序中具有該攔截器代碼相應(yīng)的壓縮包,這時(shí)我們就可以直接得到該類的 Class對(duì)象;第二種情況是目標(biāo)應(yīng)用程序中不具有該攔截器代碼相應(yīng)的壓縮包,此時(shí)就需要考慮在本地計(jì)算機(jī)中下載該壓縮包對(duì)應(yīng)的jar包,接著將該jar包轉(zhuǎn)換為 dex文件(使用本地計(jì)算機(jī)的 SDK的dx工具),然后將該dex文件注入到目標(biāo)應(yīng)用程序所在apk的dex文件中,這樣在該目標(biāo)應(yīng)用程序啟動(dòng)時(shí),就會(huì)加載我們想要的攔截器的代碼文件。
在上述的第二種情況中,我們下載到本地計(jì)算機(jī)中的壓縮包為okio-1.15.0.jar和logging-interceptor-3.12.0.jar,轉(zhuǎn)換后的 dex文件對(duì)應(yīng)的分別為 logging.jar和 okio.jar。
接下來(lái)會(huì)詳細(xì)敘述一下針對(duì)第二種情況的操作步驟。第一步,將logging.jar和okio.jar放置到安全容器所在程序的Assets目錄下,然后通過(guò)代碼將其賦值到手機(jī)的“/data/data/安全容器包名/app_文件夾名/logging.dex3.jar”中(注意,此時(shí)的logging.dex3.jar本質(zhì)上為一個(gè)dex文件)。第二步,在安全容器中從目標(biāo)應(yīng)用程序的ClassLoader對(duì)象中拿到屬性 pathList,該屬性是 DexPathList類型的,是從其父類BaseClassLoader中繼承而來(lái)的,表示需要加載的dex列表。第三步,拿到第二步中得到的pathList屬性所對(duì)應(yīng)的DexPathList對(duì)象的Element類型的數(shù)組對(duì)象dexElements,即dex列表相當(dāng)于是存儲(chǔ)在數(shù)組dexElements中。第四步,使用DexClassLoader動(dòng)態(tài)加載“/data/data/安全容器包名/app_文件夾名/logging.dex3.jar”路徑中的dex文件,類似于步驟二和步驟三的操作,得到該ClassLoader中的Element類型的數(shù)組。然后將第三步和第四步中拿到的兩個(gè)數(shù)組合并,最后將合并完的數(shù)組,通過(guò)反射設(shè)置到該目標(biāo)應(yīng)用程序的 ClassLoader對(duì)應(yīng)的數(shù)組中去,這樣就完成了合并dex的工作。本文中合并dex文件的入口點(diǎn)是在安全容器中安裝的目標(biāo)應(yīng)用程序的首頁(yè)啟動(dòng)時(shí),在完成dex合并之后,我們會(huì)通過(guò)該目標(biāo)應(yīng)用程序的classLoader手動(dòng)地去加載與攔截器相關(guān)的類。
經(jīng)過(guò)上述步驟后,就可以在目標(biāo)應(yīng)用程序中初始化一個(gè)日志攔截器,來(lái)對(duì)網(wǎng)絡(luò)交互的數(shù)據(jù)進(jìn)行打印。
實(shí)驗(yàn)過(guò)程中分批從安智市場(chǎng)下載了閱讀類、音樂(lè)類、旅游出行類、社交類和辦公類等近百個(gè)應(yīng)用,為了避免混淆,在我們的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)中每次只對(duì)一個(gè)目標(biāo)應(yīng)用程序進(jìn)行測(cè)試。在實(shí)驗(yàn)過(guò)程中發(fā)現(xiàn),大部分應(yīng)用都采用了OkHttp框架進(jìn)行網(wǎng)絡(luò)通信。圖5展示的是通過(guò)我們的攔截器框架拿到的某購(gòu)書類應(yīng)用程序的部分 HTTPS網(wǎng)絡(luò)數(shù)據(jù)交互的一部分?jǐn)?shù)據(jù)片段。實(shí)驗(yàn)表明,我們的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)具有實(shí)時(shí)性、準(zhǔn)確性和完整性的特點(diǎn)。
本文按照 Android軟件棧的層次對(duì)涉及到的網(wǎng)絡(luò)通信技術(shù)依次進(jìn)行分析,進(jìn)而提出一個(gè)基于Android安全容器的網(wǎng)絡(luò)數(shù)據(jù)采集系統(tǒng)。本論文的研究對(duì) Android平臺(tái)上的應(yīng)用程序行為分析具有重要的意義,同時(shí)也將引起人們對(duì)網(wǎng)絡(luò)通信過(guò)程中數(shù)據(jù)安全性的重視。