淺入淺出FlowDroid(二):handler、wrapper與abstraction

前言

距離這個系列第一篇隔得時間有點久,主要仍是比較懶以及不懶的時候又比較忙(被迫不懶),爭取後面儘量完善這個系列,由於不少FlowDroid的細節已經忘差很少了。同時隨着接觸更多的論文,仍是建議你們若是對污點分析的準確性要求較高甚至說以此爲創新/貢獻的話不要考慮直接使用FlowDroid來做爲程序的一部分(估摸着工業界作Android靜態分析的也看不上flowdroid,寫起來不懂給誰看233)。java

FlowDroid針對Android的各種處理仍是很值得剛接觸靜態分析時進行借鑑參考,但終歸都是些基於函數簽名的非啓發式規則,靈活性不好,而且做爲一款2014年誕生的工具即使在不斷維護終究仍是缺少一些對新特性的支持。android

後續的內容(若是有的話)大概就是對FlowDroid整個流程的解析、對IFDS算法的介紹與代碼解析、對Callback等特殊機制處理的解析以及一些簡單的soot用法,至於完成時間就不大清楚了算法

簡介

本篇將介紹TaintHandler、TaintWrapper的使用以及Abstraction的構成。這三部分沒有什麼直接地聯繫,由於它們既與其餘部分沒什麼關係又不會展開太多的篇幅,索性就丟到了一塊兒。數組

TaintPropagationHandler

若是不是單純地將FlowDroid用於污點分析,就須要使用這個interface來實現一些本身的邏輯。app

說明

當初查看的一些博客、文檔時都沒有提到這個接口,仍是扒拉源碼的時候發如今各類heros.FlowFunction的實現裏,總會在computeTargets方法裏調用一個notifyOutFlowHandler方法,並且總會實現一個computeTargetsInternal方法並在其中調用taintPropagationHandler.notifyFlowIn方法。函數

上面提到一些的類與方法,這裏僅作一個簡單說明——各類FlowFunction的實例會在soot.jimple.infoflow.solver.fastSolver.IFDSSolver.propagate方法里根據當前所分析的語句類型被獲取並調用computeTargets方法,更簡單地來講每一次被taint的數據的「傳播」都會根據傳播方式調用對應的computeTargets方法的實現,而不管何種類型的該方法都有在處理單次「傳播」的開頭與結束調用taintPropagationHandler的notifyFlowIn和notifyFlowOut兩種方法。很顯然,若是咱們想獲取數據流的傳播,就須要根據需求自行實現這一接口的邏輯並進行設置。工具

細心的話會發現我一開始提的computeTargets末尾調用的是notifyOutFlowHandler而不是taintPropagationHandler.notifyFlowOut,FlowDroid在這裏作了一個簡單的封裝,僅當傳播分析結果的outgoing符合必定的條件纔會調用taintPropagationHandler的方法,這裏須要注意outgoing爲null或者isEmpty返回true時taintPropagationHandler都不會被調用,若是須要設計一些針對傳播終點的規則要注意這一點。設計

使用

首先固然要import接口soot.jimple.infoflow.handlers.TaintPropagationHandler並聲明一個implements它的類,類中須要實現下面這兩個抽象方法code

public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, FlowFunctionType type)
public Set<Abstraction> notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set<Abstraction> outgoing, InfoflowManager manager, FlowFunctionType type)

第一個參數stmt具體是對應哪一個語句我忘了,但必定要注意這個變量並不對應當前分析的語句,想要獲取此次「傳播」中當前分析的語句須要在notifyFlowIn中調用taint.getCurrentStmt,而獲取「傳播」的下一句須要遍歷notifyFlowOut的outgoing集合中每個元素並調用getCurrentStmt。對象

manager能夠用於獲取一些全局的變量,好比我想看一看如今分析的語句是在哪一個類哪一個方法裏,就能夠經過manager.getICFG方法獲取CallFunctionGraph並調用getMethodOf獲取語句所屬的SootMethod。須要注意Abstraction.getCurrentStmt與IInfoflowCFG.getMethodOf兩個方法都有可能返回null,能力有限尚不清楚緣由。

最後要注意,nofityFlowOut的實現裏要返回outgoing或者根據你的需求過濾後的Abstraction集合,不然這一條「路徑」的污點分析就中止了。

TaintWrapper

這個類主要用於設計一些數據流分析的「捷徑」,可讓FlowDroid在分析到特定語句和方法時,根據wrapper中的規則直接taint語句中某一變量或是kill這一分析,而不是基於最基礎的語法的rule拆解語句並進入方法內,目的固然是提升分析效率並處理一些特殊狀況。FlowDroid本身實現了一個EasyTaintWrapper在soot.jimple.infoflow.taintWrappers包中,實現本身的wrapper主要就是要參考這個類是如何進行規則的設置。說實話當時我都沒有研究透這個東西,況且如今忘得差很少了,因此這裏只作一些簡單的介紹。

能夠選擇實現ITaintPropagationWrapper或者繼承AbstractionTaintWrapper中的一種方式來實現本身的wrapper,相比較而言繼承AbstractionTaintWrapper的方式固然須要的工做更少,主要須要實現如下幾個方法。

