-
Notifications
You must be signed in to change notification settings - Fork 107
JAADAS reachability analysis
JAADAS中的reachability analysis基于全局的Call graph,主要代码逻辑如下:
分析的总入口点依然在entry函数中:
def entry(enableDataFlowOut: Boolean): Iterable[VulnResult]={
G.reset
prepareMethodTraversal
var mid = doNaiveAPIScan ++ doAPIMethodScan ++ doCrashAnalysis ++ doManifestAnalysis ++ doImplicitScan
implicit val icfg = prepareFatCfg
mid = mid ++ doConstantAPIScan ++ doReachabilityAnalysis ++ doSensitiveInfoFlow
if(enableDataFlowOut){
System.gc
System.gc
mid = mid ++ doSensitiveInfoFlowOut //very slow!
}
mid
}doReachabilityAnalysis函数即为可到达性分析的入口点。
##两类不同的分析
在entry函数中值得注意的一点是, 事实上entry包含了过程内和过程间两类分析, 以prepareFatCfg为分割点。
prepareMethodTraversal函数只为过程内分析做准备,其的主要工作就是将APK加载,对每个加载进来的函数进行字节码分析并转换为中间语言。在这个函数完成后,我们可以实施过程内的分析,例如我们可以调用Soot的API去获取具体的语句是invoke还是赋值,可以生成函数内控制流图(Control Flow Graph),但是我们在这个时候并没有整体的Call Graph可以使用。
而prepareFatCfg则是对过程间分析准备的一个包装函数, 其中有很多可以配置的参数
def prepareFatCfg: IInfoflowCFG={
val app: SetupApplication = new SetupApplication(platformPath, apkPath)
app.setStopAfterFirstFlow(false)
app.setEnableImplicitFlows(true)
app.setEnableStaticFieldTracking(true)
app.setEnableCallbacks(false)
app.setEnableExceptionTracking(false)
app.setCodeEliminationMode(CodeEliminationMode.PropagateConstants)
JadeCfg.setJcustom_clinit_enabled(true)
JadeCfg.setcallbacks_enabled(false)
JadeCfg.setfield_taint_propagate_enabled(false)//I don't care for constant api scan
JadeCfg.setintent_special_wrapper_tacking_enabled(false) //don't care neither
JadeCfg.setpublic_component_only_enabled(false)
val easyTaintWrapper: EasyTaintWrapper = new EasyTaintWrapper(taintwrapperFile)
app.setTaintWrapper(easyTaintWrapper)
app.calculateSourcesSinksEntrypoints(sourceSinkDataFlowOut)
System.out.println("Running data flow analysis...")
app.generateInfoflowCFG
}这里面有两个地方是可以配置的
- SetupApplication: 该选项来源于FlowDroid,确定了一些TaintAnalysis和调用图生成的参数。因为我们现在prepareFatCfg并不是为了在这里实现taintanalysis,而只是需要一个minimum的,在无UI callback交互的情况下的callgraph,我们选择cost最低的参数
- JadeCfg: 这是JAADAS添加的参数,如注释所示,在跨过程constant api scan和reachability analysis中,我们选择关闭
setfield_taint_propagate_enabled和setintent_special_wrapper_tacking_enabled这两个参数。(可以注意到这两个参数在后续infoflow analysis中是打开的)-
setintent_special_wrapper_tacking_enabled这个参见soot.jimple.infoflow.taintWrappers/EasyTaintWrapper.java, 是对FlowDroid的一个修改。
-
if (stmt.getInvokeExpr() instanceof InstanceInvokeExpr) {
InstanceInvokeExpr iiExpr = (InstanceInvokeExpr) stmt.getInvokeExpr();
if (taintedPath.isEmpty()
|| iiExpr.getBase().equals(taintedPath.getPlainValue())) {
// If the base object is tainted, we have to check whether we must kill the taint
if (wrapType == MethodWrapType.KillTaint)
return Collections.emptySet();
// If the base object is tainted, all calls to its methods always return
// tainted values (mark: original flowdroid code/comment but not acceptable)
//FLANKER FIX: however this will generate huge false positives
if (stmt instanceof DefinitionStmt) {
DefinitionStmt def = (DefinitionStmt) stmt;
// Check for exclusions
//if (wrapType != MethodWrapType.Exclude)
//FLANKER FIX: only wrapType=>CreateTaint should be tainted here
//FLANKER FIX: special process intent (and bundle?), create taint if call is get***
if (JadeCfg.intent_special_wrapper_tacking_enabled() &&
( (iiExpr.getBase().getType().toString().equals("android.content.Intent") || iiExpr.getBase().getType().toString().equals("android.os.Bundle"))
&& method.getName().startsWith("get"))) {
taints.add(new AccessPath(def.getLeftOp(), true));
}
else {
if (wrapType == MethodWrapType.CreateTaint) //flanker fix: only accept CreateTaint
taints.add(new AccessPath(def.getLeftOp(), true));
}
}
// If the base object is tainted, we pass this taint on
taints.add(taintedPath);
}
}
这里主要有两个问题:FlowDroid的做法是如果一个base object被taint了,那么所有针对这个object的函数调用均返回taint。这会导致大量false postive. 这里主要有两点修改的地方
-
如果FlowDroid的做法是如果一个base object被taint了,那么所有针对这个object的函数调用均返回taint,这种模拟相对来说有点偷懒。JAADAS在此修改为只继续传播Taintwrapper中已声明的函数,同时继续传播Intent和Bundle中的get系函数
-
在Reachability Analysis中,我们需要将所有的component都包括进来, 所以用
JadeCfg.setpublic_component_only_enabled(false),而在intent dataflow analysis中,由于漏洞利用中一般不考虑不导出的组件,这个参数可以设为true.
在这些参数给定之后,我们调用generateInfoflowCFG函数生成全局ICFG和callgraph.
在Callgraph生成之后,我们就可以在上面进行查询.
val methods: ReachableMethods = Scene.v.getReachableMethods
val reader: QueueReader[MethodOrMethodContext] = methods.listener
获取了目前从callgraph入口可到达的函数,以iterator形式返回
val directReader: BufferedReader = new BufferedReader(new FileReader(new File(reachabilityFile)))
val lists: Set[String] = new HashSet[String]
val ret = new ListBuffer[VulnResult]()
var string: String = directReader.readLine
读取预先配置的文件,文件中包含需要权限的系统API, 例如phone call , power相关.
while (reader.hasNext) {
val method: SootMethod = reader.next.method
if (method.hasActiveBody) {
val body: Body = method.getActiveBody
for (unit <- body.getUnits) {
val stmt: Stmt = unit.asInstanceOf[Stmt]
if (stmt.containsInvokeExpr) {
val expr: InvokeExpr = stmt.getInvokeExpr
val relateMethod: SootMethod = expr.getMethod
if (lists.contains(relateMethod.getSignature)) {
System.out.println("find capability leak!")
System.out.println(relateMethod.getSignature + " @ " + method)
ret.append(VulnResult.toReachabilityVulnResult(stmt, method))
}
}
}
}
}
扫描可到达的函数体,检查其中的调用语句是否指向需要权限的系统API. 如果是,那么证明该函数存在权限泄漏.
JAADAS中数据流分析支持路径回溯, 但权限泄漏的路径回溯还没有实现, 建议用BFS实现下.