Android系统服务Fuzz测试
两年前写的东西,现在发一下。写了个最简单的fuzz脚本:基于adb的fuzzer,仅供学习,请勿作不良用途。
一、背景知识
二、Fuzz原理
Android为用户提供的很多功能是通过系统服务实现的。截至Android 6.0,系统服务数量已经达到60多个。系统服务的底层是system_server(以下简称ss),我们知道,Android存在watchdog程序,当系统服务长时间不响应时,watchdog会杀死ss进程,从而导致Android系统的软重启。Android提供的系统服务底层是通过binder进程间通信机制实现的,binder的数据传递过程这不在详解。从Java层传递的数据,最终会在C/C++代码中被使用。不管是java还是c/c++,如果传递了一个null指针引用。将会导致程序的崩溃,Android系统因此也受此影响。本Fuzz测试旨在发现Android系统服务的代码实现中,由于没有null指针做好处理导致的系统奔溃。
Android对外提供的系统服务,根据提供的功能,实现了不同的函数,以供开发者调用。例如,开发者在上层调用发送短信的API,其底层同样是通过binder调用了短信的系统服务。这些可以通过binder调用的方法一般定义在.aidl文件,一些系统服务定义了多达数百个可调用的方法。这样,可通过binder调用测试的方法多大几千个。所以,通过Fuzz测试这些方法不失为一种好的测试手段。
在Android Binder机制实现进程间数据交换(不使用aidl实现)中,已经说明了如何利用binder调用系统服务实现发送短信的功能。除了编写本地App通过binder来调用系统服务之外。其实,Android已经给我们留下了“测试后门”。在shell中,通过service指令可以直接对系统服务进行测试,并支持对所有aidl文件中定义的方法的测试。
其中,service call SERVICE CODE 就是对aidl文件中定义的方法的测试。其中,SERVICE就是对应的service名,code就是在aidl文件中定义的方法,其数值根据定义的方法递增,从1开始。例如,在IAccessibilityManager.aidl文件中,定义了以下方法。
三、测试框架
了解了以下原理之后,就可以编写Fuzz工具进行测试了。这里工具实现方式有两种,一是编写脚本调用的shell提供的service指令测试,而是编写本地App,通过调用binder来对系统服务进行测试。我们采用第二种,更加有利于我们学习Android系统服务及binder通信机制的相关知识。这里不给出具体的代码,后面会上传的github,只给出一些关键的地方。
获取所有的系统服务。
/** * 获取系统中所有的服务名 * @return 服务名字符串数组 */ public String[] ListService() { String SM[]={}; try{ SM=(String [])Class.forName("android.os.ServiceManager").getMethod("listServices").invoke(null); // Log.d(tag,"I find there are "+SM.length+" System Services"); for(int i=0;i< SM.length;i++){ // Log.d(tag,SM[i]); } }catch(Exception e){ e.printStackTrace(); Log.e(tag, e.toString()); } return SM; }
通过反射获取IBinder接口对象。
/** * 使用反射机制获取服务的IBinder接口 * @param sername 服务名 * @return 返回服务的IBinder接口 * @throws Exception */ private static IBinder getIBinder(String sername) throws Exception{ Class smcls = Class.forName("android.os.ServiceManager"); Method mth = smcls.getMethod("getService", String.class); return (IBinder) mth.invoke(null, sername); }
通过binder获取接口名。
/** * 获取接口名 * @param serHandle 服务的IBinder接口 * @return 返回接口名字符串 * @throws RemoteException */ private static String getInterfaceName(IBinder serHandle) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); serHandle.transact(INTERFACE_TRANSACTION, data, reply, 0); String interfacename = reply.readString(); data.recycle(); reply.recycle(); return interfacename; }
通过binder调用系统服务函数,进行测试。
void testNUll(String sername, int code) { for (int i = 0; i < testcaseint.length; i++) for (int j = 0; j < testcaseint.length; j++) { try { Log.d(tag, "-" + sername + "-" + code + "-arg1-" + testcaseint[i] + "-arg2-" + testcaseint[j]); Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); IBinder ib = getIBinder(sername); String in = getInterfaceName(ib); data.writeInterfaceToken(in); data.writeInt(testcaseint[i]); data.writeInt(testcaseint[j]); // data.writeInt(0); // data.writeInt(3); ib.transact(code, data, reply, 0); //可以读出reply中的数据 // Log.d(tag, "-" + sername + "-" + code + "-" + "reply is \n" + reply.readString()); reply.readException(); data.recycle(); reply.recycle(); } catch (Exception e) { } } }
单次调用binder测试系统服务中定义的方法流程如下。
Created with Raphaël 2.1.2 通过反射获取binder对象 填充除code外被写入parcel的参数 调用transact()函数完成对aidl中定义方法的调用 监控系统异常整个Fuzz工具的架构如下:
还有一个问题,要实现整个Fuzz的自动化,还需要自动化监控系统重启事件。
四、DoS的自动化检测
Android系统开机完成之后,会发出一个BOOT_COMPLETED 系统广播 ,所以通过检测此广播可以监控系统是否发生了重启,从而确定漏洞位置,即哪一个系统服务对应的哪一个方法触发。除此之外,BOOT_COMPLETED 广播是由ActivityManagerService的finishBooting方法发出的,所有可以通过Hook此API完成监测。鉴于Xposed的Hook框架可以很容易的帮助我们做到这点。
更多相关文章
- Android(安卓)简单实现倒计时功能
- 一篇文章看明白 Android(安卓)图形系统 Surface 与 SurfaceFling
- webview的白屏,和硬件加速
- Android-工作遭遇-URLConnection原生请求http和https忽略证书
- Android(安卓)-- AppWidget 高级篇
- 初识android 自定义控件
- 用Fragments在Android中进行多窗格开发
- 28、compileSdkVersion,minSdkVersion,targetSdkVersion 的区别和
- Android(安卓)Handler那些事儿(二)——几个关键类之间的关系