android string转int_Soot在Android组件NPE拒绝服务检测中的应用
点击上方蓝字关注我们噢~在对移动应用进行逆向代码静态分析时,通常可以使用正则表达式对逆向后的代码进行搜索来定位安全问题,但正则表达式仅能够对文本进行匹配,无法跟踪到代码的上下文与运行时的数据传递,效果欠佳。本文将介绍如何利用 Soot 来静态模拟应用运行时的数据传递,分析 Android 应用组件中的空指针异常。01Soot介绍Soot是什么Soot 最初是一个 Java 优化框架,它提...
点击上方蓝字关注我们噢~
在对移动应用进行逆向代码静态分析时,通常可以使用正则表达式对逆向后的代码进行搜索来定位安全问题,但正则表达式仅能够对文本进行匹配,无法跟踪到代码的上下文与运行时的数据传递,效果欠佳。
本文将介绍如何利用 Soot 来静态模拟应用运行时的数据传递,分析 Android 应用组件中的空指针异常。01Soot介绍

Soot是什么
Soot 最初是一个 Java 优化框架,它提供了四种 IR(中间表示形式),用于分析和转换Java字节码。到目前为止,Soot 可以被用来检测、优化与可视化 Java 和 Android 应用程序。Soot 详细介绍:https://soot-oss.github.io/soot/

下面是官方介绍:
Soot 提供了非常丰富的功能,我们主要目的是用 Soot 来分析 Android 组件中 Intent 取值可能引起的空指针异常,重点需要关注 Soot 提供的Call graph 生成与数据流分析。Call-graph construction(Call graph 构造)
Points-to analysis (指针分析)
Def/use chains (定义/使用链)
Template-driven Intra-procedural data-flow analysis(过程内数据流分析)
Template-driven Inter-procedural data-flow analysis(过程间数据流分析), in combination with heros (uses IFDS/IDE) or Weighted Pushdown Systems
Aliasing can be resolved using the flow-, field-, context-sensitive demand-driven pointer analysis Boomerang
Taint analysis in combination with FlowDroid or IDEal

Soot 的核心对象与 IR (中间表示形式)

Soot 核心对象
· Scene:Soot 完整的分析环境,可获取程序的分析信息,如 Call Graph
· SootClass:对应 Java 中的 class
· SootMethod:SootClass 中的方法
· SootField:SootClass 中的域(成员变量)
· Body:SootMethod 方法体,表示方法内语句的集合

Soot的 IR
Soot 会将程序转换成 IR 后进行分析,Soot 提供了四种 IR 来分析和转换 Java 字节码:
· Baf:基于栈的 bytecode
· Jimple:有类型、三地址、基于语句的 IR,soot 主要分析 Jimple
·Shimple:Jimple 的 SSA(Static Single Assignment)变种
· Grimp:Jimple 的聚合版本,更适合人读

Jimple
Soot 分析 Java 时主要使用的 IR 为Jimple,下面将介绍 Jimple 的特点与语句类型。
· 有类型:Java 被转换成 Jimple 后,类型仍会被保留
· 三地址表示:一条语句中最多只会出现三个地址,复杂语句将会被拆分
· 基于语句:语句是 Jimple 的基本组成单位
· 指令简单:相对于bytecode的200多种指令,Jimple 只有15种,分析起来更简便

Jimple的语句类型
· 核心语句:NopStmt, IdentityStmt, AssignStmt
· 过程内控制流语句:IfStmt, GotoStmt, TableSwitchStmt, LookupSwitchStmt
· 过程间控制流语句:InvokeStmt, ReturnStmt, ReturnVoidStmt
· 监控语句:EnterMonitorStmt, ExitMonitorStmt
· 其他:ThrowStmt, RetStmt
02Soot 构建 CFG

