基于最新版soot-infoflow-android绘制android应用函数调用图

来源:互联网 发布:高性能网络编程 编辑:程序博客网 时间:2024/06/14 21:03

【问题】

最近在研究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
2、AndroidCallbacks.txt可从soot-infoflow-android根目录拿到,依赖及工程结构如下:
 
 
CGGenerator.java
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);                  }              }          }      }  }  
CGExporter
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);      }  }  
【结果】
代码分析的apk是/soot-infoflow-android/testAPKs/enriched1.apk,结果图如下: