目录
Soundness & Soundiness
复杂语言特性一:Java Reflection
复杂语言特性二:Native Code
重点
理解soundiness的含义,为什么要引入它?
理解为什么Java reflection
和native code
分析这么复杂。
课程就这么结束了,抽象解释可能要在研究生课程再讲。
分析真实复杂程序时,产生的问题都与Soundiness有关,是最前沿的topic之一。
1.Soundness & Soundiness
Soundness:保守估计,分析结果能包含程序所有可能的执行。学术界和工业界都做不到。
复杂语言特性:导致分析结果不精确。
- Java:Reflection, native code, dynamic class loading, etc.
- JavaScript:eval(执行任意命令), document object model (DOM,和DOM加护), etc.
- C/C++:Pointer arithmetic(指针地址+1或乘以), function pointers, etc.
现状:有些文章不提这类问题,或者随意一提(如eval)。极具误导性,导致相信该工具很sound,且影响专家的评判。
Soundiness:直观相信的"truth",但没有任何事实和证据。
词语对比:
- sound analysis:能捕捉所有动态运行行为,理想化。
- soundy analysis:目标是捕捉所有动态行为,但对于某些复杂语言特性可以unsound。
- unsound analysis:为了效率、精度,故意不处理某些行为。
2.复杂语言特性一:Java Reflection—反射
(1)介绍Java反射
Java Reflection:反射机制很重要的一点就是“运行时”,其使得我们可以在程序运行时加载、探索以及使用编译期间完全未知的 .class
文件。换句话说,Java 程序可以加载一个运行时才得知名称的 .class
文件,然后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。
说明:无反射代码在编译时就能确定对象;反射代码在运行时才确定对象,如c指向什么,"Person"也可能是的字符串指针,很难静态分析。分析该类代码很有必要,如弄清对象到底调用了哪个目标函数、对象域的指向关系等。
(2)分析方法
分析方法:String Contant analysis + Pointer Analysis(Reflection Analysis for Java——APLAS 2005)。
示例:目标是分析m调用的目标函数。
- 找到m的定义点,即
Method m = c.getMethod(mName, ...);
- 通过
String Contant analysis
找到mName
指向内容 - 通过指针分析找到c指向内容
- 通过
String Contant analysis
找到cName
指向内容 - 知道了是调用
Person
类的setName
函数
问题:若字符串的值无法通过静态分析得到,则反射目标不能求解。Eg,字符串来自配置文件、网络、命令行、复杂字符串处理、动态生成、加密。
(3)改进
解决方法:Type Inference + String analysis + Pointer Analysis(Self-Inferencing Reflection Resolution for Java——ECOOP 2014,李樾,谭添老师的成果)。在创造点不可推,但在使用点可推。
示例:类名依赖cmd参数,解不出来;但在调用点,通过Java的类型系统推导parameters
,发现parameters
是this指针。推出结论就是,175行的目标函数有1个参数,且声明类型要么是FrameworkCommandInterpreter
要么是其子类。结果推断出50个反射目标函数,48个为true。
最新工作:Understanding and Analyzing Java Reflection (TOSEM 2019) Yue Li, Tian Tan, Jingling Xue。不仅求解反射对象更准确更多,而且能说出哪里解的不准。
常用方法:Taming reflection: Aiding static analysis in the presence of reflection and custom class loaders (ICSE 2011)。利用动态分析来解,缺点是测试用例的覆盖路径有限,优点是只要解出来,结果都为真。
3.复杂语言特性二:Native Code
Native Code:一个Native Method就是一个java调用非java代码的接口。该方法的实现由非java语言实现,已被编译为特定于处理器的机器码的代码,这些代码可以直接被虚拟机执行,与字节码的区别:虚拟机是一个把通用字节码转换成用于特定处理器的本地代码的程序,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern “C”告知C++编译器去调用一个C的函数。
Java Native Interface(JNI):是一种编程框架(函数模型,反映参数格式等),使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。
示例:先加载Native库,声明Native函数,*env
变量可以在Native代码中用于创建对象、访问域、调用Java中的方法等,支持230个JNI函数。问题是跨语言之后,如何静态分析je.guessMe()
这个调用?
方法:对重要的native code
手动建模。例如,对经常调用的arraycopy()
函数进行建模,建模后就是一个拷贝循环,但从指针分析角度来讲,看到这个循环,我们就把数组指针进行传递。
最新工作:Identifying Java Calls in Native Code via Binary Scanning (ISSTA 2020)。通过扫描二进制程序来识别native code
中的Java调用。
扩展:想深入研究Soundiness,可参考网站http://soundiness.org。