Android 系统服务安全

系列 - SystemSecurity

我们在Android开发过程中经常会用到各种各样的系统管理服务,如进行窗口相关的操作会用到窗口管理服务WindowManager,进行电源相关的操作会用到电源管理服务PowerManager,还有很多其他的系统管理服务,如通知管理服务NotifacationManager、振动管理服务Vibrator、电池管理服务BatteryManager…… 这些Manager提供了很多对系统层的控制接口。1

在挖漏洞时,也可以针对系统服务进行挖掘,可确认系统服务是在Framework中实现的,且大部分的系统服务都通过AIDL方式进行实现(Google也提倡使用该方式实现自定义服务),但无论如何,系统服务都必须在ServiceManager中进行注册,当App客户端需要使用某个Service时,也需要向ServiceManager查询该Service是否注册过了。2

我们可以先通过ServiceManager的启动源码来了解启动过程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    onrestart restart apexd
    onrestart restart audioserver
    onrestart restart gatekeeperd
    onrestart class_restart --only-enabled main
    onrestart class_restart --only-enabled hal
    onrestart class_restart --only-enabled early_hal
    task_profiles ServiceCapacityLow
    shutdown critical

ServiceManager进程启动后,会执行main函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
#ifdef __ANDROID_RECOVERY__
    android::base::InitLogging(argv, android::base::KernelLogger);
#endif
    if (argc > 2) {
        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
    }
    const char* driver = argc == 2 ? argv[1] : "/dev/binder";
    sp<ProcessState> ps = ProcessState::initWithDriver(driver); //启动Binder线程池
    ps->setThreadPoolMaxThreadCount(0);
    ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
    sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>()); //启动ServiceManager服务并获取ServiceManager服务Binder代理对象
    if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
        LOG(ERROR) << "Could not self register servicemanager";
    } //将ServiceManager服务的Binder代理对象注册到ServiceManager服务中
    IPCThreadState::self()->setTheContextObject(manager);
    ps->becomeContextManager();//将ServiceMager服务自己注册为上下文管理者
    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
    BinderCallback::setupTo(looper);
    ClientCallbackCallback::setupTo(looper, manager);
    while(true) {
        looper->pollAll(-1);
    }//启动Binder线程池Looper并接收处理消息
    // should not be reached
    return EXIT_FAILURE;
}

上面的代码主要进行了如下的启动流程:

  1. 启动Binder线程池
  2. 启动ServiceManager服务并获取ServiceManager服务Binder代理对象
  3. 将ServiceManager服务的Binder代理对象注册到ServiceManager服务中
  4. 将ServiceMager服务自己注册为上下文管理者
  5. 启动Binder线程池Looper并接收处理消息