将 Java 代码转换 Jimple
纸上谈来终觉浅,介绍了这么多,还是没有看到 Jimple 的真身,那么下面将来介绍如何利用 Soot 将 Java 代码转换成 Jimple 代码,来看看 Jimple 的真面目吧。
首先需要下载 Soot 的 release 版 jar包:https://soot-build.cs.uni-paderborn.de/public/origin/master/soot/soot-master/4.1.0/build/sootclasses-trunk-jar-with-dependencies.jar写一个简单的 Java demo,命名为 Test.javapublic class Test { public static void main(String[] args) { int a = 1; int b = 2; System.out.println(new Test().add(a, b)); } public int add(int a, int b) { return a + b; }}
编译得到 Test.class 文件:
javac Test.java
Soot 拥有自己的类路径,进行 Java 分析需要将 JDK 的 rt.jar 添加到 soot 的类路径中(jdk1.8 中 rt.jar 位于$JAVA_HOME/jre/lib 中),同时亦需将 .class 文件所在目录添加到 soot 的类路径。
执行以下命令转换成 Jimple 代码:
java -cp sootclasses-trunk-jar-with-dependencies.jar soot.Main -cp rt.jar;. Test -f J
-
soot.Main 为 soot 的主类路径
-
-cp 用于指定 soot 的类路径,windows下用分号隔开,linux下用冒号隔开
-
Test 为需要转换的目标
-
-f 用于指定输出的 IR,J 代表 Jimple
执行成功后当前目录将生成 sootOutput 目录,里面即为转换后的输出文件 Test.jimple
public class Test extends java.lang.Object{ public void () // 默认构造方法 { Test r0; r0 := @this: Test; specialinvoke r0.()>(); // 调用父类构造方法 return; } public static void main(java.lang.String[]){ Test $r0; java.io.PrintStream $r1; int $i2; java.lang.String[] r2; r2 := @parameter0: java.lang.String[]; // IdentityStmt $r1 = ; // AssignStmt $r0 = new Test; specialinvoke $r0.()>(); // 构造方法 $i2 = virtualinvoke $r0.(1, 2); // InvokeStmt virtualinvoke $r1.($i2); // InvokeStmt return; // ReturnVoidStmt } public int add(int, int){ int i0, i1, $i2; Test r0; r0 := @this: Test; // IdentityStmt i0 := @parameter0: int; // IdentityStmt i1 := @parameter1: int; // IdentityStmt $i2 = i0 + i1; // AssignStmt return $i2; // ReturnStmt }}
可以看到,上面 Jimple 用到了 InvokeStmt、AssignStmt、IdentityStmt、ReturnStmt和ReturnVoidStmt,其中 IdentityStmt 与 AssignStmt 都为赋值语句,前者指的是本地变量的赋值(入参、成员变量),后者指的是其他普通赋值。

生成Android组件入口的控制流图
Soot 可以对 apk 进行分析,下面使用 soot 来生成一个 apk 导出组件的控制流图。
首先,我们来写一个 apk demo,并进行编译,得到 app-debug.apk
这次需要用到 soot.tools.CFGViewer 这个类,命令如下:
java -cp sootclasses-trunk-jar-with-dependencies.jar soot.tools.CFGViewer--graph=BriefUnitGraph--ir=Jimple--soot-class-path rt.jar;android.jar--src-prec apk--allow-phantom-refs -ire--process-dir app-debug.apk
关键参数解释:
-
--graph 用于指定图的类型
-
--ir 用于指定 IR
-
--soot-class-path 用于指定 soot 的类路径,这里需要传入 android.jar,需要找到编译 apk 对应 API 版本的 SDK,一般位于 xxx/Android/Sdk/platforms/android-xx/目录下
-
--src-prec,分析 apk 时传入 apk 即可
-
--process-dir 用于指定被分析 apk 的路径
执行成功后,sootOutput 会生成以下文件
命令如下:
dot -Tpng "com.example.npedemo1.NpeActivity void onCreate(android.os.Bundle).dot" -o npe.png
执行成功后即可得到该类的控制流图(见下文)。03
NPE 拒绝服务分析
不同于 Java 程序具有固定的 main 方法入口,Android 程序的入口一般为可导出组件,而唤起导出组件时需要传入一个 intent。所以对 Android 组件的 NPE 拒绝服务分析,我们一般只需关注 intent 带来的数据。下面将介绍对以下 demo 的 NPE 拒绝服务分析。源码:public class NpeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_npe); Intent intent = getIntent(); String extra = intent.getStringExtra("extra"); if (extra != null && intent.getAction() != null) { intent.getStringArrayExtra("extra").equals("abc"); intent.getAction().equals("def"); return; } if (extra == null) { extra.hashCode(); return; } extra.toUpperCase(); intent.getAction().toUpperCase(); }}
通过上述方法得到的控制流图:

