【问题】
最近在研究android应用函数调用图分析,准备基于soot做代码静态分析。
调用图过程中主要参考了Blog:http://blog.csdn.net/liu3237/article/details/48827523《使用FlowDroid生成Android应用程序的函数调用图》
新的soot-infoflow-android函数调用图获取使用constructCallgraph(),不再需要提供source和sink,老的函数不再适用,猜测老的版本是基于release1.5。详细参见:https://github.com/secure-software-engineering/soot-infoflow/issues/38
【解决方案】
1、根据steven的提示和阅读最新源码,了解只需要设置AndroidCallbacks,再调用constructCallgraph()即可;关键代码如下:
SetupApplication app = new SetupApplication(androidPlatformPath, appPath);
//传入AndroidCallbacks文件
app.setCallbackFile(CGGenerator.class.getResource("AndroidCallbacks.txt").getFile());
app.constructCallgraph();
2、绘制调用图,主要参考《使用FlowDroid生成Android应用程序的函数调用图》,改动点在于获取调用函数的方式跟原来的不再一致;关键代码如下:
//SootMethod 获取函数调用图
SootMethod entryPoint = app.getDummyMainMethod();
CallGraph cg = Scene.v().getCallGraph();
//可视化函数调用图
visit(cg,entryPoint);
//导出函数调用图
cge.exportMIG("flowdroidCFG", "E:/");
【代码工程】
工程基于当前最新的代码去构建
1、flowdroidcg依赖于gexf4j-1.0.0.jar、stax2-api-4.0.0.jar、woodstox-core-asl-4.4.0.jar、soot(依赖heros、jasmin)、soot-infoflow、soot-infoflow-android
soot等获取gihub最新develop分支:
- Jasmin: https://github.com/Sable/jasmin
- Heros: https://github.com/Sable/heros
- Soot: https://github.com/Sable/soot
- soot-infoflow: https://github.com/secure-software-engineering/soot-infoflow
- soot-infoflow-android: https://github.com/secure-software-engineering/soot-infoflow-android
package flowdroidcg;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import soot.MethodOrMethodContext;
import soot.Scene;
import soot.SootMethod;
import soot.jimple.infoflow.android.SetupApplication;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.jimple.toolkits.callgraph.Targets;
public class CGGenerator {
//设置android的jar包目录
public final static String androidPlatformPath = "D:/Android/sdk/platforms";
//设置要分析的APK文件
public final static String appPath = "E:/enriched1.apk";
static Object ob = new Object();
private static Map<String,Boolean> visited = new HashMap<String,Boolean>();
private static CGExporter cge = new CGExporter();
public static void main(String[] args){
SetupApplication app = new SetupApplication(androidPlatformPath, appPath);
soot.G.reset();
//传入AndroidCallbacks文件
app.setCallbackFile(CGGenerator.class.getResource("AndroidCallbacks.txt").getFile());
app.constructCallgraph();
//SootMethod 获取函数调用图
SootMethod entryPoint = app.getDummyMainMethod();
CallGraph cg = Scene.v().getCallGraph();
//可视化函数调用图
visit(cg,entryPoint);
//导出函数调用图
cge.exportMIG("flowdroidCFG", "E:/");
}
//可视化函数调用图的函数
private static void visit(CallGraph cg,SootMethod m){
//在soot中,函数的signature就是由该函数的类名,函数名,参数类型,以及返回值类型组成的字符串
String identifier = m.getSignature();
//记录是否已经处理过该点
visited.put(identifier, true);
//以函数的signature为label在图中添加该节点
cge.createNode(identifier);
//获取调用该函数的函数
Iterator<MethodOrMethodContext> ptargets = new Targets(cg.edgesInto(m));
if(ptargets != null){
while(ptargets.hasNext())
{
SootMethod p = (SootMethod) ptargets.next();
if(p == null){
System.out.println("p is null");
}
if(!visited.containsKey(p.getSignature())){
visit(cg,p);
}
}
}
//获取该函数调用的函数
Iterator<MethodOrMethodContext> ctargets = new Targets(cg.edgesOutOf(m));
if(ctargets != null){
while(ctargets.hasNext())
{
SootMethod c = (SootMethod) ctargets.next();
if(c == null){
System.out.println("c is null");
}
//将被调用的函数加入图中
cge.createNode(c.getSignature());
//添加一条指向该被调函数的边
cge.linkNodeByID(identifier, c.getSignature());
if(!visited.containsKey(c.getSignature())){
//递归
visit(cg,c);
}
}
}
}
}
package flowdroidcg;
import it.uniroma1.dis.wsngroup.gexf4j.core.EdgeType;
import it.uniroma1.dis.wsngroup.gexf4j.core.Gexf;
import it.uniroma1.dis.wsngroup.gexf4j.core.Graph;
import it.uniroma1.dis.wsngroup.gexf4j.core.Mode;
import it.uniroma1.dis.wsngroup.gexf4j.core.Node;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.Attribute;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.AttributeClass;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.AttributeList;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.AttributeType;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.GexfImpl;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.StaxGraphWriter;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.data.AttributeListImpl;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
public class CGExporter {
private Gexf gexf;
private Graph graph;
private Attribute codeArray;
private AttributeList attrList;
public CGExporter() {
this.gexf = new GexfImpl();
this.graph = this.gexf.getGraph();
this.gexf.getMetadata().setCreator("liu3237").setDescription("App method invoke graph");
this.gexf.setVisualization(true);
this.graph.setDefaultEdgeType(EdgeType.DIRECTED).setMode(Mode.STATIC);
this.attrList = new AttributeListImpl(AttributeClass.NODE);
this.graph.getAttributeLists().add(attrList);
//可以给每个节点设置一些属性,这里设置的属性名是 codeArray,实际上后面没用到
this.codeArray = this.attrList.createAttribute("0", AttributeType.STRING,"codeArray");
}
public void exportMIG(String graphName, String storeDir) {
String outPath = storeDir + "/" + graphName + ".gexf";
StaxGraphWriter graphWriter = new StaxGraphWriter();
File f = new File(outPath);
Writer out;
try {
out = new FileWriter(f, false);
graphWriter.writeToStream(this.gexf, out, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
}
public Node getNodeByID(String Id) {
List<Node> nodes = this.graph.getNodes();
Node nodeFinded = null;
for (Node node : nodes) {
String nodeID = node.getId();
if (nodeID.equals(Id)) {
nodeFinded = node;
break;
}
}
return nodeFinded;
}
public void linkNodeByID(String sourceID, String targetID) {
Node sourceNode = this.getNodeByID(sourceID);
Node targetNode = this.getNodeByID(targetID);
if (sourceNode.equals(targetNode)) {
return;
}
if (!sourceNode.hasEdgeTo(targetID)) {
String edgeID = sourceID + "-->" + targetID;
sourceNode.connectTo(edgeID, "", EdgeType.DIRECTED, targetNode);
}
}
public void createNode(String m) {
String id = m;
String codes = "";
if (getNodeByID(id) != null) {
return;
}
Node node = this.graph.createNode(id);
node.setLabel(id).getAttributeValues().addValue(this.codeArray, codes);
node.setSize(20);
}
}
【结果】