距离这个系列第一篇隔得时间有点久,主要仍是比较懒以及不懒的时候又比较忙(被迫不懒),争取后面尽量完善这个系列,由于不少FlowDroid的细节已经忘差很少了。同时随着接触更多的论文,仍是建议你们若是对污点分析的准确性要求较高甚至说以此为创新/贡献的话不要考虑直接使用FlowDroid来做为程序的一部分(估摸着工业界作Android静态分析的也看不上flowdroid,写起来不懂给谁看233)。java
FlowDroid针对Android的各种处理仍是很值得刚接触静态分析时进行借鉴参考,但终归都是些基于函数签名的非启发式规则,灵活性不好,而且做为一款2014年诞生的工具即使在不断维护终究仍是缺少一些对新特性的支持。android
后续的内容(若是有的话)大概就是对FlowDroid整个流程的解析、对IFDS算法的介绍与代码解析、对Callback等特殊机制处理的解析以及一些简单的soot用法,至于完成时间就不大清楚了算法
本篇将介绍TaintHandler、TaintWrapper的使用以及Abstraction的构成。这三部分没有什么直接地联系,由于它们既与其余部分没什么关系又不会展开太多的篇幅,索性就丢到了一块儿。数组
若是不是单纯地将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集合,不然这一条“路径”的污点分析就中止了。
这个类主要用于设计一些数据流分析的“捷径”,可让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的变量。
这个类用于表示数据流传播图中的节点,类文件在soot.jimple.infoflow.data包中。去查看类文件的话会发现其中有不少看着颇有用的成员变量,但在我实际使用中发现不少变量其实一直是null,FlowDroid其实没有在构建数据流传播图的同时更新里面的一些值,固然也多是我我的的缘由,或者新版本的FlowDoird如今已经更新了。总之这里主要就提一下accessPath和currentStmt两个类成员,类方法基本都是一些get/set或者用于根据参数生成新的Abstraction,这里再也不叙述。
这个成员存储的是被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)。
这个成员存储了被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"即是原来的代码无需修改,其余代码也不要删除。