基本分析思路
· 数据收集,分析整个流程 intent 产生的数据
· 约束收集,通过判断语句来获得每条语句的前置约束
· 约束计算
上图由 intent 产生的数据共有三种,分别是extra、other_extra、action。
假设:· a = (extra == null)· b = (action == null)· c = (other == null)可得到每条语句的约束,如下图:
下面,根据约束来分析语句的调用方是否为 null。
首先看最左边的分支:



利用 Soot 进行 NPE 分析
上文提供了分析的思路,下面将简单介绍如何利用 soot 进行自动化分析。由于篇幅有限,本文只介绍关键思路与关键API的实现。
Soot 环境配置与分析:public static void sootPreSet() { G.reset(); Options.v().set_src_prec(Options.src_prec_apk); Options.v().set_output_format(Options.output_format_jimple); Options.v().set_process_dir(Collections.singletonList("app-debug.apk")); Options.v().set_android_jars("D:\\Android\\Sdk\\platforms"); Options.v().set_whole_program(true); Options.v().set_allow_phantom_refs(true); Scene.v().loadNecessaryClasses(); PackManager.v().runPacks();}
执行完上面的代码就可以通过Scene.v()获取到apk相关的信息(类、方法等)。
准备工作
· Intent 数据流建模:建立每条语句的向前数据流
· Intent 分支约束建模:建立每条语句的向前分支约束

Intent 数据流建模
soot 提供了三种FlowAnalysis,分别是ForwardFlowAnalysis、BackwardsFlowAnalysis与ForwardBranchedFlowAnalysis,这一步,我们需要收集每条语句执行后,该语句之前的所有数据,所以应选用向前数据流分析。
ForwardFlowAnalysis关键API:entryInitialFlow数据流的初始化,指方法入口可能产生的数据流,这里我们只需要关注 intent 数据。在方法入口中,intent的来源有两种,分别是类成员变量或方法的入参,只需要根据参数类型是否为android.content.Intent即可判断是否需要加入到数据流。flowThroughprotected abstract void flowThrough(A in, N d, A out);// in:执行前的数据流// d:当前语句// out:执行后的数据流
该API用于计算每条语句执行后的数据流,即out,已知参数为执行前的数据流。在这个方法中,首先我们需要将in复制到out(即默认情况下出口需要包含语句执行前的入口数据)。然后判断语句是否为赋值语句(有赋值才有数据的产生),再判断语句的右表达式是否为调用语句(如getIntent、getStringExtra)。如是且返回类型为intent或调用者为intent(需判断是否为getAction或getXxxExtra),则加入到out中。
Intent 分支约束建模:
这一步目的是收集每条语句执行后,该语句之前的所有约束条件,需继承 soot 的 BranchedFlowAnalysis。BranchedFlowAnalysis关键API:entryInitialFlow约束的初始化,指方法入口可能产生的约束,这里我们默认为true(方法入口不存在任何约束)flowThroughprotected abstract void flowThrough(A in, Unit s, List fallOut, List branchOuts);// in:执行前的约束// unit:即语句stmt// fallOut:不进分支的约束// branchOut:进入分支的约束
同理,这个方法需要计算语句执行后的约束,首先也是需要将in赋值到out,然后判断语句是否为判断语句(IfStmt),如是,将语句加入到out中。这里fallOut是指if条件不成立的约束,branchOut指if条件成立的约束。05
分析流程
前面计算好每条语句之前的数据流与分支约束,信息已经收集完毕,下面可以开始对数据进行分析了。流程图如下:
分支约束计算举例

长按关注 更多安全技术干货等你发现
更多推荐
所有评论(0)