public boolean isExclusiveInternal(Stmt stmt, AccessPath taintedPath)
public Set<AccessPath> getTaintsForMethodInternal(Stmt stmt, AccessPath taintedPath)
public Set<Abstraction> getAliasesForMethod(Stmt stmt, Abstraction d1, Abstraction taintedPath)
public boolean supportsCallee(SootMethod method)
public boolean supportsCallee(Stmt callSite)

isExclusiveInternal方法返回true時表示傳入的語句只產生由該wrapper生成的傳播結果,反之則既會根據wrapper的規則產生傳播也會根據FlowDroid原有的規則產生傳播;getAliasesForMethod這個方法我也不大懂,註釋中大意是返回語句中的方法的別名,這個別名應該也是數據流分析專有的名詞,不瞭解的話能夠跟EasyTaintWrapper同樣直接返回null;兩個supportCallee方法做用是同樣的,若是返回true則表示這一wrapper能夠從傳入的method或callsite中生成新的傳播,註釋中還說到「might be removed if not needed elsewhere",暫不清楚只是不在被wrapper分析仍是會對整個數據流分析產生影響,不瞭解的話能夠去照抄EasyTaintWrapper的實現或者更求穩地老是返回true。

最核心的就是getTaintsForMethodInternal方法,它會根據其中實現的規則返回產生的新的taint。根據所設計的規則可能調用的方法千差萬別,故這裏不作一些展開說明,最後生成的AccessPath須要調用manager.getAccessPathFactory().createAccessPath(val, taintedPath.getTaintSubFields())獲取,val表示分析這一語句後須要taint的變量。

Abstraction

這個類用於表示數據流傳播圖中的節點,類文件在soot.jimple.infoflow.data包中。去查看類文件的話會發現其中有不少看着頗有用的成員變量,但在我實際使用中發現不少變量其實一直是null,FlowDroid其實沒有在構建數據流傳播圖的同時更新裏面的一些值,固然也多是我我的的緣由,或者新版本的FlowDoird如今已經更新了。總之這裏主要就提一下accessPath和currentStmt兩個類成員,類方法基本都是一些get/set或者用於根據參數生成新的Abstraction,這裏再也不敘述。

accessPath

這個成員存儲的是被taint的變量自己的信息,其中AccessPath.value存儲taint的變量的base,AccessPath.fields則是存儲可能有的域成員。若是value存儲了變量a,fields數組發現值爲{b, c},則實際上被taint的變量是a.b.c。另外若是被taint的對象類型爲數組,則AccessPath.arrayTaintType會存儲一個枚舉值來表示究竟是數組成員被taint了仍是數組長度來自被taint的變量(不要期望能動態追蹤taint的數組哪一個index或者集合中究竟哪一個元素被taint了,FlowDroid只會簡單的把數組與集合對象一塊兒taint)。

currentStmt

這個成員存儲了被taint的變量所在語句的信息,自己是soot中的stmt類型。soot裏提供了很是多的stmt的子類,好比跳轉語句、方法調用語句、條件判斷語句等等,能夠經過instanceof進行判斷後,裝換成特定子類獲取一些進一步的信息。

其餘

由於可能莫得後文了,先提一下若是一些特殊情形中須要作數據流的分析卻又難以圈定可能的sink方法,能夠在soot.jimple.infoflow.infoflow.runAnalysis方法中修改sinkCount的值或者直接修改相關if block中的邏輯,使得FlowDroid能夠繼續運行。不然,若是sink方法未被發現,FlowDroid不會繼續運行。

另外在FlowDroid 2.6.1裏,若是設置callback類型方法做爲source點會觸發bug,緣由是FlowDroid在初始化中針對source生成MethodSourceSinkDefinition時未聲明parameters參數使得被設置了默認值null,但在後續將callback方法做爲source時須要taint方法的參數因此會報錯。須要修改soot.jimple.infoflow.android.data.parsers.PermissionMethodParser.parse()方法以下:

private void parse() {
    // ...
    
    for (AndroidMethod am : methods.values()) {
        SourceSinkDefinition singleMethod = null;

        // new added
        if (am.getReturnType().contains("void") && am.getMethodName().startsWith("on")) {
            List<String> types = am.getParameters();
            @SuppressWarnings("unchecked")
            Set<AccessPathTuple>[] params = (Set<AccessPathTuple>[] )new Set[am.getParameters().size()];
            for (int i = 0; i < types.size(); i++) {
                List<String> param = new ArrayList<String>();
                param.add(types.get(i));
                AccessPathTuple apt = AccessPathTuple.fromPathElements(null, param, SourceSinkType.Source);
                params[i] = Collections.singleton(apt);
            }
                
            singleMethod = new MethodSourceSinkDefinition(am, null, params, null, CallType.Callback);
        }
            
        // origin part
        if (singleMethod == null) singleMethod = new MethodSourceSinkDefinition(am);
        if (am.getSourceSinkType().isSource())
            sourceList.add(singleMethod);
        if (am.getSourceSinkType().isSink())
            sinkList.add(singleMethod);
        if (am.getSourceSinkType() == SourceSinkType.Neither)
            neitherList.add(singleMethod);
    }
}

我這裏只是省略了for循環前面的代碼,須要作的只是在對應位置添加「// new added"註釋下面一部分的代碼,"// origin part"即是原來的代碼無需修改,其餘代碼也不要刪除。

相關文章
相關標籤/搜索