Android污點分析工具flowdroid源碼簡析
flowdroid是一款對Android app進行風險分析的應用,下面深入其源碼對其工作的流程進行相關的探究。
1、準備
a)
下載相關源碼(包括soot、heros、jasmin、soot-infoflow、soot-infoflow、soot-infoflow-android)到同一文件夾中,使用eclipse將源碼依次導入就完成了整體項目的導入,盡量使用最新版eclipse,如果版本太老,導入後可能會出現各種問題;完成導入後整體項目結構如下所示:
b)
本次測試使用的APK是flowdroid本身提供的一個apk:enriched1.apk,位於soot-infoflow-android/testAPKs目錄下,該應用包含一個主Activity,下圖展示了Mainfest.xml文件的具體內容:主要的操作是在主Activity的onCreate方法中獲取設備ID(DeviceId)然後通過簡訊的形式發送出去,使用Jimple代碼表示如下:
c)
source(風險產生點)與sink(風險觸發點)點,使用flowdroid源碼中提供的:SourceAndSink.txt,位於soot-infoflow-android根目錄下;下面展示本次使用的source點、sink點,從中可以看出source、sink的定義不僅包含了Jimple格式的方法聲明,也包含了調用該方法需要聲明的許可權,從上圖的Manifest文件中可以看出,這兩個方法需要使用的許可權均已聲明;d)
回調函數(執行某些回調操作將調用的函數)使用soot-infoflow-android根目錄下的AndroidCallbacks.txt;e)
污染易傳播點,表示當source(風險產生點)經過該函數後,新產生的變數將會成為新的source點,如list = List.add(source1),list將會成為新的source源;該文件使用soot-infoflow根目錄下的EasyTaintWrapperSource.txt文件;f)
本地運行的主函數位於soot-infoflow-android/src/soot.jimple.infoflow.android.TestApps目錄下的Test.java文件中,在Run Configurations中配置待掃描apk文件地址及android.jar目錄地址,配置詳情如下圖所示:至此一切配置就緒,點擊上圖Run按鈕運行,就會看到flowdroid運行起來了,經過不到一分鐘(這是demo的時間,複雜的應用就呵呵吧)的分析,就能夠看到運行輸入的結果,先來一張運行結果圖:
由於結果太長,不能完全展示結果的內容,下面將結果拷貝下來,用文字進行展示:
至此我們已經完成了項目導入及運行一個demo程序,下面正式進入源碼的分析。
2、收集source點、sink點、入口點(Entrypoint)
這個題目是根據執行的第一個關鍵函數(如下)取的,但是這個函數實際的作用其實有一些名不副實,該函數位於soot-infoflow-android/src/soot.jimple.infoflow.android.TestApps/Test.java的642行:
從main()函數的起始點到該函數之間的一些操作,主要是變數初始化、賦值的一些操作,在此不再詳述;進入該函數,該函數主要進行一個操作,根據source、sink文件定義類型,對文件中的內容進行提取,然後進入calculateSourcesSinksEntrypoints(parser)函數,其參數parser就是對source、sink點進行解析後的變數,解析函數:
這個函數是對txt文件進行解析;進入
calculateSourcesSinksEntrypoints這個
函數,大致瀏覽下該函數,函數應該是這個本次操作的主函數,對其操作進行拆解,首先執行操作的代碼如下所示:上面代碼的主要作用是反編譯並解析該APK的Manifest.xml文件,生成變數processMan,獲取其packagename並賦值給this.appPackageName=」de.ecspride.reflectionprivacyleak1」,並獲取其程序的入口點,Android應用的入口點實際上就是其定義的四大組件(Activity、Broadcast、Provider、Service),此應用只定義了一個Activity,因此
然後使用ARSCFileParser類反編譯apk文件,並賦值給變數resParser,然後進入calculateCallbackMethods(resParser, lfp)函數,其參數resParser表示剛反編譯的apk文件,lfp表示對布局文件的反編譯,屬於類LayoutFileParser,進入calculateCallbackMethods函數,首先看到的就是對soot的一些操作,如下所示:
第一個語句soot.G.reset()是一個標準的soot操作,用於清空soot之前所有操作遺留下的緩存值,後面的代碼中將會多次使用該操作;
第二個語句initializeSoot(true)的完整函數如下所示,參數true表示是否構建程序調用圖(控制流圖);該函數主要作用是反編譯.dex文件,反編譯生成jimple文件,為查看方便,函數的說明將會在代碼註解中詳細給出;
初始化soot完成後,進入第三個語句createMainMethod(),其代碼如下所示;其主要的操作是構造一個虛擬的main方法,並將入口點(entrypoint)相關類添加到這個虛方法中;
此處在將entrypoint塞入虛擬main方法中的時候,由於entryponit是Android的四大組件,因此在塞入main方法中的時候需要對組件進行建模,建模的方法是根據組件的生命周期(onCreate、onStart、onResume、onPause、onStop、onRestart、onDestory),依次塞入其相關的方法,下面給出生成的
dummyMainMethod
方法主體函數,可以從中感受下。
接下來便是進行回調函數的收集,主要的操作函數如下所示:
其操作是遍歷entrypoint,然後將entry中的回調函數添加到this.callbackMethods變數中,這裡執行的函數在else中,添加到this.callbackMethods中的值為:
從中可以看出添加的回調函數主要是該Activity生命周期中的函數,這樣在上面生成的虛main方法調用到onCreate這些方法時,就會到this.callbackMethods中尋找相關的方法;隨後還會進入xml文件中分析相關的回調函數,這裡沒有涉及。
calculateCallbackMethods(resParser, lfp)函數執行完成後,跳轉回calculateSourcesSinksEntrypoints()函數,至此完成了對Entry point、回調函數的收集,下一步就是完成source、sink的收集,這一步的操作並不涉及soot的相關操作,只是將SourceAndSink.txt文件中包含的source、sink點,封裝到sourceSinkManager中,具體在代碼中搜索source、sink點,在數據流追蹤中完成;
到此已經完成了第一步的操作,主要是做一些分析前的準備工作,包括收集入口點(entrypoint)、回調函數(callback)、source點、sink點,為後面的數據流分析做準備。
3、數據流分析
數據流的分析主要依賴heros工具,可能大家有些時候對heros、jasmin與soot的關係理不大清,heros、jasmin是基於soot開發的工具,相當於soot的插件,不能獨立運行,因為沒有自己的main調用方法,目前下載最新版的編譯後的soot.jar裡面默認是包含這兩個工具的。
上面進行初步的分析工作,下面將正式進行數據流的分析,即執行完Test.java/runAnalysis()方法的642行app.calculateSourcesSinksEntrypoints(「SourcesAndSinks.txt」)後,繼續向下執行,然後執行到數據流分析的入口點,651行:
final InfoflowResults res = app.runInfoflow(new MyResultsAvailableHandler()),
進入runInfoflow()函數,函數的前半部分主要進行一些賦值、初始化的操作,其中比較重要的是實例化一個info變數,該變數是Infoflow類的實例對象,然後使用info.computeInfoflow(apkFileLocation, path, entryPointCreator, sourceSinkManager)進行實際的分析,四個參數的含義為:
apkFileLocation:待分析apk文件的地址;
path:android.jar文件的地址,用於後面反編譯使用;
entryPointCreator:前面獲得的應用的入口函數;
sourceSinkManager:從SourceAndSink.txt文件中獲取的source點與sink點,一共包括89個source點、133個sink點;
進入該函數,其代碼如下所示,從diamante中可以看出,其操作跟2中的操作相類似:初始化soot,然後構造虛擬main方法並設置為入口點,最後使用runAnalysis()方法進行分析。
進入runAnalysis函數,首先第一個比較重要的操作就是使用soot構造控制流圖:constructCallgraph();先進入該函數,為方便說明,將在代碼中對關鍵部分進行註解;通過該方法生成控制流圖(callgraph)後,獲取控制流圖變數:
CallGraph appCallGraph = Scene.v().getCallGraph();
控制流圖在整個分析中非常關鍵,後面將有獨立的章節介紹控制流圖。
下面就是構建最關鍵的ICFG圖,我把它叫做數據流圖,數據流圖的構成推薦大家看flowdroid推薦的相關論文,很經典的演算法,想要把它講明白需要很大的篇幅,如果有機會,我會單獨寫一篇關於數據流圖構成的文章,原論文中的數據流圖在圖形展示上會給人造成一些誤解,容易造成混淆。生成ICFG代碼如下所示,此處不再深入此代碼。
使用ICFG進行數據流分析,其代碼是非常冗長的,但是這些代碼大部分都是一些初始化的工作,看多了很容易把你繞暈,個人覺得這裡面的關鍵操作主要在於兩點:
一是對於source、sink點的統計,其代碼如下所示;遍歷應用所有的方法,然後使用scanMethodForSourcesSinks函數對方法內的source、sink進行統計,並返回該方法中包含的sink點的數量;
進入scanMethodForSourcesSinks方法,源碼如下所示;其主要操作是通過判斷方法是否存在方法體,如果存在方法體,則遍歷方法體中的所有語句,soot中定義為Unit對象,可以將其強制轉化為Stmt對象,可以理解為jimple形式的java語句,然後判斷該語句是否在source、sink中包含,被包含的話,如果是source點,則首先將其作為初始0向量(ICFG圖起始點)存入zeroValue中,然後保存到collectedSources容器中;如果是sink點則直接存儲到collectedSinks容器中,這個尋找source點的方法,在後面介紹控制流圖的時候會使用到。
二是調用前向追蹤方法:forwardSolver.solve(),確認source點到sink點是否存在聯通的數據路徑,如果存在則認為是一個風險點。前向追蹤的演算法主要在heros中實現,此處不再展開;最終分析的結果保存在results變數中,通過以下方法將結果列印出來,源碼如下所示:
至此我們比較簡單的介紹了flowdroid的執行流程,對於控制流圖的演算法及heros的具體實現並沒有做更深入的介紹,後續可能作為獨立的文章進行分析。
4、優化
flowdroid無論從演算法、實現上,還是從效果上都堪稱是一款非常牛逼的產品,但是他也有個非常大的問題就是,太耗內存,分析時間太長,實際使用的價值很低,因此我常常稱它為一個實驗室的產品。那麼有什麼優化方法呢,首先需要明白造成flowdroid分析耗時的原因,其實無非就是apk較大時,代碼量太大,造成數據流圖(ICFG)呈現爆炸式增長,那麼很明顯的一條思路就是削減ICFG圖的體量,我曾經的一個方法是只對幾個相關聯的文件構造ICFG圖,這樣就使得ICFG圖體量呈現幾何式的下降,分析速度肯定明顯提升,但是這個方法比較適用於對風險進行驗證,並不適用於分析。
最近在研究其他源碼掃描工具(如我上篇文章的RIPS)的時候發現,這些工具在進行源碼掃描的時候並沒有進行所謂的數據流分析,更多的只是對調用關係進行分析。誠然數據流分析能夠減少很多誤報,但是這些誤報在我們進行漏洞驗證的時候可能很容易就排除掉,這樣只使用控制流圖進行風險分析看起來也是個不錯的想法。進行控制流分析首先要獲取Android應用的控制流圖,下面的代碼展示如何使用soot構造Android應用的控制流圖,相關說明上文均有提及,此處不再進行詳細說明。
獲取控制流圖後,下一步就是確定圖中的兩個點是否存在連線,假設這兩個點為我們定義的source點、sink點,如果存在連線,即source點與sink點之間存在調用關係,那麼就可以作為一個風險點拋出。那麼如何確定是否存在調用關係,查看CallGraph的源碼中是否定義了相關方法,發現存在findEdge這個方法,該方法用於尋找某個語句對於某個方法是否存在調用調用關係,如果把第一個參數u定義為一個sink點,即調用點,第二個參數callee定義為一個source點,那麼便定義了他們之間的一種調用關係。unit的獲取可以通過上面介紹的獲取應用內source、sink點的方法獲取。
註:使用控制流分析本人並沒有親自試驗,可能存在問題,望見諒。
*本文作者:nightmarelee,轉載請註明FreeBuf.COM