我们可以通过ServiceManager的源码来获取ServiceManager的作用

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
 * Manage binder services as registered with the binder context manager. These services must be
 * declared statically on an Android device (SELinux access_vector service_manager, w/ service
 * names in service_contexts files), and they do not follow the activity lifecycle. When
 * building applications, android.app.Service should be preferred.
 *
 * @hide
 **/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class ServiceManager {
    private static final String TAG = "ServiceManager";
    private static final Object sLock = new Object();
    @UnsupportedAppUsage
    private static IServiceManager sServiceManager;
    ...
    /** @hide */
    @UnsupportedAppUsage
    public ServiceManager() {
    }
    @UnsupportedAppUsage
    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }
        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }
    /**
     * Returns a reference to a service with the given name.
     *
     * @param name the name of the service to get
     * @return a reference to the service, or <code>null</code> if the service doesn't exist
     * @hide
     */
    @UnsupportedAppUsage
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
    ...
    /**
     * Place a new @a service called @a name into the service
     * manager.
     *
     * @param name the name of the new service
     * @param service the service object
     * @hide
     */
    @UnsupportedAppUsage
    public static void addService(String name, IBinder service) {
        addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
    }
    ...
    /**
     * Retrieve an existing service called @a name from the
     * service manager.  Non-blocking.
     * @hide
     */
    @UnsupportedAppUsage
    public static IBinder checkService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(getIServiceManager().checkService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in checkService", e);
            return null;
        }
    }
    /**
     * Returns whether the specified service is declared.
     *
     * @return true if the service is declared somewhere (eg. VINTF manifest) and
     * waitForService should always be able to return the service.
     */
    public static boolean isDeclared(@NonNull String name) {
        try {
            return getIServiceManager().isDeclared(name);
        } catch (RemoteException e) {
            Log.e(TAG, "error in isDeclared", e);
            return false;
        }
    }
    /**
     * Returns the list of declared instances for an interface.
     *
     * @return true if the service is declared somewhere (eg. VINTF manifest) and
     * waitForService should always be able to return the service.
     * @hide
     */
    public static String[] getDeclaredInstances(@NonNull String iface) {
        try {
            return getIServiceManager().getDeclaredInstances(iface);
        } catch (RemoteException e) {
            Log.e(TAG, "error in getDeclaredInstances", e);
            return null;
        }
    }
    /**
     * Returns the specified service from the service manager.
     *
     * If the service is not running, servicemanager will attempt to start it, and this function
     * will wait for it to be ready.
     *
     * @return {@code null} only if there are permission problems or fatal errors.
     * @hide
     */
    public static IBinder waitForService(@NonNull String name) {
        return Binder.allowBlocking(waitForServiceNative(name));
    }
    private static native IBinder waitForServiceNative(@NonNull String name);
    /**
     * Returns the specified service from the service manager, if declared.
     *
     * If the service is not running, servicemanager will attempt to start it, and this function
     * will wait for it to be ready.
     *
     * @return {@code null} if the service is not declared in the manifest, or if there are
     * permission problems, or if there are fatal errors.
     * @hide
     */
    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    @Nullable public static IBinder waitForDeclaredService(@NonNull String name) {
        return isDeclared(name) ? waitForService(name) : null;
    }
    /**
     * Register callback for service registration notifications.
     *
     * @throws RemoteException for underlying error.
     * @hide
     */
    public static void registerForNotifications(
            @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException {
        getIServiceManager().registerForNotifications(name, callback);
    }
    /**
     * Return a list of all currently running services.
     * @return an array of all currently running services, or <code>null</code> in
     * case of an exception
     * @hide
     */
    @UnsupportedAppUsage
    public static String[] listServices() {
        try {
            return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL);
        } catch (RemoteException e) {
            Log.e(TAG, "error in listServices", e);
            return null;
        }
    }
    /**
     * Get service debug info.
     * @return an array of information for each service (like listServices, but with PIDs)
     * @hide
     */
    public static ServiceDebugInfo[] getServiceDebugInfo() {
        try {
            return getIServiceManager().getServiceDebugInfo();
        } catch (RemoteException e) {
            Log.e(TAG, "error in getServiceDebugInfo", e);
            return null;
        }
    }
    /**
     * This is only intended to be called when the process is first being brought
     * up and bound by the activity manager. There is only one thread in the process
     * at that time, so no locking is done.
     *
     * @param cache the cache of service references
     * @hide
     */
    public static void initServiceCache(Map<String, IBinder> cache) {
        if (sCache.size() != 0) {
            throw new IllegalStateException("setServiceCache may only be called once");
        }
        sCache.putAll(cache);
    }
    ...
}

通过上述的源码,我们可以看见在ServiceManager中存在获取服务,添加服务,获取服务列表等函数,对于几个关键函数解析如下

通过ServiceManager的Binder代理对象,调用getService(String name)即可获取到指定名称服务的Binder接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/**
     * Returns a reference to a service with the given name.
     *
     * @param name the name of the service to get
     * @return a reference to the service, or <code>null</code> if the service doesn't exist
     * @hide
     */
    @UnsupportedAppUsage
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

这里的sCache是一个Map,可以通过key值,即service名称获取对应的IBinder对象

通过listServices函数能够获取到当前在ServiceManager中注册的所有service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
     * Return a list of all currently running services.
     * @return an array of all currently running services, or <code>null</code> in
     * case of an exception
     * @hide
     */
@UnsupportedAppUsage
public static String[] listServices() {
    try {
        return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL);
    } catch (RemoteException e) {
        Log.e(TAG, "error in listServices", e);
        return null;
    }
}
  • 先定义一个服务需要包含的信息:服务名,函数的code值,函数参数类型和返回值类型
1
2
3
4
5
6
7
8
/**
 * 定义服务结构
 * 包含了服务名和接口结构,methodInfo中包括了接口的Code值,接口的名称,接口参数类型数组,返回值类型
 */
