提到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 {//设置android的jar包目录public final static String jarPath = "/home/liu/Android/Sdk/platforms";//设置要分析的APK文件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{//计算APK的入口点,这一步导入的文件是Flowdroid进行污点分析的时候需要的,这里直接新建一个空文件即可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){//在soot中,函数的signature就是由该函数的类名,函数名,参数类型,以及返回值类型组成的字符串String identifier = m.getSignature();//记录是否已经处理过该点visited.put(m.getSignature(), true);//以函数的signature为label在图中添加该节点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
  • 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);}
}
  • 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应用程序的函数调用图相关推荐

  1. 使用xdebug分析thinkphp框架函数调用图

    开发中需要性能调优,使用xdebug分析thinkphp框架函数调用图. 关于xdebug的安装参考这2篇 NetBeans配置Xdebug 远程调试PHP php扩展xdebug安装以及用kcach ...

  2. 为了偷懒,我开始用Python编写Android应用程序

    为了偷懒,我开始用Python编写Android应用程序 说明 环境准备 开发环境准备(windows) 编译环境准备 自行配置制作编译环境 偷懒:直接下载配置好的虚拟机 编译 验证APK 总结 说明 ...

  3. scala android 开发环境搭建,使用Scala + sbt + sbt-android-plugin开发Android应用程序

    自动生成Android应用程序框架 设置好上面的环境后,可以使用sbt-android-plugin来自动生成一个简单的Android应用程序框架.插件的作者推荐使用一个名为giter8的模板工具来生 ...

  4. 【Android 逆向】Android 逆向通用工具开发 ( adb forward 网络端口重定向命令 | PC 端逆向程序主函数分析 )

    文章目录 前言 一.adb forward 网络端口重定向命令 二.PC 端逆向程序主函数分析 前言 本篇博客重点分析 PC 端 hacktool 模块 ; 一.adb forward 网络端口重定向 ...

  5. android敏感api函数,基于敏感API调用的Android应用程序动态监控

    摘要: 近年来,Android操作系统凭借其开源的优势,占领了移动操作系统市场.Android通过对外提供API接口重组资源,吸引了大量第三方开发人员创造出功能丰富的Android应用.但是,由于An ...

  6. 出现身份验证错误 要求的函数不受支持_学习使用Kotlin创建Android应用程序第3部分:身份验证登录...

    在上一篇文章中,我们讨论了学习Kotlin制作Android应用程序的初学者第2部分:创建登录表单.这次我们来学习创建登录表单后,我们将尝试对上一篇创建的登录表单使用Firebase身份验证.因此,我 ...

  7. android 动态生成tablelayout,动态tablelayout创建的android应用程序崩溃

    似乎我不是第一个动态创建TableLayOut的人.问题是,当我将TableRow对象添加到TableLayout时,我的android应用程序崩溃并完成了意外错误. 我想做的事? 我想用下一个项目布 ...

  8. 利用FRIDA攻击Android应用程序(三)

    利用FRIDA攻击Android应用程序(三) 前言 在我的有关frida的第二篇博客发布不久之后,@muellerberndt决定发布另一个OWASP Android crackme,我很想知道是否 ...

  9. 利用FRIDA攻击Android应用程序(一)

    前言 直到去年参加RadareCon大会时,我才开始接触动态代码插桩框架Frida.最初,我感觉这玩意还有点意思,后来发现这种感觉是不对的:应该是非常有意思.您还记得游戏中的上帝模式吗?面对本地应用程 ...

最新文章

  1. Windows Live Messenger  正式版已经发布
  2. Lotus Notes常见问题答疑
  3. Java zip 压缩 文件夹删除,移动,重命名,复制
  4. 公众号服务器配置url证书问题,公众号服务器配置url,验证token一直出错,为什么?...
  5. 读入自然数m、n,判断m/n是有限小数还是循环小数
  6. 12 | 排序(下):如何用快排思想在O(n)内查找第K大元素?
  7. Vijos1775 CodeVS1174 NOIP2009 靶形数独
  8. eclipse新建maven报错
  9. 2015.10.13课堂
  10. Premiere Elements使用教程:将音乐添加到视频片段
  11. java实现avg函数_PostgreSQL avg()函数
  12. android intent传文件夹,Android之intent传值篇
  13. ubuntu18.04桌面美化
  14. 网络音视频下载小套路-dy、xmly
  15. php file get contents 总是超时,file_get_contents超时问题及解决方案
  16. 利用百度地图Android定位SDK获取经纬度
  17. Request和Response的概述及其方法
  18. stm32f4 RTC实时时钟解析
  19. 小米note2不上Android9吗,我的第二部小米手机,小米9简单到不能再简单的简单体会...
  20. C语言编写可以实现malloc() free()功能的函数(空间/时间复杂度低)

热门文章

  1. 中科燕园GIS外包-----基于ArcGIS的应急平台
  2. 回溯算法超详细讲解(附代码)
  3. 编程方法学8:信息隐藏
  4. 【比赛】论如何七天内在研究生电子设计竞赛中拿国奖
  5. 云炬创业政策学习笔记20210116
  6. 我用 PyTorch 复现了 LeNet-5 神经网络(自定义数据集篇)!
  7. 《C++ Primer中文版(第5版)》学习笔记与习题完整发布!
  8. python第一个发行版本由c语言实现_【Python】讲真,你知道Python咋来的吗?
  9. pls-00302: 必须声明 组件_手把手教你开发vue组件库
  10. GRE难句典型结构2