Android学习第十一篇:获取apk的函数调用图
1,为什么要获取apk的函数调用图?
在进行android应用分析时,我们对apk的整体运作流程是未知的,通过函数调用图能很好的反映应用的执行流程。
2,如何获取?
2,1,新建项目experiment
2,2,在该网址下载相应的jar包,并导入到项目当中
https://github.com/secure-software-engineering/soot-infoflow-android/wiki
2,3,在eclipse中导入
https://github.com/secure-software-engineering/soot-infoflow-android/wiki
中的几个项目导入后会出错,根据博客http://blog.****.net/liu1075538266/article/details/62361295的方法,既删除所有heros的Build Path的MavenDependence Path即可解决问题。
2,4,编写代码
package com.lengbo.experiment;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import soot.MethodOrMethodContext;
import soot.PackManager;
import soot.Scene;
import soot.SootMethod;
import soot.jimple.infoflow.android.SetupApplication;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.jimple.toolkits.callgraph.Targets;
import soot.options.Options;
import soot.util.dot.DotGraph;
import soot.util.dot.DotGraphEdge;
public class CGGenerator {
// 设置android的jar包目录
public final static String androidPath = "/Volumes/Lengbo/AndroidSDK/platforms";
// 设置要分析的APK文件
public final static String apkPath = "/Volumes/Lengbo/MalwareDetection/MonkeyRunner/app-release.apk";
// dot保存目录
public final static String dotpath = "/Volumes/Lengbo/MalwareDetection/MonkeyRunner/";
// 设置sourceAndSink目录
public final static String sourceAndSink = "/Volumes/Lengbo/MalwareDetection/ToolsAndFiles/apps/sourcesAndSinks.txt";
private static Map<String, Boolean> visited = new HashMap<String, Boolean>();
private static CGExporter cge = new CGExporter();
private static DotGraph dotGraph = new DotGraph("dot");
public static void main(String[] args) {
SetupApplication app = new SetupApplication(androidPath, apkPath);
try {
// 计算APK的入口点
app.calculateSourcesSinksEntrypoints(sourceAndSink);
} catch (Exception e) {
e.printStackTrace();
}
soot.G.reset();
Options.v().set_src_prec(Options.src_prec_apk);
Options.v().set_process_dir(Collections.singletonList(apkPath));
Options.v().set_force_android_jar(androidPath + "/android-21/android.jar");
Options.v().set_whole_program(true);
Options.v().set_allow_phantom_refs(true);
Options.v().set_output_format(Options.output_format_none);
Options.v().setPhaseOption("cg.spark verbose:true", "on");
Scene.v().loadNecessaryClasses();
SootMethod entryPoint = app.getEntryPointCreator().createDummyMain();
Options.v().set_main_class(entryPoint.getSignature());
Scene.v().setEntryPoints(Collections.singletonList(entryPoint));
PackManager.v().runPacks();
// 获取函数调用图
CallGraph cg = Scene.v().getCallGraph();
System.out.println("*********************************");
System.out.println(entryPoint);
System.out.println("*********************************");
// System.out.println(cg.toString());
System.out.println("*********************************");
toDot(cg, entryPoint);
File file = new File(dotpath, "test.dot");
OutputStream out;
try {
out = new FileOutputStream(file);
dotGraph.render(out, 0);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void toDot(CallGraph cg, SootMethod m) {
Iterator<MethodOrMethodContext> inTargets = new Targets(cg.edgesInto(m));
visited.put(m.getSignature(), true);
if (inTargets != null) {
while (inTargets.hasNext()) {
SootMethod in = (SootMethod) inTargets.next();
if (in == null) {
System.out.println("in is null");
}
System.out.println("----------------In Edge-----------");
System.out.println(in + " --> " + m);
DotGraphEdge dotGraphEdge = dotGraph.drawEdge(in + "", m + "");
dotGraphEdge.setLabel("inEdge");
dotGraph.drawNode(in + "");
dotGraph.drawNode(m + "");
System.out.println("************************************");
if (!visited.containsKey(in.getSignature())) {
toDot(cg, in);
}
}
}
Iterator<MethodOrMethodContext> outTargets = new Targets(cg.edgesOutOf(m));
if (outTargets != null) {
while (outTargets.hasNext()) {
SootMethod out = (SootMethod) outTargets.next();
if (out == null) {
System.out.println("out is null");
}
System.out.println("----------------Out Edge-----------");
System.out.println(m + " --> " + out);
DotGraphEdge dotGraphEdge = dotGraph.drawEdge(m + "", out + "");
dotGraphEdge.setLabel("outEdge");
dotGraph.drawNode(m + "");
dotGraph.drawNode(out + "");
System.out.println("************************************");
if (!visited.containsKey(out.getSignature())) {
toDot(cg, out);
}
}
}
}
}
2.5,运行即可得到函数调用图
3,问题与解答
3,1,注意(2,3)下载依赖工程时,需用删除heros的依赖关系,则所报的错误都会解决
3,2,每个CallGraph对象其实是一个边的容器,代表了所有相关的边的集合。通过.edgesInto()可以获得到某条边的所有边,.edgesOutOf()可以获得某条边能到的所有边。
3.3,当你运行该程序到较低版本的android apk时,遇到的sourcetype错误,是你没有加入相应的sdk版本,目前我遇到的有android platforms最低是android-3。