public class ServiceInfo {
    private String serviceName;
    private Map<Integer, List<String>> methodInfo;
	...
  • 反射获取系统服务列表
1
2
3
4
5
6
7
8
9
public static String[] getServiceNameList(){
    String[] serviceNameList = {};
    try{
        serviceNameList = (String[]) serviceManager.getDeclaredMethod("listServices").invoke(null);
    }catch (Exception e){
        e.printStackTrace();
    }
    return serviceNameList;
}
  • 根据服务名获取服务的接口描述符
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static String getInterfaceDescriptor(String serviceName){
    try{
        Method getService = serviceManager.getMethod("getService", String.class);
        IBinder serviceBinder = (IBinder) getService.invoke(serviceManager.newInstance(), serviceName);
        if(serviceBinder == null){
            Log.e(TAG,  "Service " + serviceName + " Binder is null");
            return null;
        }
        String interfaceDescriptor = serviceBinder.getInterfaceDescriptor();
        return interfaceDescriptor.length() != 0 ? interfaceDescriptor : null;
    }catch (Exception e){
        Log.e(TAG, "Service " + serviceName + " get InterfaceDecriptor failed!");
        return null;
    }
}
  • 获取函数和code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private static HashMap<String,Integer> getMethodMap(String interfaceDescriptor) {
    HashMap<String, Integer> codes = new HashMap<>();
    if (interfaceDescriptor == null)
        return codes;
    try {
        Class<?> cStub = Class.forName(interfaceDescriptor + "$Stub");
        Field[] f = cStub.getDeclaredFields();
        for (Field field : f) {
            field.setAccessible(true);
            String k= field.toString().split("\\$Stub\\.")[1];
            if (k.contains("TRANSACTION_") && field.getInt(null) > 0)
                codes.put(k, field.getInt(null));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return codes;
}
  • 获取函数参数类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private static String getParamterTypeStr(Method method){
    method.setAccessible(true);
    Class[] params = method.getParameterTypes();
    String returnStr = "";
    for(Class i : params){
        returnStr += i.getName() + ";";
    }
    return returnStr.length() > 0 && ";".equals(returnStr.substring(returnStr.length() - 1)) ?
        returnStr.substring(0, returnStr.length() - 1):returnStr;
}
  • 获取函数返回值类型
1
2
3
4
private static String getReturnType(Method method){
    method.setAccessible(true);
    return method.getReturnType().getName();
}
  • 结合上述函数,获取完整服务信息
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static ServiceInfo getServiceInfo(String serviceName){
    if(serviceName == null){
        return null;
    }
    Map<Integer, List<String>> methodInfo = new HashMap<>();
    String interfaceDescriptor = getInterfaceDescriptor(serviceName);
    ServiceInfo serviceInfo = new ServiceInfo();
    if(interfaceDescriptor == null){
        serviceInfo.setServiceName(serviceName);
        serviceInfo.setMethodInfo(null);
        return serviceInfo;
    }
    try{
        Class<?> classStub = Class.forName(interfaceDescriptor + "$Stub$Proxy");
        Method[] methods = classStub.getDeclaredMethods();
        HashMap<String, Integer> tempMethodMap = getMethodMap(interfaceDescriptor);
        for(Method method : methods){
            method.setAccessible(true);
            String methodName = method.getName();
            int methodCode = 0;
            for(String key : tempMethodMap.keySet()){
                if(methodName.contains(key.substring("TRANSACTION_".length()))){
                    methodCode = tempMethodMap.get(key);
                }
            }
            if(methodCode == 0)
                continue;
            String parameterTypeStr = getParamterTypeStr(method);
            String returnType = getReturnType(method);
            List<String> methodSignature = Arrays.asList(methodName, parameterTypeStr, returnType);
            methodInfo.put(methodCode, methodSignature);
        }
        return serviceInfo.setServiceInfo(serviceName, methodInfo);
    }catch (ClassNotFoundException e) {
        Log.e(TAG, "Serivce " + serviceName + "$Stub$Proxy ClassNotFoundException");
        serviceInfo.setServiceName(serviceName);
        serviceInfo.setMethodInfo(null);
    }catch (Exception e){
        e.printStackTrace();
    }
    return serviceInfo;
}

当获取一个服务的完整信息后,结合前面获取整个服务列表的函数,就能够获取整机框架服务信息了,当然,在执行程序前,需要解除Google对非SDK接口的限制,即执行 adb shell settings put global hidden_api_policy 1

通过以上代码获取Service

通过动态获取系统服务信息存在缺点,会存在部分服务无法获取到接口与函数的定义点,针对这类问题,我们也可以使用静态匹配的方法来解决。相比App直接获取相关信息,使用静态需要经过反编译并匹配的过程,运行速度会比动态获取慢很多,但是相对的也能匹配到更多的信息。

TODO

并由binder进行通信,那么针对binder,可以通过Frida注入dex的方式获取到ServiceManager中注册的服务名,再通过反射获取binder或者hook system_server的方式获取其中的方法,这样就可以进行Fuzz测试了。


  1. Android系统服务(SystemService)简介 - 云+社区 - 腾讯云 (tencent.com) ↩︎

  2. 浅谈Android java层ServiceManager_Androider_Zxg的博客-CSDN博客_java manager层 ↩︎

  3. Android之ServiceManager服务 - naray - 博客园 (cnblogs.com) ↩︎

  4. 农民斗地主——Binder fuzz安全研究 | Colbert’s blog (colbert337.github.io) ↩︎

  5. Android Binder Fuzzing 的一些思路 | 安全代码 (usmacd.com) ↩︎