随着移动互联网的飞速发展,智能手机承载着越来越多的服务功能。Android系统由于其开源的原因,市场占有率稳居第一,因此成为了恶意应用程序重要的滋生平台[1]。
针对Android恶意应用程序的检测主要分为静态检测和动态检测。静态检测是指在不运行应用程序的情况下,通过扫描反编译的应用程序代码,发现其恶意行为。Felt等[2]从应用程序中提取出相关申请权限,通过分析应用程序的权限判断应用程序是否为恶意应用程序。CHEX[3]首先通过识别应用程序中可能被调用的接口,然后对这些接口进行数据流分析,判断接口是否有可能被恶意应用程序利用以达到提权的目的。静态检测方法的优点是可以快速地判断应用程序是否存在恶意行为,但无法应对代码的混淆、加密、动态加载等情况。
动态检测是在应用程序执行的过程中实时监控应用程序的行为,一旦发现恶意行为,立刻提示终端用户。TaintDroid[4]对Android系统应用程序框架层进行修改,在应用程序框架层敏感API的位置插入相应的监控代码,起到实时监控的目的。但这种方法不适合应用于实际使用,因为对相关的API进行修改后需要重新编译。Aurasium[5]对应用程序的代码进行修改,在敏感API调用处添加监控代码,从而监控应用程序的执行,虽然该方法无需对系统进行修改重新编译,但无法监控应用程序的动态加载,需要对应用程序进行重新打包签名,有可能影响应用程序的正常执行。
无论采取哪种检测方式,都必须要面对应用程序第三方库。而目前绝大多数的检测方案集中在Java层,导致恶意应用程序的开发者通过第三方库实现其恶意行为,达到绕过Java层检测的目的,甚至有可能破坏Java层的安全检测系统[6]。所以,对于Android检测系统的研究,应用程序第三方库的行为检测应该得到相应的重视。
本文在充分研究Android系统Binder机制及其源码后,提出了全新的Android系统动态监测方法。首先,检测应用程序的第三方库中是否存在敏感函数破坏监测系统,其次,将应用程序安装到动态链接文件libbinder.so中的ioctl函数被挂钩子的系统中,监测来自Java层和Native层对系统服务的调用,实现动态监测Android应用程序。
1 Android系统分析 1.1 Android系统架构Android系统架构如图 1所示,分为应用层、应用程序框架层、本地库和Android运行时、Linux内核,相关的定义如下[7]:
应用层 Android平台运行的应用程序,如微信、日历、地图等,这些应用程序通常使用Java语言编写,可以使用Java与本地代码接口(Java Native Interface, JNI)机制与本地已经编译的代码进行交互。
应用程序框架层 为应用程序的开发者提供访问核心功能的API框架,该层的应用简化代码的编写,提高了程序的复用性。
本地库和Android运行时刻 Android系统使用了大量的C/C++函数库,这些本地库通过应用程序框架层对应用程序开发者开放。Android运行时包含核心库和Dalvik虚拟机两部分,核心库中提供了Java语言核心库中包含的大部分功能和利用JNI封装的C/C++函数库,Dalvik虚拟机是专门针对Android平台解释运行Dalvik字节码的虚拟机。
Linux内核 Android是基于Linux内核开发的,依赖于Linux的系统服务,如内存管理,进程管理、安全机制等。Linux内核为软件提供了与硬件的交互。
1.2 Android的安全机制Android应用程序是作为Linux进程在系统中运行,进程间相互独立不能互相访问资源,即使本地代码也不能进程间互相访问,只有使用相同签名的应用程序才可以建立信任关系、实现数据共享。Android系统服务也是作为Linux进程在系统中运行,如果应用程序需要跨进程访问系统服务,在没有root权限的前提下,必须获得相应的权限。Android系统的权限主要分为2类:①对设备的直接访问,Android系统直接使用Linux内核提供的用户权限机制,采用Group IDs (GID)的形式管理该类型的权限,Android系统会在platform.xml文件中记录该应用程序GID与权限字符串的对应关系;②对设备或者服务的间接访问权限,Android系统采取Binder机制为应用程序提供服务,当应用程序向提供某服务的服务端发出请求时,该服务端会检查该应用程序是否被授予该权限,如有则提供该服务,反之亦然。
Android系统权限申请采取“all-or-nothing”的策略,所以应用程序可能在用户疏忽甚至是不同意的情况下窃取用户的隐私信息[8]。
1.3 Binder机制Linux内核提供了管道、信号、消息队列、共享内存等进程间通信机制。Android系统是基于Linux内核开发的,而Android系统却开发了一套新的进程间通信机制Binder。Binder进程间通信机制是在OpenBinder的基础上实现的,采用CS通信方式,其中,提供服务的进程成为Server进程,访问服务的进程称为Client进程。Server进程和Client进程的通信都要依靠运行在内核空间的Binder驱动程序来进行,Binder驱动程序向用户控件提供一个设备文件/dev/binder,使得应用程序进程可以间接地通过它来建立通信通道。Service组件在启动时,会将自己注册到一个Service Manager组件中,为了让Client组件可以通过Service Manager组件找到该服务。Binder驱动程序、Service、client、Service-Manager的关系如图 2所示。Binder进程间通信机制的代码是由C/C++实现的,Android系统在应用程序框架层中提供了Binder进程间通信机制的Java接口,Java层通过使用JNI的方法来调用Binder库的C/C++接口[9]。
1.4 动态链接加载机制动态链接是把程序按照模块拆分成各个相对独立的组成部分,在应用程序运行时才将各部分链接在一起,形成一个完整的应用程序。程序在运行时会检查所依赖的目标文件是否在内存中,如果不在,则将目标文件拷贝至内存,随后进行链接工作,包括对符号的解析和地址重定位,所以动态链接可以很好地解决空间浪费和应用程序更新的问题[10]。动态链接文件被称为动态共享对象(Dynamic Shared Objects,DSO),简称共享对象,以".so"作为文件的扩展名。
Android系统中,Bionic是Android系统C/ C++库,Bionic库实现了对系统调用的封装,库中的API与系统调用相对应,其中有部分API需要多次调用系统调用实现API功能。Bionic库的动态链接文件保存在系统lib目录下,文件名叫“libc.so”。整个And-roid系统只保留一份“libc.so”,当应用程序被装载到系统时,系统的动态链接器会将应用程序所需要的libc.so装载到进程的地址空间,并进行重定位工作。
2 获取系统服务过程的分析如图 2所示,ServiceManager是Android系统的服务管理器,远程服务对象都是以Binder的形式存在,而ServiceManager是远程服务对象的管理者。应用程序在申请获得系统服务时,必须通过Servic-eManager获取服务的索引号,通过索引号,应用程序建立与该系统服务的通讯。
在应用程序获取系统服务的过程中,应用程序需要先打开设备文件/dev/binder,通过IO控制函数ioctl与Binder驱动程序进行交互,因此Binder驱动文件提供了一系列的IO控制命令来和应用程序进程通信。在这些IO命令中,最重要的是BINDER_WRITE_READ命令,命令参数binder_write_read,它是用来描述进程间通信过程中所传输的数据,包括输入、输出数据,它的结构体如Code 1所示,其中write_buffer和read_buffer都是数组,数组中的每一个元素都是由通信协议代码和通信数据组成。协议代码在write_buffer中被称为命令协议代码,在read_buffer中被称为返回协议代码。在命令和返回的协议代码中,最重要的协议代码是BC_TRANS-ACTION和BC_REPLY。
在命令协议代码中,当一个进程请求另一个进程执行某一操作时,源进程就使用命令协议代码BC_TRANSACTION来请求Binder驱动程序将通信数据传递到目标进程,当目标进程处理完成源进程所请求的操作之后,就使用命令协议代码BC_REPLY来请求Binder驱动程序将结果数据传递给源进程。Code 1
struct binder_write_read {
signed long write_size; /* bytes to write */
signed long write_consumed; /* bytes consumed by driver */
unsigned long write_buffer;
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer;
}
在返回协议代码中,当一个Client进程向一个Server进程发出进程间通信请求时,Binder驱动程序会使用返回协议代码BC_TRANSACTION通知该Server进程来处理该进程间通信请求,当Server进程处理完该Client进程通信请求后,Binder驱动程序会使用返回协议代码BC_REPLY将进程间通信请求结果数据返回给Client进程[11]。
3 本文动态监测系统本文的监测系统主要分为2部分,第一部分主要是检查应用程序的第三方库中是否存在敏感函数,第二部分是将通过第一部分检测的应用程序载入系统调用ioctl被hook的系统中,进行动态监测。
3.1 针对第三方库的检测用户对Android应用程序的使用体验要求越来越高,应用程序的开发者仅仅使用Android SDK编写应用程序已经不能满足用户的需求,比如3D游戏的呈现,涉及到CPU的高性能运算等。所以,应用程序的开发者越来越多地使用本地语言开发工具(native development kit,NDK)来编写应用程序native部分的代码和移植C/C++语言开发的程序来节省开发的时间和成本。
第三方库应用的普及也带来了潜在的安全风险:①应用程序一旦被授予申请的权限,那么应用程序无论Java层还是native层都获得了相同的权限,即意味着通过native代码可以通过JNI机制调用应用程序框架层API获得系统资源,甚至可以直接调用系统服务的C/C++接口。flexdroid[12]指出在含有第三方库的应用程序中,50%的第三方库与广告有关,其中不少第三方库在执行过程中设法获取用户的隐私信息。②应用程序Java层和Native层共处于一个独立的虚拟空间内,所以恶意应用程序的第三方库可以通过操纵native代码修改进程内虚拟内存的属性,写入相应的数据,执行恶意的行为。
Google[13]关于NDK的官方说明指出native代码应该仅仅被用于执行CPU-intensive的操作,所以如果应用程序的开发者通过native代码去获取系统资源,甚至修改内存数据,显然都是违背Android系统NDK的初衷。许多研究者对应用程序第三方库进行了深入的研究,Appcage[14]采取对应用程序第三方库进行二次编写的方法,将Java层的代码和Native层的代码分开,分别在各自的沙箱中运行。NativeGuard[6]采取Android接口定义语言(android interface definition language, AIDL)的方法将应用程序的第三方库运行在另一个进程,为了第三方库获得的权限从应用程序的权限中脱离,使得第三方库只能执行CPU-intensive的操作。上述的研究成果都可以有效地限制应用程序第三方库的行为,但在时间开销、空间开销和有效性方面还有欠缺。
同时,在文件映射的过程中,系统调用mmap指定不同内存区域拥有不同的属性,但应用程序仍然可以通过native代码执行系统调用mprotect修改内存区域的读、写、执行属性[10]。例如,代码区的默认属性是只读,意味着任何代码都不能修改该区域的数据,但是恶意应用程序的native代码可以通过执行系统调用mprotect来改变代码区的属性由只读变为可读写,对代码进行修改,执行恶意行为。本文监测系统的目的是能够实时监测到来自Java层和native层对系统服务的调用,所以本文的监测系统并没有限制第三方库的权限,但是禁止应用程序的native代码执行系统调用mprotect,如第三方库中存在mprotect函数,就直接将其判定为恶意应用程序,不纳入动态监测的范围。
3.2 hook对象的选取hook是一种消息处理机制,也是处理消息的程序段,通过系统调用将此程序段载入系统,每当有特定消息发出时,在没有到达目标地址前,hook程序就已经得到该消息,获得了程序的控制权,可以对消息进行相应处理[14]。
Binder进程间通信机制是由C/C++代码实现的,Android系统在应用程序框架层中提供Binder进程间通信机制的Java接口,所以应用程序Java层的代码可以通过JNI机制来调用Binder的C/C++接口,实现进程间的通讯。部分恶意应用程序直接通过与Binder的C/C++接口通讯来获取系统服务,绕过Java层对应用程序框架层敏感API检测。无论是来自Java层还是Native层对系统服务的调用,归根结底是需要调用ioctl函数,所以本文选取了“libbinder.so”中的ioctl函数进行hook,既可以实时监测到通过应用程序架构层API获取系统服务的行为,也可以监测到通过native代码获取系统服务的行为[15]。
无论在命令协议代码还是在返回协议代码中,BC_TRANSACTION和BC_REPLY的通讯数据是使用binder_transaction_data来描述,结构体如Code 2所示,成员变量sender_pid和sender_euid表示发起进程间通信请求进程的PID和UID,目标进程通过这2个成员变量就可以识别出client进程是否有权限访问。
Code 2
struct binder_transaction_data{
union{
size_t handle;
void *ptr
}target;
void *cookie;
unsigned int code;
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size;
union{
struct{
const void *buffer;
const void *offsets;
}ptr;
uint8_t buf[8];
}data; };
而上述的命令都需要使用IO控制函数ioctl,所以本文将所需要实现的代码注入libbinder.so中,新建一个ioctl来替换旧的ioctl,当执行完注入代码后,再将指针交还给原ioctl,伪代码如Code 3所示。
Code 3
int new_icotl (int_fd, unsigned long int_request, void *arg)
{
if (_request==BINDER_WRITE_READ)
{
根据命令获取传输方向、类型、类型命令、传输数据大小
if (write_size>0)
{将write_buffer中的数据写入到Binder}
while (already_got_size < write_size)
{循环处理buffer中的每一个命令}
if (read_size>0)
{从Binder中读取数据写入到read_buffer}
while (already_got_size < read_size)
{循环处理buffer中的每一个命令, 获取PID和申请的service的信息}
int res=(*old_ioctl) (_fd, _request, arg);
return res;
}
3.3 IPC数据的解析ServiceManager是Android系统的服务管理器,远程服务对象都是以Binder的形式存在,而它们的共同管理者是ServiceManager。应用程序在申请获得系统服务时,必须通过ServiceManager获取服务的索引号[16]。郑勇鑫[17]通过对SerciceManager中涉及敏感权限的系统服务进行挂钩子,得到应用程序获取涉及敏感权限的系统服务信息。这种方法能监控到应用程序获取被监测系统挂钩子的系统服务,但不能获知应用程序调用的所有系统服务。
结构体binder_transaction_data的ptr成员变量buffer指向一个数据缓冲区,这个缓冲区是用来保存通信数据的,它的大小由成员变量data_size决定。本文通过对ioctl函数挂钩子,将binder_transaction_data的ptr成员变量所指的data_size大小的数据缓冲区中的内容转化为16进制表达,系统服务的名称是由大小写字母和点号组成,通过增加判断语句if (((*data >=65) & & (*data < =90))‖((*data >=97) & & (*data < =122))‖(*data==46))可以获知应用程序意图获取的所有系统服务名称。
4 实验与评估 4.1 实验环境与实验样本本文所有实验在内存为8G,处理器为Intel (R) Core (TM) i5-3337U 1.80GHzPC控制器和Android OS 4.3, 内存1G,处理器1.2GHz的酷派8720L型号手机终端完成。
4.2 实验组成1) 本文从Google Play应用市场下载了下载排名前10的应用程序,动态监测应用程序的运行,检查是否能够监测到与应用程序申请权限相关的系统服务。
2) 本文自编native层系统服务,使用应用程序第三方库利用native代码获取系统服务,用来检测hook的ioctl函数是否能够监测到应用程序直接与Binder驱动通讯获取系统服务的行为。
3) 通过对比hook前后应用程序启动、系统服务执行的时间,证明对ioctl函数挂钩子对时间开销的增加微乎其微。
4.3 实验评估本文首先对待检测应用程序中存在的第三方库,使用Android NDK中readelf命令进行反汇编,在反汇编出的文件中存在动态链接重定位表,分别是“rel.dyn”和“rel.plt”, 其中“rel.dyn”是对数据引用的地址重定位,所修正的位置位于.got段以及数据段;“rel.plt”是对函数引用的地址重定位,所修正的位置位于“.got.plt”段。如果在“.got.plt”段中发现第三方库中存在调用系统函数mprotect时,就判定该应用程序存在恶意行为的可能,不将其列入动态监测的范围。
4.3.1 可用性验证本文的检测系统是为了动态地监测应用程序对Android系统服务的调用,而调用这些系统服务需要向Android系统申请相应的权限。
Monkey工具是利用socket通讯的方式模拟用户的按键输入、触摸屏输入、手势输入等。本文监测系统利用Monkey工具对从Google Play应用市场下载的10个应用程序进行自动化的随机测试,能够实时监测到与应用程序申请权限相关的系统服务调用。以Wifi万能钥匙和百度糯米为例,从表 1可以看出,本文监测系统能够实时监测到应用程序通过Binder机制调用系统服务的情况,但无法监测到获取访问设备权限后直接访问设备的情况,因为直接访问设备是不需要通过Binder机制的,同时直接访问硬件设备的监控可以通过对系统Java层敏感API的监测实现,这部分不是本文的重点。
应用程序 | 申请的权限 | 是否监测到与申请权限相关的行为 |
Wifi万能钥匙 | android.permission.INTERNET android.permission.ACCESS_FINE_LOCATION android.permission.SEND_SMS android.permission.READ_EXTERNAL_STORAGE |
YYYN |
百度糯米 | android.permission.CAMERA android.permission.RECORD_AUDIO android.permission.READ_CONTACTS android.permission.READ_PHONE_STATE android.permission.WRITE_EXTERNAL_STORAGE |
YYYYN |
应用程序获得申请的权限后,其第三方库也获取了相应的权限,意味着native代码可以直接通过调用Binder的本地接口获取到受保护的系统服务。虽然目前Google还未公开Android系统系统服务的本地接口,但如果存在本地接口并被恶意应用程序的开发者掌握,那么在Java层对系统服务调用的监测将全部失效。随着Android系统需求越来越高,native service应用也越来越多。
本文在充分研究Binder机制的基础上,编写了native层的系统服务。应用程序通过第三方库去获取本文自定义的系统服务,该第三方库未使用JNI机制从native层去调用应用程序框架层的API获取系统服务,而是直接调用自定义的系统服务本地接口去获取系统服务。同理,如果存在原生系统服务的本地接口,应用程序也可以通过native代码调用系统服务。
在终端运行该应用程序,本文监测系统能够成功地监测到应用程序使用第三方库直接与Binder驱动通讯调用系统服务,说明了应用程序获取相应的权限后,通过native代码直接与Binder驱动本地接口进行交互是可以获得受保护的系统服务。同时,也说明针对Android的监测系统如果将监测点仅仅放在Java层进行监测应用程序获取的系统服务是存在潜在风险的,Java层的检测完全可能被恶意应用程序通过第三方库与Binder驱动本地接口直接交互而绕过。
4.3.3 性能评估Android系统除了从界面上启动程序之外,还可以使用命令行工具启动应用程序。本文使用am start-W package name/ activity name的命令启动应用程序,对比hook前和hook后应用程序启动的时间消耗,以Wifi万能钥匙和百度糯米为例,结果如表 2所示,hook前后启动时间的值是取进行了50次实验的平均值。从表 2可以看出,监测系统ioctl函数hook后并未明显增加应用程序启动时间的开销。
最后,本文自编应用程序,在应用程序中调用应用程序框架层API来获取系统资源,通过在应用程序框架层API代码前后插入计时代码(以getDeviceId为例,如Code 4所示),计算ioctl函数被hook前后获取系统服务所需的时间,结果如表 3所示,hook前后完成系统服务的用时也是取50次实验的平均值。
Code 4
Long start=System.currentTimeMillis ();
TelephonyManager telephonemanage=(TelephonyManager)
getSystemService (Context.TELEPHONY_SERVICE);
System.out.println (“串号:”+telephonemanage.getDeviceId ());
long end=System.currentTimeMillis ();
System.out.println (“运行时间:”+(end-start)+“毫秒”);
从表 3可以看出,hook前后对应用程序跨进程调用系统服务获取系统资源所需时间影响不大。
5 结论本文动态监测系统通过对libbinder.so中ioctl函数挂钩子的方法,能够有效、实时地监控应用程序通过应用程序框架层API或是通过native层直接与Binder驱动本地接口进行交互获取系统服务的行为。同时该方法对应用程序的启动时间、应用框架层API调用的时间开销影响较小。应用程序载入监控系统前第三方库的检测确保了应用程序第三方库无法对本文监测系统构成威胁,具有实际应用的价值。
下一步的研究重点是如何监测应用程序通过第三方库打开设备文件;其次对部分系统服务进行更加细粒度的监测,力争将动态监测系统做的更加完善。
[1] |
许铝才, 张源, 杨珉.
SysTracker:一种采用系统调用监测安卓应用资源使用的方法[J]. 计算机应用与软件, 2014, 31 (10): 244–250.
Xu Lücai, Zhang Yuan, Yang Min. Systracker:A System Call-Based Re-Sourses Usage Monitoring Technique for Android Applications[J]. Computer Applications and Software, 2014, 31(10): 244–250. (in Chinese) |
[2] | Felt A P, Chin E, Hanna S, et a1. Android Permissions Demystified[C]//Proceedingsof the 18th ACM Conference on Computer and Communications Security, New York, ACM, 2011:627-638 |
[3] | Lu L, Li Z, Wu Z, et al. Chex:Statically Vetting Android Apps for Component Hijacking Vulnerabilities[C]//Proceedings of ACM Conference on Computer and Communications Security, New York:ACM, 2012 http://adl.csie.ncu.edu.tw/adlab/ppt/478_Chex.pdf |
[4] | Enck W, Ongtang M, McDaniel. On Lightweight Mobile Phone Application Certication[C]//Proceedings of the 2009 ACM Conterence on Computer and Communications Secutity, 2009:24-41 |
[5] | Xu R, Saïdi H, Anderson R. Aurasium:Practical Policy Enforcement for Android Applications[C]//Proceedings of the 21st USENIX Conference on Security Symposium, 2012:27-27 |
[6] | Sun M, Tan G. NativeGuard:Protecting Android Applications from Third-Party Native Libraries[C]//Proceedings of the 2014 ACM Conference on Security and Privacy in Wireless & Mobile Networks, 2014:165-176 |
[7] |
张玉清, 王凯, 杨欢, 等.
Android安全综述[J]. 计算机研究与发展, 2014, 51 (7): 1385–1396.
Zhang Yuqing, Wang Kai, Yang Huan, et al. Survey of Android OS Security[J]. Journal of Computer Research and Development, 2014, 51(7): 1385–1396. (in Chinese) |
[8] | Yan L K, Yin H. DroidScope:Seamlessly Reconstructing the OS and Dalvik Semantic Views for Dynamic Android Malware Analysis[C]//Proceedings of the 21st USENIX Conference on Security Symposium, 2012:29-29 |
[9] | Enck W, Octeau D, Mcdaniel P, et al. A Study of Android Application Security[C]//Usenix Conference on Security, 2011:1175-1175 |
[10] |
俞甲子, 石凡, 潘爱民.
程序员的自我修养[M]. 北京: 电子工业出版社, 2009.
Yu Jiazi, Shi Fan, Pan Aimin. Programmer's Self-Improvement[M]. , 2009. (in Chinese) |
[11] |
罗升阳.
Android系统源代码情景分析[M]. 北京: 电子工业出版社, 2012.
Luo Shengyang. Android Source Code Scenario Analysis[M]. , 2012. (in Chinese) |
[12] | Seo J, Kim D, Cho D, et al. Flexdroid:Enforcing In-App Privilege Separation in Android[C]//Proceedings of the 2016 Annual Network and Distributed System Security Symposium, CA, 2016 |
[13] | Google. Android ndk[EB/OL]. (2012-09-02)http://developer.android.com/tools/sdk/ndk/index.html |
[14] |
华保健, 周艾亭, 朱洪军.
Android内核钩子的混合检测技术[J]. 计算机应用, 2014, 34 (11): 3336–3343.
Hua Baojian, Zhou Aiting, Zhu Hongjun. Hybrid Detection Technique for Android Kernel Hook[J]. Computer Applications, 2014, 34(11): 3336–3343. (in Chinese) |
[15] | Zhou Y, Patel K, Wu L, et al. Hybrid User-Level Sandboxing of Third-Party Android Apps[C]//Proceedings of the 10th ACM Symposium on Information, Computer and Communication Security, 2015:19-30 |
[16] |
金泰延, 宋享周, 朴知勋, 等.
Android框架揭秘[M]. 北京: 人民邮电出版社, 2012.
Jin Taiyan, Song Xiangzhou, Piao Zhixun, et al. Inside the Android Framework[M]. , 2012. (in Chinese) |
[17] |
郑勇鑫. Android动态监控系统设计与实现[D].南京:东南大学, 2014
Zheng Yongxin. Design and Implementation of Dynamic Monitoring System for Android[D]. Nanjing, Southeast University, 2014(in Chinese) |