王国珍,杨红丽
(北京工业大学 信息学部,北京 100124)
随着移动互联网的高速发展,智能手机已经成为每个人生活中必不可少的工具,手机操作系统也正在高速发展.截止到2017年,Android智能手机在全球市场中占有超过85%的市场份额[1],占据着市场的主导地位.Android应用市场为用户提供了功能各异的应用程序,截止2017年2月,Google Play已经发布了2700 000个应用[2].Android应用的种类变得更加多样,提供的功能也更加专业,应用之间可以分享信息并且相互协作完成一些复杂的任务.例如,一个电子支付应用可以被多个第三方的电子商务应用调用来完成支付功能.Android系统提供了开放活动(Exported Activity,EA)机制,可以将应用内特定的Activity分享给其他的应用.
EA往往包含开发者想要推广的关键功能,可以被其他的应用重复执行.因此,它们的质量是关键功能得到有效推广的关键因素.另一方面,因为EA可以被任意的应用执行,因此包含EA但是开发不是很完备的应用很容易被恶意应用滥用[3].综上,开放活动的质量验证是一个非常值得关注的课题.
在Android应用质量验证方面,测试是一个很热门的课题.大部分现有的工作主要集中在分析和测试单个应用的图形交互界面(Graphical User Interface,GUI)上,被测应用与外部应用之间的交互往往被忽略.我们的实验结果表明,在现有工作中EA很难被覆盖到.因此,在对Android应用的测试过程中应该使用针对性的技术对这类Activity进行测试.
本文讨论了如何系统的测试Android应用中的EA.首先,自动的生成一系列的应用作为测试驱动应用,然后利用这些测试驱动应用启动目标应用中的EA,发现其中的漏洞.本文使用数据流分析技术得到启动EA所需的信息,结合预先定义的模板生成测试驱动应用.选取了10个现实生活中使用非常广泛的安卓应用进行了测试并且有效的检测到了一些问题.通过分析测试结果,将检测到的问题分为两类:应用崩溃和界面异常.实验结果表明本文提出的方法可以有效的对EA进行系统化测试,间接提高了存在EA应用的质量.
这部分内容将介绍在一些相关的背景知识,主要包括EA和Android系统提供的Intent机制.
Activity是使用率最高的Android组件,它提供了与用户交互的GUI窗口.Activity可以分成两类:内部活动(Internal Activity,IA)和开放活动(Exported Activity,EA).前者只能被同一应用中的其他组件启动,后者允许被外部应用调用.EA是一种应用间有效的交互、协作方式.默认情况下除了应用的Main Activity之外所有的Activity都是IA类型,开发者可以在Manifest文件中将Activity的属性(android:exported)设置为TRUE,使得该Activity变为EA类型.如果不设置android:exported属性,也可以在Manifest配置文件中EA对应的 外部应用通过Intent机制调用其他应用中的EA.Intent是消息传递的载体,其中包含目标组件的标识符和目标组件初始化所需的数据. 发送方组件通过一系列重载的API(Application Programming Interface)以键值对(key-value)的形式将不同类型的数据附加到Intent中,例如,putExtra(String,int)可以添加整形数据,putExtra(String,String)可以附加字符串型数据.该方法的第一个参数是用于识别不同数据的键值(key),第二个参数是表明该数据的值(value).Android系统还提供了接收方组件获取特定键值所对应数据的API.例如,getIntExtra(String key)可以根据指定的key值来获取其对应的整形数值. 第三方开发商开发的应用通常会通过该机制为EA传递数据来完成一些任务,所以数据的有效性检验和EA的容错性是非常重要并且需要特别关注的方向. 本小节将通过一个简单的例子介绍本文研究的动机.图1是样例应用中一个EA(Foo)的代码片段,该EA不会被应用中的其他组件调用.在onCreate方法中包含一条字符串类型数据和一条整形数据,它们的键值分别为:“key1”、“key2”.由于该 Activity 不会被应用自身调用,所以现有的技术和工具[4,5]不会覆盖到它. 图1 EA的代码片段 图2是一个调用EA的测试驱动应用的示例代码.它创建了一个Intent实例,设置它的目标Activity组件为Foo,并且设置了Foo初始化数据,最后通过startActivity(Intent intent)方法执行该Intent.通过这个例子可以发现,对EA系统化测试的关键问题是如何为Intent中各种各样的数据赋值. 图2 Foo的测试驱动应用 本文提出了一种系统化测试Android应用中EA的方法.该小节中,首先对方法进行了简单的概述,然后详细介绍几个关键模块. 为了测试待测样本中所有的EA,本文所提方法的主要思想是自动化的生成一批测试驱动应用,每个测试驱动应用会执行携带不同数据(包括无效数据)的Intent,启动被测EA以检测它处理不同数据时是否会出现漏洞.图3是方法的概述.它包含四个模块:Android安装包(Android Package,APK)解析、Intent分析、测试驱动生成、测试执行.第一个模块提取待测样本中所有的EA信息,包括:EA的包名和类名等;第二个模块获取每个EA启动所需的数据信息;测试驱动生成模块生成一批可调用EA的测试驱动应用;最后一个模块在一台真实的设备上执行生成的测试驱动应用并记录执行结果. Android应用大多数都采用Java语言实现并被编译为Dalvik字节码.应用中的字节码文件、资源文件和配置文件最后被打包成一个二进制形式的APK文件.为了进行后面的分析,本文首先利用Apktool[6]对APK文件进行反编译,反汇编二进制代码和配置文件.Apktool是Google官方提供的一款对Android应用逆向工程的工具.基于Android官方文档中对Manifest配置文件的介绍,解析Manifest文件提取所有Activity信息,根据EA的android:exported属性以及 图3 方法概述 为了构建测试驱动应用,需要获取每个EA所需的数据信息.根据上文所述,Activity主要利用若干特定的系统API(参见1.2)将键值(或者数据名称)指定为API的参数来获取数据内容.因此,该模块的目标就是提取每个EA中数据的键值,并通过判定API的类型确定数据的类型. 以图1中的源码为例,需要分析该EA代码中所有的方法并且确定getStringExtra和getIntExtra的第一个参数的值,即Intent中附加数据的键值.实际上,该问题可以被看作是一个到达定值问题,它是数据流分析中最普遍和最有用的模型.到达定值模型可以静态的确定那些预定义的变量在到达代码指定位置时的值.本文中使用Soot[7,8]提供的数据流分析模型ForwardDataFlowAnalysis实现了该算法. 该模块生成测试EA的测试驱动应用,预先设计了一个测试驱动应用的模板,只包含一个Activity.在入口函数onCreate中创建了一个Intent实例,它的目标Activity是被测EA,这样就可以在测试驱动程序启动后可以自动的启动目标EA.之后我们随机生成之前模块中提取到的键值所对应数据类型的值,并且使用putExtra APIs将生成的数据添加到Intent实例中.在onCreate的最后将Intent作为startActivity API的参数启动EA. 除了这些代码,Manifest文件也是应用执行的重要部分,所以创建了一个Manifest文件并且在其中声明Activity列表.最后,将源码和Manifest文件一起打包成一个APK文件. 测试驱动应用生成之后,将它们部署到设备中并执行它们去启动EA,为了检测EA中存在的问题,需要记录执行过程中设备中出现的信息.使用Adb工具将上述过程自动化,Adb是Android 软件程序工具包(Software Development Kit,SDK)提供的一款用于连接Android设备的工具.Adb可以批量的将测试驱动应用安装到设备中并执行这些应用,测试驱动应用被Adb启动后会自动化的启动测试目标应用中的EA,并且保存设备的屏幕截图供之后的分析用. 为了评估方法的有效性,本文根据上文提到技术实现了一个工具EASTER(Exported Activity:Specific TestER),使用若干真实应用在一台Google Nexus 5上进行了实验. 从Google Play和其他的Android市场上下载了10个应用作为实验应用.表1展示了这些应用的详细信息,包括应用大小、Activity的数量和EA数量.从表中可以发现EA在现实应用中被广泛使用,系统的测试它们是一个非常重要的研究课题.本文使用了很流行的Android应用黑盒测试工具Monkey[9]对处理过的应用进行测试,用以评估Monkey对应用中EA的覆盖情况,本次试验中对应用中EA的字节码进行了插桩处理.实验中设置Monkey对每个应用进行测试时生成的事件数量为10 000.覆盖率如表1中最后一列所示,实验表明应用中大约有17%的EA可以被Monkey生成的事件序列所覆盖. 实验中首先使用EASTER为应用中每个EA生成一个测试驱动应用,然后让它们在设备中有序的运行.试验中,如果一个测试驱动应用可以成功的启动该EA,就认为该测试驱动应用可以覆盖该EA.表2展示详细的实验结果,其中第三列给出了被覆盖的EA数量,最后一列表明了覆盖率.从表中可以发现,生成的测试驱动应用可以覆盖实验应用中大多数的EA(平均73.7%),覆盖率远远大于Monkey对EA的覆盖率.有几个特殊的样本的覆盖率较低,例如:Snapchat和腾讯新闻.这些应用中对一些EA设置了某些系统或者自定义的权限,拒绝来自于未授权应用的调用请求.这个问题将在之后的工作中解决. 表1 实验应用 表2 EASTER的覆盖率 实验中使用EASTER结合不同的Intent数据为应用中每个EA生成了5个测试驱动应用,并深入研究了它们执行过程中出现的问题.可以将实验过程中出现的问题分成两类:应用奔溃和显示异常,其中前者表示该EA在测试中会崩溃,后者代表EA显示不正常,例如显示没有友好用户提示的空白窗口.表3展示了详细的实验结果,其中第二和第三列是生成测试驱动应用的数量和报告出有漏洞EA的数量.最后两列分别列出了在EA测试过程中出现应用崩溃和显示异常两类问题的测试驱动程序的数量. 表3 EASTER检测到漏洞数量 本小节中进一步的分析了存在漏洞应用的代码,准确的定位漏洞的位置.首先利用反编译工具jdgui[10]将有漏洞的应用的字节码转换成Java程序,之后人工的检查检测报告中EA的代码.下面分别对两个应用中的两种漏洞进行分析. 图4显示爱奇艺应用中一个类名为 FalconActivity的EA的代码片段.推测第三行中开发者忽视了对“argument”键值所对应数据使用前的有效性校验.在实验中发现,随机生成一个字符串赋值给该变量,应用总会出现崩溃. 图4 爱奇艺应用的代码片段 图5给出了优酷应用中类名为 GameDetailsActivity的EA中onCreate方法的代码片段.我们可以发现如果Intent不为空并且appid为空时loaddata方法不会被执行并且该Activity会显示了一个空白窗口,这样会迷惑用户. 图5 优酷的代码片段 这部分将简要介绍了几种Android应用自动测试生成方法的相关工作. Fuzzing是一个被普遍使用,对Android应用进行黑盒测试的方法.Machir等人[4]开发了一款随机生成系统事件的工具——Dynodroid,该工具生成的事件与用户事件相同. 基于模型的测试也是测试GUI程序典型的技术,也可以用来测试Android应用.Wang等人[5]将GUI行为构建成一个FSM模型,他们利用静态分析技术提取与控件相关的事件,使用动态探测技术捕获Activity的迁移.Azim和Neamtiu[11]也提出了一个测试生成工具——A3E,它使用静态分析技术构建GUI模型,基于深度优先搜索和目标搜索策略并结合GUI模型生成事件序列. 以上研究值关注为应用生成事件序列.Mirzaei等人[4]将Android应用中的事件输入和数据输入区分开,利用符号执行引擎Symbolic PathFinder(SPF)生成数据输入.Jensen等人[12]使用符号执行技术生成事件和数据覆盖指定代码. 现有的工作主要是通过动态执行应用或者静态分析代码来提取和遍历程序行为.这些方法有一个共同的缺点就是它们不能有效的覆盖应用的特殊部分——EA,因为EA往往在应用内部不会被调用,大部分EA是提供给外部应用进行访问的. 本文介绍了一个自动化测试Android应用中EA的方法,现有的工作中很少有相关的讨论.实验结果表明EA被广泛的应用在现实的应用中,但是很多时候开发者对它的开发不是非常完备.由于EA通常包含开发者渴望推广的功能,对EA进行系统化的测试在保证Android应用质量的研究中是非常值得关注的问题.相信本文的工作是对现有工作的一个非常有用的补充,之后的研究也是很有意义的. 本文只是对Android应用中EA进行了初步的研究,并没有全面考虑EA和Intent机制的所有特性,例如Intent Filter和Permission等.之后的工作将解决这些问题以加强EASTER的性能.除此之外,Intent的数据也是通过自己开发的工具随机生成的,之后的工作中也可以使用模糊测试或者混合测试技术加强EASTER的功能.1.2 Intent
1.3 示例说明
2 测试方法
2.1 方法概述
2.2 APK解析
2.3 Intent分析
2.4 测试驱动生成
2.5 测试执行
3 试验结果与分析
3.1 实验设计
3.2 EA的测试覆盖率
3.3 实验结果分析
3.4 案例分析
4 相关工作
5 结语