-
-
[翻译]StaDynA:解决Android APP安全分析中的动态代码更新问题
-
发表于: 2018-5-27 10:01 4846
-
动态代码更新技术:动态类加载(dynamic class loading)和反射(reflection)常常限制了静态分析技术。最近的Android恶意软件也用到了这些技术来隐藏它们的恶意行为,以免被静态分析工具发现。这些技术确实可以躲过近来的大多数静态分析工具(例如[12,21,31]),因为这些工具通常是假设一个“封闭的世界”(反射调用的目标可以在分析阶段解析出来;只有在分析阶段类路径可达的类才在运行时用到)。我们提出的方案可使现有分析工具不用基于这个假设。我们通过结合静态和动态分析揭露应用程序的隐藏/更新行为,并基于这一信息扩展了静态分析结果。这篇论文了包括StaDynA的设计、实现和初步评价。
移动应用程序(简称app)利用Android平台的动态代码更新功能给用户提供了丰富的体验。
但是,这些功能(反射和动态类加载)却给静态分析app带来了巨大挑战。尤其是这些静态分析是用来检测移动应用的安全(也就是,检测是否存在恶意行为)。实际上,Rastogi et al. [40] 提到反射技术可以使现在的很多静态分析工具无法检测到恶意代码。另外,动态代码更新也限制了静态分析工具,因为有些代码在安装的时候是无法检测或分析到的,它们只在运行时出现。事实上,现在的移动应用分析工具([12,21,31])都假设代码不会动态更新,并且反射调用的目标可以提前检测到。这是实际情况的简化版本,因为在实际情况中,很多app都需要在运行时更新代码。
Wang et al. [43] 证明了检测恶意应用的难度。他们开发了一个poc恶意iOS app,可以绕过苹果的App Store的审查过程。审查时上传的代码是正常的,但是该app可以在设备上自动更新,引入恶意的控制流,并实施恶意操作(例如攻击其它app和利用内核漏洞)。Android平台也已经开发出同样功能的poc app,利用动态代码更新技术绕过Google Bouncer,也已经开发出来了[38]。
同时,前人提出的解决Java动态更新技术的静态分析方法(例如[17]),因为平台的不同,也不能直接运用到Android平台(Android不支持类的加载时instrumentation,load-time instrumentation)。而且离线instrumentation也不能解决这个问题,因为这样会破坏应用程序签名,有些应用程序在运行时会检查签名。如果签名与某些硬编码的值不一致,它们可能就无法运行。而在恶意应用中,这个检查可以用来隐藏恶意行为。
本文中,我们提出了StaDynA,一个帮助安全软件分析动态代码更新功能的系统。我们的贡献主要包括:
余下部分组织如下。§ 2是我们关于Android应用中动态代码更新功能的分析结果。§ 3是Android中动态类加载和反射的相关背景知识。§ 4是StaDynA概述,在§ 6中会给出实现细节。§ 5介绍如何构建方法调用图并可视化。§7是用实际app评估StaDynA。§8讨论不足并展望未来。§9是相关研究,§10是结论。
##2.Android APP中的动态代码更新功能分析
为了解Android app中反射和动态类加载(dynamic class loding,DCL)的使用有多广泛,我们研究了来自Google Play[10](Google官方应用商店)的13863个应用和在2013年6月收集的来自几个第三方应用市场的14283个应用,以及来自[51]的1260个恶意应用样本。注意,在反射中,我们只考虑会影响app的方法调用图(method call graph,MCG)的调用,也就是方法调用(invoke
)和对象创建(newInstance
)函数,而不考虑其他的反射API,比如域修改(因为在我们的分析中它们不会影响MCG)。
我们用修改过的Androguard[1]对这些应用分析的结果如表1所示。很明显,动态代码更新功能被应用程序开发者广泛使用。
我们在Google Play下载了每一个目录下的排名前500的免费app。分析显示,Google Play上的app,有18.5%使用DCL,88%使用反射。平均,一个app会用到一次DCL,和22次反射调用。 ”BUSINESS“, "SHOPPING" 和 ” TRAVEL_AND_LOCAL“的DCL使用率最少(最高是10%)。使用动态功能最多的目录是”GAME“,这个目录下38.3%的app使用了DCL。
我们还从其他6个第三方应用商店,分别是androidbest [4], androiddrawer [5], androidlife [6],
anruan [7], appsapk [8] and f-droid [9] 下载了app。前五个只提供apk文件,最后一个(f-droid)在提供apk之余还提供了源码链接。f-droid中的应用程序的DCL使用量最少,只有1个用到了。就单个应用来说,第三方应用商店中的应用,平均每个应用用到了19次反射(f-droid依然是使用率最低的,大概14次)。
除了分析正常应用,我们还分析了[51]中提供的恶意样本。所有恶意样本的DCL使用率大约是19.9%,而反射是81%。但是,这个数据集有点老了,DCL在最近的恶意软件中的使用率应该会更高[38],因为这个功能可用于隐藏恶意payload[26],以避开像Google Bouncer这样的静态和动态分析工具。
Listing 1是AnserverBot 木马[50]的一个代码片段,展示了如何使用反射和DCL绕过静态分析工具的恶意功能检查。第16行是用DexClassLoader动态加载类的一个例子。代码加载的文件名称在运行时由第8行计算得出。第26行表现了如何用反射调用创建已加载类的一个对象。第28行是反射调用的一个例子,调用的方法名是一个参数,所以可能有些静态分析工具检测不到。
为方便理解StaDynA的设计,我们提供了一些关于Android中动态类加载和反射实现的背景知识。注意,在本文中,我们只考虑Dalvik虚拟机(Dalvik VM或DVM)。相同的功能,也就是DCL和反射,在最近的Android runtime(ART)中也有。ART在最近Android 平台中取代了DVM。
程序在运行时操控数据等表示程序状态的能力就叫做反射[16]。尽管Android是基于Dalvik VM的,但是反射API和Java的大都一样(只有少数不一样)。这个API用于在运行时访问类的信息,创建对象,调用类方法,改变修饰符,和数据成员的值[44]。更具体的说,Android出于以下几个目的使用反射API:
隐藏API方法调用。Android OS的开发者将某些方法标记为隐藏(使用@hide
标记)在这种情况下,这些方法的声明和描述在SDK库中是不会出现的,因此,开发者就无法使用它们。与此同时,app 开发者可以在运行时使用反射API调用这些方法。
访问私有API方法和域 。在编译的时候,编译器要确保根据特定的修饰符访问域和方法。但是,使用反射API可以操作修饰符,因此,可以在运行时访问私有成员。
将 JSON和 XML 表示的东西转换为 Java对象。 反射API大量使用在自动将JSON和XML对象转换为Java对象以及相反操作的场景中。
向后兼容。 建议使用反射使得app可以兼容先前的Android SDK 版本。在这种情况下,反射要么用来调用在先前版本中以及被标记为hidden的API方法,要么用于检查需要的SDK类和方法是否存在于当前的框架版本。
插件和外部库支持。 为了扩展应用程序的功能,反射API可以在运行时使用动态类加载功能调用插件或外部库方法。
Dalvik VM允许开发者加载从某一位置,诸如外存或网络,获取的运行时代码[9]。这个功能通常用于:
突破64K方法数限制。 dex文件中引用的最大方法数是64K,但是超出的方法可以放在另一个dex文件中并动态加载。
在运行时扩展app功能。 app可以提供一个桩来用其他人写的代码处理事件。这些代码块叫做插件,而DCL被广泛用于加载插件到内存中。
尽管Android允许开发者动态加载和执行代码,Google却强烈建议不要使用这一功能[3]。这一建议是因为DVM没有为这些动态的代码提供一个安全的环境。因此,这些代码有着和加载它们的那些app一样的权限。而且,DVM没有从底层操作系统中限制这些代码,因此这些动态加载的代码可以不受限制的操控原生库[3]。这些就是Android安全体系与Java的不同之处。
类加载器负责控制将类加载到DVM。Android的类加载过程与Java的相似[34,41]。与JVM一样,Dalvik VM也是用bootstrap
类加载器加载核心API类,用system
加载应用程序类。
和Java一样,Android类加载器以树形式组织。为组织其结构,每个类加载器都有指向其父节点的引用。bootstrap
类加载器是这棵树的根节点,其父节点为null
。应用程序还可以定义额外的类加载器。在Android中,所有的额外类加载器均由java.lang.ClassLoader
派生(可能是间接的)。Android提供了几种该类的具体实现方式,PathClassLoader
和DexClassLoader
是最常用的两个。
StaDynA架构如Figure 1 所示,包括两个部分:服务器端和客户端。
对app的静态分析是在服务器端进行。在这一部分,分析人员可以在StaDynA中插入并使用任何静态分析器。服务器端的静态分析器形成该app的初始方法调用图(method call graph,MCG),并与来自客户端的动态分析结果整合,然后保存结果。StaDynA的客户端是一个修改过的Android 操作系统,可以是真机或虚拟机。当需要进行动态分析时,客户端就运行该app。
在操作过程中,我们的系统交错执行静态和动态分析。但是,为简化描述,我们分个先后描述。
初步分析
服务器静态分析该app程序包,并形成其MCG(见Figure 1的第a步,实线表示静态解析)。不会在这个阶段分析动态加载的代码,因此,相关的点和边不会在MCG中出现。另外,如果反射调用的函数名称是加密过的字符串或动态形成的,那么它们也无法被推断出来。还有,静态分析工具可以有效的检测会在运行时扩展的MCG中的点。事实上,反射和DCL的使用需要用到Android平台提供的特定API调用。服务器端在静态分析时通过搜索DCL和反射API调用会用到的方法来检测这些调用。我们称这些方法为MOI(methods of interest)。
动态执行
如果检测到了app的MOI,StaDynA将安装该app到客户端(第二步),并启动动态分析。动态分析阶段将补充该app的MCG,并访问动态加载的代码。动态分析是在搭载了修改过的Android操作系统的真机(或虚拟机)上执行的。增加的修改会在app用反射调用或动态加载额外代码时记录这些事件。除了这些事件,客户端还提供其他的信息,也就是说,在反射调用时,调用函数和stack trace(包括从开始到最近的方法调用)信息也会添加进来。在DCL调用时,将提供代码文件路径和stack trace。这些由客户端搜集的信息会传回服务器端(第三步)。
进一步分析
服务器端根据得到的信息进行进一步分析。当发生反射调用时,服务器端会给app的MCG增加一条新的边(在Figure 1中以虚线表示)。这条边连接通过反射初始化的那个方法的点(起点)和被调用的函数(终点)。
若触发DCL,客户端就判断哪个文件是用来获取代码的。根据这个信息,服务器端下载该文件(第四步),并对它进行静态分析。app的MCG将根据获取的信息进行更新(见Figure 1 中MCG的虚线椭圆)。另外,对于每一个下载的文件,服务器会分析它是否包含其他的MOI。如果有,将更新这款app的MOI列表,也就让StaDynA探索到更多隐藏起来的MOI。stack strace数据和反射、DCL都用来检测哪个MOI初始化了这个调用。
标记可疑行为
在Android中,有些API 调用是需要权限的。因为需要权限的API可能会对系统造成伤害或者侵犯用户隐私,所以需要在AndroidManifest.xml
文件中申请权限。但是,系统并没有对运行这些代码所需要的权限进行确切的检查,而且有的时候,程序开发者还会过多的申请权限,也就是申请权限过度。很多研究,例如Bartel et al. [14],证明恶意软件、广告软件、间谍软件在运行时会利用额外的权限访问敏感信息。
根据这些考虑,我们将以下行为模式定为可疑:
我们的工具已经添加对此类可疑模式的检测。若在分析时发现此类模式,StaDynA将发出警告。第七部分证明确实有恶意软件会暴露这些可疑行为。
方法调用图(或函数调用图)定义了程序中方法的调用与被调用关系。程序的这些结构化表示被广泛用于各种场合。在Android中,方法调用图用于,例如,恶意软件检测[27,29,33],检测应用程序中潜在的隐私泄露[23,28,49],挖掘漏洞[42]和自动测试的路径执行[48]。
StaDynA利用在运行时获取到的信息扩展了由传统静态分析工具生成的初始MCG。因此,若某个应用程序有动态加载行为,上述方法(恶意应用检测、隐私泄露、漏洞挖掘这些)将可从由StaDynA扩展的MCG图获益。
例子
为可视化StaDynA的能力和MCG延展的过程,我们以demo_app为例进行评估。Figure 2a 是由静态分析工具AndroGuard得到的该app的MCG,Figure 2b 是StaDynA在还没有执行动态分析得到的MCG,Figure 2c 是经过动态分析之后的。demo_app在运行时动态从外部的jar文件加载代码并经反射调用。
Figure 2a 表明AndroGuard仅能发现正常的方法和DCL调用(椭圆1),但并没对它们进行进一步分析。Figure 2b 说明在经过初步分析后,StaDynA选择了三条路径,也就是图中的三个椭圆。椭圆1是一个MOI(那个灰黑色节点),经反射调用了一个构造函数(深绿色节点)。同样的,椭圆2 展示了一个经反射调用的方法。椭圆3描述了在一个MOI(灰黑色节点)中的DCL调用(红色节点)。
在动态分析的时候,StaDynA添加了椭圆4-7中的边(见 Figure 2c)。这些椭圆表示在解析这些MOI的时候,其相关的点和边将被加入到MCG中。椭圆4 是一个DCL调用(红色节点)调用一个新的代码文件(粉色节点)的结果。椭圆7表示一个类构造函数(灰色节点)被反射调用。椭圆5表示某个方法被反射调用。这个方法包含了一个需要Android权限的API调用,也就是椭圆6中的蓝色节点。还有其他的点和边都是对动态加载的文件(粉红节点)分析的结果。图中剩下的点和边都是经由new instance
调用的(见椭圆7)。
椭圆2,3,8,9是经由我们的工具获得的MCG的点中其它类型的连接。椭圆2连接的是某个类和它的构造函数。椭圆3是两个方法之间的正常调用。椭圆9连接某个类和它的静态初始化模块,而椭圆8是被静态初始化代码块调用的方法。
每个节点类型都分配一系列没有在图中展示的属性。对这些属性值的分析可以帮助解析Android应用程序和扩展的方法调用图。举个例子,每个方法节点都带有一些属性,包括类名,方法名,和方法签名。权限节点就包括其权限等级和它所保护的API调用信息。
这一部分提供StaDynA几个关键点的实现细节。系统的工作流图如Figure 3 所示。先在服务器端分析APP。所有出现的反射和DCL方法在被分析的应用程序的代码中标记。如果没有任何发现,StaDynA就构造该app的MCG并退出。否则,它将在搭载修改过的Android 操作系统的机子上,也就是StaDynA的客户端进行动态分析。
StaDynA的服务器端是一个Python程序,它内嵌了一个静态分析工具。目前,StaDynA用AndroGuard[1]作为静态分析工具。AndroGuard把编译好的Android代码看作一系列可操作和分析的python对象。但是,StaDynA可以和任意能够分析apk和dex文件的静态分析工具一起合作。为了提高对可疑行为的检测,我们用PScount[13]为Android4.1.2生成的权限映射表替换了AndroGuard中的映射表(在[25]中为Android 2.2)。
服务器端主要功能的伪码如Algorithm 1 所示。服务器端首先分析给定的app,通过提取其classes.dex
文件(见Figure 3的第1,2,3步;Algorithm 1的第2行),然后提取其代码。在这一步,StaDynA搜索代码中所有的反射和DCL调用。对这些API调用的搜索模式列表如Table 2所示。
如果发现MOI,StaDynA将选择一个设备(真机或实体机)进行动态分析(第8行)并将要分析的app安装到客户端(Figure 3 的第五步)。之后服务器端获取这个app的UID,然后开启一个循环(13-25行)一行行分析logcat日志。基本上,每一条获取到的信息都是json格式,包含如下信息:UID(必需),operation(必需),stack(必需),class(可选) ,method(可选),proto(可选),source(可选),output(可选)。UID
的值是用来选择由被分析的应用程序产生的信息。如果用户停止分析,StaDynA将保存其值并停止该应用程序。
analyzeStadynaMsg
函数(第18行)分析StaDynA选择的由客户端获取的信息。它提取operation变量
的值,并基于这个值选择合适的操作分析这条信息。
反射信息分析流程大都一样,所以伪码以reflection invoke 为例。对reflection invoke
信息的分析如Algorithm 2 所示。第2-4行提取method、class、proto,这些都是经反射调用的。第5行获取stack。第7行搜索stack中发生的第一个reflection invoke 。该方法的下一个stack被赋值给invSrcFrStack
(第9行)。接下来的循环,StaDynA拿这个方法和由该应用程序运行得到的MOI列表进行比较(第10-20行)。如果这个方法在MOI中,那么就将它加到MCG。(第15行),并将它从未发现的调用(uncoveredinvoke
)MOI列表中删除。不然就将它加到不清楚的函数(addVagueInvoke
)列表中(第21行)。这个信息稍后将用来分析为什么在静态分析时没能找到这个方法调用了反射。
对于DCL信息的处理有些许不同(见Algorithm 3)。对于从客户端拿到的信息,服务器端提取包含动态代码加载文件的源路径(第2行)。利用这个信息,StaDynA将文件下载到本地(第4行)并进行处理(第5行)。处理过程包括计算文件hash,复制该文件到result文件夹并重命名,文件名称中包含了计算得到的hash。文件hash可以帮助我们检查该文件是否被下载过,并避免分析已经分析过的代码。如果文件没有被分析过,就对下载的代码分析代码的MOI(第15行)。getDLPathFrStack
函数(第6行)搜索DCL调用和该app运行后得到的stack中MOI。如果找到,就将它们从为发现的DCL(uncovered DCL)中删除(第11行)。没有找到就将关于动态类加载的信息加入到vague call(第19行)。
注意,这里提供的算法是真实算法的简化版本。比方说,在真实应用程序中,有可能同一个MOI会像代理那样被用于调用不同的目标(也就是,同一个方法可以用来加载不同的代码文件)。StaDynA的具体算法可以解决这些问题。
客户端可以在真机或虚拟机上运行。用虚拟机会方便一点,因为这样就可以在同一个机子上运行客户端和服务器端。唯一的缺点就是现在的Android 虚拟机太慢了。还有,有些应用程序会检测它们是否是在一个虚拟环境中运行。考虑到这些情况,我们用真机进行实验。因为代码并不依赖于设备,所以可以很容易把代码放到虚拟机或其他设备上。
为了获取反射和DCL分析所需要的信息,我们修改了DVM
和libcore
。 为了获取DCL相关信息,我们hook了 DexFile.java
的openDexFile
方法。当打开某一个代码文件时需要调用这个方法。它有3个参数,我们感兴趣的是sourceName
。增加的代码会形成一个包含路径的JSON信息,代码就是从这个文件中加载的(sourceName
)。除了这个信息,还有stack trace数据和UID,之后由Android的main日志文件打印出来。
为了获取方法反射调用信息,我们hook了Method.java
的invoke
函数 。每一个Method对象都有declaringClass
,name
和parameterType
成员变量,分别表示了调用的方法的类名,方法名和原型。这些信息和stack strace 一起加到StaDynA信息中。同样的,为了记录经反射创建的类,我们hook了Class.java
和Constructor.java
的newInstance
方法。
每一条StaDynaA信息都包含stack track 信息。Stack track是当前线程的一系列方法调用。stack stace信息常用于寻找程序异常的源头。在我们这里,stack trace信息用于检测调用了反射或DCL方法的MOI。基本上,一个stack trace就是一个stack trace元素数组。每一个stack trace元素包含的信息包括类名,方法名,方法调用在源码中的行数。但是,仅用这个信息是不能唯一确定MOI的,因为我们没有应用程序的源码。还有,在一个类中可以用同样的名字不同的方法来加载函数。为解决这个问题,我们改写了StackTraceElement.java
让它可以保存函数原型信息。方法名和其原型可助我们唯一确定类中的方法。
一个StaDynA信息包括一个header和一个body。为将StaDynA信息和其他日志信息区分开来,我们在其头部增加了一个特殊的标记。header的第二部分是编号。目前,Android 日志文件的大小受限于LOGGER_ENTRY_MAX_PAYLOAD
常数。为解决这个问题,我们在客户端添加了一个函数,让它可以将一条信息分成几个部分。信息的合成在服务器端。
这一部分是我们的系统在测试集上的应用以及对实验结果分析。为了评价StaDynA,我们用真实的应用程序测试它,包括正常的和恶意的。服务器端运行在2.5GHz Intel i5处理器和4GB DDR3内存的机器上。客户端是搭载修改过的4.1.2 r2 Android操作系统的Google Nexus S,用USB数据线连接服务器端。
测试集包括5个正常应用程序和5个恶意程序。正常应用程序的选择是基于它们的流行度和代码中的MOI数。恶意样本的选择基于第二章研究发现的恶意行为中有DCL的家族。我们还基于杀软公司[24,37]的报告往测试集里添加了两个恶意样本(FakeNotify.B
和 SMSSend
)。
为评估StaDynA,对被选择的app都进行手工操作,以便触发MOI的执行。我们也用自动测试工具monkey[11]来自动触发。monkey形成伪随机的用户事件流并在设备上执行它们。但是,因为其随机性,monkey对我们的实验没有多大效果,因为StaDynA需要触发那些有反射和DCL调用的方法。为便于手工分析,我们扩展了一个功能用于报告哪些MOI没有被触发过。观察这个列表,分析人员就可以预测哪一种操作会触发没有执行过MOI。在这里我们展示的是我们手工触发得到的分析结果。
Table 3 展示了每一个操作检测到的MOI数量(“Refl. Invoke”, “Refl. NewInstance”和 “DCL”) 。每一种操作下面有三个子操作,表示初始应用得到的MOI数(“Init.”),经分析得到的MOI数(“Final”),还有我们在分析的时候触发的(“Triggered”)。因为StaDynA还分析由MOI动态加载的代码,所以“Final”栏的数量通常会比“Init”的高。“Triggered”和“Final”的比例可以看作是StaDynA在每一个操作上的覆盖率。也就是说,100%意味着,所有的MOI都被触发了一次。
MOI触发的结果就是MCG的增长。Table 4 表示了StaDynA分析后MCG的增长效果。MCG的增长取决于两个因素:a). StaDynA能够分析动态加载的代码,并将这些信息加入到最终的MCG中的。b). StaDynA能够分解反射调用的目标(在初始图中是做不到的)。第一个子栏(“Init.”)表现了初始MCG中的点数,边数和权限节点数,第二个子栏(“Final”)表示经过StaDynA分析后这些参数的数量。
Table 4中的“Perm. Nodes”是需要危险权限的API 方法数量。Table 5 是在StaDynA下找到的危险权限节点。 “Permissions”栏列出了要运行动态加载的代码需要的危险权限的名称。“New”栏中的叉(×)表示这个API在app开始执行时并没有发现。同时,这个权限在运行由动态加载功能加载的代码中是必需的。这些应用程序(“New”栏中有叉的)会在[13,14,25]中判为申请过多权限,但事实上它们并不属此类(因为它们用这些权限来执行动态代码)。
在正常应用程序上的结果
ImageView
并没有动态代码加载功能,因此它的MCG没有被StaDynA扩展多少。流行游戏FlappyBird
有1个DCL,成功被StaDynA找到,还找到了几个Reflection Invoke和Reflection NewInstance 。但是StaDynA对MCG的扩展还是相对较小(22 个新的点和17条新的边)。更复杂的应用程序,像手机杀软Norton
和Avast
以及通信工具Viber
, 它们的MCG就得到了很大的扩展:对于每一个app,StaDynA找到了超过1000个新的点和边。
Norton AV
,Avast AV
和Viber
也有可疑行为:它们动态加载的代码调用了由权限保护的危险Android API。注意,Norton AV
中新加的一个API调用在初始MCG中是没有的,因此Norton AVA
被[13,14,25]中的工具判为申请权限过度(申请的权限超过其实际用到的)。
在恶意软件上的结果
FakeNotify.B
和SMSSend
没有DCL调用,所以它们被StaDynA添加的MCG新元素都是反射调用的结果。对于这些应用程序,MCG扩展得相对小(但也确实找到隐藏的可疑功能)。比较让人感兴趣的是StyDynA对AnserverBot
,Basebrige4
和DroidKungFu43
的扩展,扩展的部分和初始静态产生的数目相当。实际上,DroidKungFu43
在动态加载后代码量爆炸式增长(MCG大小增长的重要原因)。这个样本加载的文件settings.apk
包含的点和边是原应用程序的近13倍。
另外两个由DCL行为的恶意样本来自AnserverBot
和BaseBridge
家族。这两个样本都包含不止一个DCL。这些样本都下载两个名为moduleconfig.jar
和bootablemodule.jar
的文件。前者没有MOI,但是后者包含reflection invoke和DCL调用。bootablemodule.jar
之后会下载另一个文件,为mainmodule.jar
。这个样本表现了StaDynA如何扩展被隐藏起来的调用。
相比于正常软件,所有用于测试的恶意软件都由可疑行为。这是一个很有意思的结果,如它所证明的那样,高级的恶意软件确实会隐藏其逻辑,并仅在执行时暴露。例如,SMSSend
在StaDynA分析前,没有任何需要危险权限的节点,而在StaDynA分析后找到了4个这样的节点(新节点需要READ_PHONE_STATE
和SEND_SMS权限
)。
我们的分析结果证明,恶意软件更容易申请过度权限(为动态加载代码,它们需要申请更多的权限),所以将一个申请权限过度的app判为可疑是由根据的。但是,因为正常软件也会申请权限孤独,所以需要对一个不知其是正常或恶意的软件进行进一步的研究,而StaDynA在这些过程中就很有用。
我们的工具还有提升的空间。对于StaDynA来说,MOI的覆盖率(得到执行的MOI数与发现的MOI总数之比)尤其重要。目前,我们使用手工的方法触发MOI。因为我们是手工触发这些方法的,StaDynA并不能完全覆盖所有的MOI,因为手工触发大多是基于GUI的(它是分析人员生成大范围可以触发所有MOI的系统事件过程中的最大挑战)。改进StaDynA的一种方法是自动触发。在这个方向上的第一步是看看是否有像monkey[11]这样的工具。但是在实验过程中,我们发现这种伪随机事件生成工具覆盖到MOI的数量并不是很让人满意。一种可能的改进方法是使用像SmartDroid[48]这样的工具。SmartDroid可以触发指定敏感API方法。就StaDynA来说,敏感API方法就是反射和DCL调用。其他或许能促进自动测试的工具是[15,39,45]。
另一个减少人工的方向是解析静态反射的目标,起码能解析那些用字符串常量表示的[31]。文献[25]的分析显示在59%使用了反射的应用程序中是可以做到自动解析反射调用的目标的。但是,它们的实验是假设一个“封闭的世界”,这是不现实的,因为在现在的很多应用程序中,DCL的使用非常广泛。还有,我们发现相比于2011年,现在反射的使用量加大了(在我们的研究中是88%,而文献[25]的是61%)。
通常,动态分析每次只能执行一条路径。但是,动态记录可能会因为执行内容的不同而不同,例如,有些方法的参数可能会影响反射调用的目标。因此,StaDynA的另一个改进方向是整合多次分析得到的信息。
StaDynA还有其他的缺点。它是根据UID分析应用程序的。但是,有可能好几个app使用同一个UID。在这种情况下,StaDynA也会收集其他app产生的信息。同时,这些信息并不是用于扩展MCG,但是会加入到vague call目录下,以便后续手工分析。
Android因其开放性和开发者发布app到Google Play与第三方市场的便利性,成为最流行的移动操作系统。但是其开放性是以大量恶意软件污染其生态系统为代价的。解决移动应用程序安全和隐私问题的一个方法是,加大平台对恶意软件的检测力度或实施合理的安全策略[20,47]。在这个方法上的解决方案,大多需要修改系统镜像。
另一种方法,和StaDynA相关的,考虑的是对移动应用程序代码的分析。针对Android已经有很多静态和动态分析工具。文献[23]中的系统将Dalvik字节码转换为Java class文件,然后就可以使用很多用java开发的工具来分析。文献[23]中的FortifySCA是用于检测漏洞和危险功能,如设备IMEI泄露。DroidAlarm[49]通过在进程间调用图中根据敏感权限构造到可以通往其他app的公共接口之间的路径静态检测权限提升漏洞。StaDynA可以通过补全过程间调用图帮到这些静态分析工具。
Hu et al. 提出利用函数调用图(function call graph,FCG)并基于图相似度,利用已知的恶意软件图模式检测恶意软件[33]。Gascon et al. 在这个方向上继续研究Android恶意软件,通过比较AndroGuard[27]生成的FCG检测恶意软件。StaDynA可以帮这些分析工具提供更为详细的图。
TaintDroid是第一代为Android app进行动态分析的工具中的一款[22]。在TaintDroid基础设施齐全的手机的软件栈上追踪信息的传播。敏感信息源(source)通常是设备传感器或用户隐私信息,陷入点(sink)是网络接口,所以TaintDroid的主要功能是检测隐私泄露[45]。DroidScope模拟app运行,并的Android 软件栈的各层:在原生代码层、在Dalvik字节码层,在系统api层,结合原生代码和dalvik,上追踪其内容。在DroidScope上运行app的时候,安全分析人员可以通过追踪不同层次的事件和函数调用的参数去寻找恶意行为。
动态分析工具很难自动化,因为需要模拟大量的系统和用户的互动行为(UI互动)。为自动触发UI事件,前人提出了好几种方法,从随机事件生成[32]到更高级一点的像AppsPlayground[39]和SmartDroid[48]。但是它们在产生的事件类型和覆盖率方面还有很多缺陷。
最近,Poeplau et al. [38] 发现了Android app中动态代码加载问题。作者选择动态代码加载中可能引发的漏洞特征并开发一个可以分析这些已知特征的工具。而且,它们还提出使用白名单阻止动态加载那些会有危险行为的代码。白名单在运行时阻拦未授权代码。要获取授权,代码要么已经签名[46],要么它的签名已在可信授权机构中。但是,就如文献[38]所提到的,提取危险行为本身就很难,尤其是对经过反射调用的API。但是,StaDynA目的并非要阻止动态加载(因为很多合法app也会用到,而且提取的复杂性也不会被开发者接受),而是对它进行分析。
Java的反射和动态类加载
静态分析工具分析动态类加载、反射和原生代码时遇到的困难,之前在Java上已有研究。比如说,和我们的方法一样,在[30]中,针对全JAVA语言的指针分析工具(基于程序调用图),通过“线上”分析可确定动态类加载和反射,当通过程序执行,动态类加载和反射动态构造出调用图时,通过分别修改对应的指针分析约束,可以实时处理native代码。
在[18]中对Java的运行时形状分析(shape analysis)进行了研究。一般来说,形状分析操作是基于程序的调用图,可推导出堆对象是如何与其他对象联系的(例如,某个变量是否可以被几个线程访问)。但是在Java中,程序产生的调用图有可能不完整,文献[18]提出了如何在调用图动态进化时进行可增长的形状分析。我们的方案没有引入形状分析,但是背后的思路和[18]的是相似的。
Livshits, Whaley 和Lam 研究了Java中的反射分析[36]。他们提出改进静态分析算法以推测反射调用近似目标的更精确信息,以及为了找到反射目标,查找需要用户提供精确信息的程序入口点。
与StaDynA相关的是TamiFlex[17],它扩充了对有反射和自定义类加载器的Java程序的静态分析。利用加载时Java instrumentation API,TamiFlex修改原有程序,记录类加载和反射调用事件。这个信息将帮助另一个工具对动态分析阶段获取的信息进行静态分析。它与StaDynA有几点不同:第一,TamiFlex使用的特定Java API,在Android中没有。第二,尽管Android允许在加载app之前先将其instrumentation(离线instrumentation),但是有些Android app会在检查应用程序签名。所以,对于那些程序,TamiFlex的方法在Android上不管用。第三,TamiFlex需要看到某些调试信息(函数调用所在的代码行)。在Android中,此类信息可能会被检测出来。因此TamiFlex的方法行不通,但是StaDynA就可以正确处理此类问题,因为我们修改了Dalvik VM。
在现代移动应用程序中,动态加载技术得到了广泛使用,在Android中它们就是反射和动态类加载。来源于Java,这些技术在Android中还遇到另一个威胁,因为被加载的代码和加载它的程序拥有同样的权限。恶意程序可以利用这些功能隐藏恶意行为,不被分析工具发现。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课