提到Android應用程序靜態分析,就不能不提Flowdroid。該工具是目前使用很廣泛的Android應用程序數據流分析工具。它基于強大的Java分析工具Soot開發,提供了許多有用的功能。具體的介紹和使用幫助可以訪問開發團隊的網站?點這里。
然而有時候我們并不需要使用Flowdroid的全部功能,例如有時候我們只想要一個APK里面的函數調用圖。當然我們可以使用androguard(最近好像又更新了)來生成函數調用圖,或者用APKTool來反匯編APK,然后再自己編程搜索反匯編后的smali文件來生成函數調用圖。但是我認為使用Flowdroid生成的調用圖是比較有說服力的。下面直接放代碼。
-
整個工程的目錄結構,其中CGGenerator.java用來生成調用圖,CGExporter.java里面封裝了一些把調用圖可視化的函數。除此之外還需要導入4個jar包:gexf4j.jar,?AXMLPrinter2.jar,?stax2-api-3.1.1.jar,?woodstox-core-asl-4.0.6.jar。?
當然本工程還要導入對Flowdroid原本工程(可以從他們的官方網站上找到下載)的依賴。?
-
CGGenerator.java
package flowdroidcg;
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;
public class CGGenerator {public final static String jarPath =
"/home/liu/Android/Sdk/platforms";
public final static String apk =
"/home/liu/app-release.apk";
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(jarPath, apk);
try{app.calculateSourcesSinksEntrypoints(
"/home/liu/temp/sourcesAndSinks.txt");}
catch(Exception e){e.printStackTrace();}soot.G.reset();Options.v().set_src_prec(Options.src_prec_apk);Options.v().set_process_dir(Collections.singletonList(apk));Options.v().set_force_android_jar(jarPath +
"/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();visit(cg,entryPoint);cge.exportMIG(
"flowdroidCFG.gexf",
"/home/liu/temp");}
private static void visit(CallGraph cg,SootMethod m){String identifier = m.getSignature();visited.put(m.getSignature(),
true);cge.createNode(m.getSignature());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);}}}}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
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);
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);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 生成的可視化的函數調用圖?
最后生成的調用圖被保存成了gexf格式,這個格式在網絡分析里面用的比較多。可以用軟件Gephi打開生成的調用圖。
生成的函數調用圖,密密麻麻的全是節點。?
還可以縮放,查看節點label,修改節點顏色等。?
- 后記?
FlowDroid比較強大,我自己也沒搞太明白…………Soot里面有可以把調用圖導出成dot格式的函數,但是節點比較多的時候我的電腦打不開那個dot文件了。可能是本人電腦比較渣吧。其實大部分時候獲得了函數調用圖就可以在上面進行分析了,沒必要把它導出來。導出來只是為了人看著方便。
原文地址:?http://blog.csdn.net/liu3237/article/details/48827523
總結
以上是生活随笔為你收集整理的使用FlowDroid生成Android应用程序的函数调用图的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。