※Gartner:2017年11大頂尖信息安全技術
※Nexus 9漏洞允許黑客通過耳機介面攻擊設備
※Linux中的Stack Clash漏洞,可被黑客利用獲取本地root許可權
TAG:FreeBuf |
※ThreadLocal源碼分析
※閱讀Android源碼BitmapFactory
※網關 Spring-Cloud-Gateway 源碼解析——路由之RouteDefinitionLocator一覽
※PopupWindow源碼分析
※druid-spring-boot-starter源碼解析
※JsBridge 源碼分析
※kafka 源碼分析 3 : Producer
※disruptor 源碼解讀
※spring源碼分析——spring大綱
※AtomicInteger 源碼解析
※HikariCP源碼分析之leakDetectionThreshold及實戰解決Spark/Scala連接池泄漏
※Storm源碼分析之Trident源碼分析
※LinkedList源碼分析
※Flutter圖片緩存 Image.network源碼分析
※分散式共享 Session之SpringSession 源碼細節
※React Native BackHandler exitApp 源碼分析
※Istio技術與實踐02:源碼解析之Istio on Kubernetes 統一服務發現
※Prometheus原理和源碼分析
※windows離線狀態下源碼安裝Robotframework 環境及依賴包
※Kafka 源碼分析 5 :KafkaConsumer 消費處理