Table of Contents
- [what is FlowDroid?]
- [what is soot?]
- [soot 使用]
- [编译]
- [调试]
- [源码初步阅读]
- [level-1 环境初始化]
- [level-2 污点规则解析]
- [level-3 Soot配置等]
- [level-4 分析主流程]
- [what is CG, CFG, ICFG]
- [源码调试和流程梳理]
- [问题总结]
what is FlowDroid?
针对APK实现的污点分析框架,其中包括的几个概念:
source 表示污点产生的位置
sink 表示目标触发的位置
taintWrapper 表示污点的传递规则
entry 表示程序的入口
void onCreate(Bundle b){
TelephonyManager mgr = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
String deviceId = mgr.getDeviceId(); // source
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage("+49 1234", null, deviceId, null, null); // sink, leak
}
污点从 deviceId 到发送,最后触发到信息泄漏的位置,一个分析就完成了
该框架的四个部分,看了下代码结构和命名,大概功能为:
soot-infoflow: 数据流分析核心包(基于Soot)
soot-infoflow-android: 与安卓特性相关的数据流分析
soot-infoflow-summaries:大部分自定义API的类封装
soot-infoflow-cmd:命令行入口
分析基础是soot框架程
what is soot?
该框架提供了apk静态分析的大部分功能,如apk文件反汇编,将java字节码和dex字节码翻译成中间语言
可以有四种输出结果的表示方式,这里主要使用Jimple 这种表示方式,
其实跟java非常像,用较少的信息来表示类、变量之间的关系,它只要15种opcode就可以描述
Soot 提供所有类之间相互关联,可生成CFG(control flow graph)
里边还整合Heros,做到了从指定的变量到另一个指定的变量的寻路过程
关于soot介绍文章暂时不耗费精力去爬,先关注主线
后续有时间再详细爬一爬的文章:
[soot静态分析框架]
[soot学习笔记]
soot 使用
soot 不支持java 16
使用soot 命令行跑目标java文件,坑可真多,,,最终
单个java文件生成 jimple
java -cp sootclasses-trunk-jar-with-dependencies.jar soot.Main -cp . -pp -f jimple Hello -d sootOut
针对整个包进行处理问题还没解决,暂时先记录
╰─ java -cp sootclasses-trunk-jar-with-dependencies.jar soot.Main -cp . -pp -f jimple -process-dir TestCase/src
Soot started on Wed Apr 21 15:06:51 CST 2021
Exception in thread "main" java.lang.Error: Error: Failed to load java.lang.CharSequence.
at soot.JastAddJ.PathPart.getCompilationUnit(PathPart.java:109)
at soot.JastAddJ.Program.getCompilationUnit(Program.java:844)
at soot.JastAddJ.Program.getLibCompilationUnit_compute(Program.java:1517)
at soot.JastAddJ.Program.getLibCompilationUnit(Program.java:1500)
at soot.JastAddJ.Program.lookupLibType_compute(Program.java:1465)
先处理优先级比较搞的载荷收集工作
编译
配置依赖环境 jdk, FlowDroid, maven
编译:
cd FlowDroid && mvn compile -DSkipTests
...
...
[INFO] -----------------< de.fraunhofer.sit.flowdroid:parent >-----------------
[INFO] Building FlowDroid Parent Module 2.9.0-SNAPSHOT [5/5]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for FlowDroid Parent Module 2.9.0-SNAPSHOT:
[INFO]
[INFO] soot-infoflow ...................................... SUCCESS [06:00 min]
[INFO] soot-infoflow-android .............................. SUCCESS [02:44 min]
[INFO] StubDroid .......................................... SUCCESS [ 25.699 s]
[INFO] FlowDroid Command Line Util ........................ SUCCESS [ 3.720 s]
[INFO] FlowDroid Parent Module ............................ SUCCESS [ 0.001 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 09:14 min
[INFO] Finished at: 2021-04-19T10:48:58+08:00
[INFO] ------------------------------------------------------------------------
mvn的几个命令
mvn compile: 编译,将Java 源程序编译成 class 字节码文件
mvn test: 测试,并生成测试报告
mvn clean: 清除,将以前编译得到的旧的 class 字节码文件删除
mvn package: 打包,动态 web工程打 war包,Java工程打 jar 包
mvn install: 将项目生成 jar 包放在仓库中,以便别的模块调用
-DskipTests: 不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下。
-Dmaven.test.skip=true: 不执行测试用例,也不编译测试用例类。
配置下 Android SDK, 编译生成jar包,跑一下测试样例:
╰─ java -jar soot-infoflow-cmd/target/soot-infoflow-cmd-jar-with-dependencies.jar -a soot-infoflow-android/testAPKs/enriched1.apk -p $ANDROID_SDK_HOME/platforms -s soot-infoflow-android/SourcesAndSinks.txt
[main] INFO soot.jimple.infoflow.cmd.MainClass - Analyzing app /mnt/d/Working/github/FlowDroid/soot-infoflow-android/testAPKs/enriched1.apk (1 of 1)...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Initializing Soot...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Loading dex files...
[main] INFO soot.jimple.infoflow.android.SetupApplication - ARSC file parsing took 0.0242925 seconds
[main] INFO soot.jimple.infoflow.memory.MemoryWarningSystem - Registered a memory warning system for 2,833.2 MiB
[main] INFO soot.jimple.infoflow.android.entryPointCreators.AndroidEntryPointCreator - Creating Android entry point for 1 components...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Constructing the callgraph...
Found 55 instanceinvoke , 4 staticinvoke edge descriptions
...
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - The sink virtualinvoke $r10.<android.telephony.SmsManager: void sendTextMessage(java.lang.String,java.lang.String,java.lang.String,android.app.PendingIntent,android.app.PendingIntent)>($r6, $r11, $r12, $r13, $r14) in method <de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onCreate(android.os.Bundle)> was called with values from the following sources:
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - - $r6 = virtualinvoke $r4.<android.telephony.TelephonyManager: java.lang.String getDeviceId()>() in method <de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onCreate(android.os.Bundle)>
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Data flow solver took 0 seconds. Maximum memory consumption: 133 MB
[main] INFO soot.jimple.infoflow.android.SetupApplication - Found 1 leaks
调试
原项目基于eclipse编译,这个ide太丑了。。。
查了下mvn项目导入jb的资料,实践下来用 intellij idea 调试步骤:
File -> New -> Project from Existing Sources… -> FlowDroid -> Import project from external model -> Maven
Settings -> Build, -> Build Tools -> Maven -> Maven home path
等待下完Maven插件以后,编译即可
Debug -> New Application -> soot-info-cmd -> soot.jimple.infoflow.cmd.MainClass -> params -> Apply
下好断点,即可开始调试
interesting!!!
源码初步阅读
直接使用 understand 先读一下整个结构
level-1 环境初始化
代码从 MainClass.run 开始派发
命令行处理,目标收集 Collections,输出目录创建,污点分析对象初始化等操作
后续可能需要详细理解的几个函数:
soot.jimple.infoflow.cmd.MainClass.initializeTaintWrapper
soot.jimple.infoflow.cmd.MainClass.injectStubDroidHierarchy
soot.jimple.infoflow.cmd.MainClass.createFlowDroidInstance
soot.jimple.infoflow.android.SetupApplication.setTaintWrapper
level-2 污点规则解析
代码由 soot.jimple.infoflow.android.SetupApplication.runInfoflow 开始派发
SourceSink文件解析(支持 xml, txt, rifl)
level-3 Soot配置等
soot.jimple.infoflow.android.SetupApplication.runInfoflow
Soot初始化和配置,获取SourceSink规则,
apk资源解析,查找应用入口点
需要详细理解的几个函数:
soot.jimple.infoflow.android.SetupApplication.parseAppResources
soot.jimple.infoflow.android.SetupApplication.processEntryPoint
soot.jimple.infoflow.android.SetupApplication.serializeResults
level-4 分析主流程
代码由 soot.jimple.infoflow.android.SetupApplication.processEntryPoint 开始派发
这部分代码需要详细调试跟一遍
what is CG, CFG, ICFG
代码中出现了很多类似的概念,理解这三者的区别
CG是表示整个程序中方法(函数)之间调用关系的图,图中的节点是方法,边表示调用关系。
例如方法foo()调用了方法bar(),则CG中应有一条从foo()到bar()的有向边。
CFG是表示一个方法内的程序执行流的图,图中的节点是语句(指令),边表示执行流。
例如语句A执行后的下一条语句是B,则CFG中应有一条从A到B的有向边。
条件语句(if-else, while-do)之后可能执行的语句不止一个,可能执行true-branch或false-branch,
所以CFG上条件语句节点的后缀会有多个,表示其后可能执行的不同branches。
ICFG(interprocedural control-flow graph),它的信息就是CG加上CFG的信息。
ICFG可以看做是给所有方法的CFG加上这些方法之间互相调用的边(CG)所形成的图。
调用边(call edge)从调用语句(call site)连到被调方法(callee)的入口。
与CG不同的是,ICFG除了调用边,还包含相应的返回边(return edge),从callee的出口连到call site之后执行的下一个语句。
源码调试和流程梳理
还要安装windows android sdk, jdk, 真是麻烦。。。
跟下函数 processEntryPoin
通过soot提取目标所有入口点信息,即class文件相关信息
entryPointClasses = {HashSet@2176} size = 1
0 = {SootClass@2821} "de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1"
name = "de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1"
shortName = "ReflectionPrivacyLeak1"
fixedShortName = null
packageName = "de.ecspride.reflectionprivacyleak1"
fixedPackageName = null
modifiers = 1
fields = null
subSigToMethods = {SmallNumberedMap@2827}
methodList = {Collections$SynchronizedRandomAccessList@2828} size = 10
0 = {SootMethod@2837} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void <init>()>"
1 = {SootMethod@2838} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: java.lang.String deobfuscateString(java.lang.String)>"
2 = {SootMethod@2839} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void dummyActivityCalls()>"
3 = {SootMethod@2840} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onCreate(android.os.Bundle)>"
4 = {SootMethod@2841} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onDestroy()>"
5 = {SootMethod@2842} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onPause()>"
6 = {SootMethod@2843} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onRestart()>"
7 = {SootMethod@2844} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onResume()>"
8 = {SootMethod@2845} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onStart()>"
9 = {SootMethod@2846} "<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onStop()>"
interfaces = null
isInScene = true
superClass = {SootClass@2829} "android.app.Activity"
outerClass = null
isPhantom = false
moduleName = null
moduleInformation = null
refType = {RefType@2830} "de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1"
resolvingLevel = 3
number = 1915
line = 0
col = 0
mTagList = {ArrayList@2831} size = 1
0 = {SourceFileTag@2867} "ReflectionPrivacyLeak1.java"
通过 soot 生成 ICFG 控制流,截取部分:
STATIC edge: staticinvoke <dummyMainClass: de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1 dummyMainMethod_de_ecspride_reflectionprivacyleak1_ReflectionPrivacyLeak1(android.content.Intent)>(null) in <dummyMainClass: void dummyMainMethod(java.lang.String[])> ==> <dummyMainClass: de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1 dummyMainMethod_de_ecspride_reflectionprivacyleak1_ReflectionPrivacyLeak1(android.content.Intent)>
SPECIAL edge: specialinvoke $r0.<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void <init>()>() in <dummyMainClass: de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1 dummyMainMethod_de_ecspride_reflectionprivacyleak1_ReflectionPrivacyLeak1(android.content.Intent)> ==> <de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void <init>()>
VIRTUAL edge: virtualinvoke $r0.<de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onCreate(android.os.Bundle)>(null) in <dummyMainClass: de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1 dummyMainMethod_de_ecspride_reflectionprivacyleak1_ReflectionPrivacyLeak1(android.content.Intent)> ==> <de.ecspride.reflectionprivacyleak1.ReflectionPrivacyLeak1: void onCreate(android.os.Bundle)>
遍历 ICFG.cg 所有的 SootMethod(方法), 获取 SootMethod 中的 jimple 代码(方法对应的实现),
遍历所有jimple中的语句,如果存在 SourceSink 中,相应的加入到 collectedSources 和 collectedSinks
最后调用 forwardSolver.solve() 确认source点到sink点是否存在联通的数据路径,如果存在则认为是一个风险点
前向追踪的算法主要在heros中实现,暂时不去研究
总结下来,其实现的伪代码如下:
int sinkCount = 0;
// 如果 InfoflowCFG 存在 cg,则获取所有的SootMethod列表
for (SootMethod sm : getMethodsForSeeds(iCfg)) {
// 获取每个SootMethod对应的jimple语句
units = sm.getActiveBody().getUnits();
for (Unit u : units) {
if (u in getSourceInfo())
collectedSources.add(s);
if (u in getSinkInfo) {
sinkCount++;
collectedSinks.add(s);
}
}
}
// 查找Sink关联路径
if (sinkCount > 0):
forwardSolver.solve();
问题总结
FlowDroid 是针对 apk 文件进行分析
如果apk文件过大,则会在生成 数据流调用图(ICFG)时占用大量时间和主机性能
如果apk加壳,则在反编译阶段就会出现问题
因此针对apk分析除了需要完善 SourceSink 规则之外,更多的要考虑程序的稳定性和效率问题
如果只针对 java 做污点分析的话,apk反编译问题可以很好的避开,但是效率问题仍然不能忽略
在后续的研究中,如果碰到效率问题,考虑将ICFG转成CG或CFG