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

【问题】

最近在研究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分支:

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,结果图如下:


  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值