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系统服务Fuzz测试_第1张图片

  在Android Binder机制实现进程间数据交换(不使用aidl实现)中,已经说明了如何利用binder调用系统服务实现发送短信的功能。除了编写本地App通过binder来调用系统服务之外。其实,Android已经给我们留下了“测试后门”。在shell中,通过service指令可以直接对系统服务进行测试,并支持对所有aidl文件中定义的方法的测试。

Android系统服务Fuzz测试_第2张图片

  其中,service call SERVICE CODE 就是对aidl文件中定义的方法的测试。其中,SERVICE就是对应的service名,code就是在aidl文件中定义的方法,其数值根据定义的方法递增,从1开始。例如,在IAccessibilityManager.aidl文件中,定义了以下方法。

Android系统服务Fuzz测试_第3张图片

三、测试框架

  了解了以下原理之后,就可以编写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工具的架构如下:

Android系统服务Fuzz测试_第4张图片
  还有一个问题,要实现整个Fuzz的自动化,还需要自动化监控系统重启事件。

四、DoS的自动化检测

  Android系统开机完成之后,会发出一个BOOT_COMPLETED 系统广播 ,所以通过检测此广播可以监控系统是否发生了重启,从而确定漏洞位置,即哪一个系统服务对应的哪一个方法触发。除此之外,BOOT_COMPLETED 广播是由ActivityManagerService的finishBooting方法发出的,所有可以通过Hook此API完成监测。鉴于Xposed的Hook框架可以很容易的帮助我们做到这点。

你可能感兴趣的:(Android安全)