Android系统篇之----Binder机制和远程服务调用机制分析

一、前景概要

最近要实现Android中免注册Activity就可以运行的问题,那么结果是搞定了,就是可以不用在AndroidManifest.xml中声明这个Activity即可运行,主要是通过骗取系统,偷龙转凤技术的,这个知识点后面会详细讲解的,因为在研究了这个问题过程中遇到了很多知识点,当然最重要也是最根本的就是Android中的Binder机制和远程服务调用机制,而关于Binder机制的话,在Android中算是一个非常大的系统架构模块了,光这篇文章是肯定不能讲解到全部的,而且本人也不是非常的熟悉Binder机制,只是发表个人的理解,并且会用最简单的语言讲解最核心的知识,因为现在有很多知识都在介绍Binder机制,但是大部分说的都太抽象了大部分都看不懂。

二、具体需求

接下来几篇主要是介绍关于Android中的应用启动流程,通过Hook机制拦截Activity的启动流程,达到我们想要的功能,同时可以实现Activity无需在AndroidManifest.xml中声明即可运行的效果。

三、Android中远程服务调用分析

我们如果做过多进程之间通信的话都了解远程服务的使用了,使用也是非常简单的,下面来看一下如何定义自己的远程服务:

1、定义一个AIDL文件

类似于定义接口类型,这个AIDL文件将在本地和远端都要使用到

2、定义远端服务

在远程服务中的onBind方法,实现AIDL接口的具体方法,并且返回Binder对象

3、本地创建连接对象

本地创建一个服务连接对象,实现ServiceConnection接口,在连接成功之后,会得到一个远端传递过来的Binder对象,就是上面的远端服务onBind方法返回的,得到Binder对象之后在进行转化就可以得到AIDL对象,然后即可调用方法

4、连接服务

连接服务也是比较简单的,这时候把上面的连接对象传递进去即可

到这里我们就看到完成了本地端和远端的通信了,如果把DemoService远端服务定义在另外一个进程中,那么这里就可以实现多进程通信了。看到上面的步骤很简单,但是有一个核心的地方就是Demo.Stub类,这个类起着重要的作用,下面来分析一下他的实现,每次定义了AIDL接口文件之后,编译一下就会在gen目录中产生对应的java文件了:

这里的实现稍微有点复杂了,但是这些内容其实都是编译工具帮我们实现的,而这个实现都是有一定规则的:

1、AIDL接口必须实现IInterface接口

关于IInterface接口的实现也很简单:

这里可以把当前的AIDL对象转化成一个IBinder类型对象

2、AIDL接口中肯定有一个静态实现类Stub

这个类必须实现Binder类,以及本身的AIDL接口类型。那么这个类就具备了Binder类中的四个功能:

1》可以将Binder对象转化成AIDL对象,调用asInterface方法,可以看到这个方法其实和上面的asBinder方法对立的

2》通信方法onTransact实现,这个方法是最核心的用于通信之间的逻辑实现

3》通过queryLocalInterface方法可以根据类的描述符(字符串可以唯一标识这个远端服务的名称即可)获取到对应的AIDL对象(其实是IInterface类型的)

4》在构造方法中必须调用Binder中的attachInterface方法把当前服务对象和描述符进行关联

上面看完了Stub类之后,发现他其实是远端服务Binder对象的一个中间者,用来和客户端进行交互的,下面再来看一下Proxy类:

这个代理对象其实就是远端传递过来Binder对象的一个代理,他是客户端这边用户和服务端交互的中间者。我们在前面的Stub类中可以看到:

这里会把远端传递过来的Binder对象转化成一个本地对象,发现先是通过queryLocalInterface方法借助服务描述符来获取对象:

而这个mOwner和mDescriptor之间的关系就在attachInterface方法中进行初始化的,也就是在Stub类的构造方法中

那么现在就清楚了,如果客户端和服务端是在一个进程中,那么其实queryLocalInterface获取的就是Stub对象,如果不在一个进程queryLocalInterface查询的对象肯定为null,因为不同进程有不同虚拟机,肯定查不到mOwner对象的,所以这时候其实是返回的Proxy对象了。通过上面的讲解之后,发现多进程服务通信基准就是借助Binder对象,先传递Binder对象,然后在把Binder转成可以使用的原生对象即可调用了,而对于Stub类和Proxy类其实就是相当于是服务端和客户端的中间者,把一些逻辑封装起来,这种设计也会显得不是那么凌乱:

Stub类是服务端的中间者,一般是实现了AIDL接口类型和继承了Binder类,具备将Binder对象转化成原生对象的能力

Proxy类是客户端的中间者,一般是实现了AIDL接口类型

四、分析系统服务调用流程

这种图就够形象了,所以后面会看到系统中的一些服务使用的时候其实也是跨进程使用,比如下面来看一下著名的PackageManager,IPackageManager,PackageManagerService体系:

这个就是IPackageManager.aidl文件,因为我们还没有编译源码,所以这里可能需要AIDL工具单独编译才能看到IPackageManager.java了:

这里看到了熟悉的远端服务中间者Stub和本地端的中间者Proxy类了,而这两个类的规则都和上面一样的。

下面来看一下远端服务实现代码PackageManagerService.java:

实现了上面的的Stub类功能。

下面我们再走一遍获取PackageManager的流程:

而这个getPackageManager方法是在ContextImpl.java中实现的:

看到了吧,这里先从ServiceManager的getService中获取到一个PackageManager远端的IBinder对象,然后在使用Stub的asInterface方法进行转化成本地的PackageManager对象,其实就是那个Proxy对象。然后就可以通过PackageManager来调用方法和远端的PackageManagerService服务进行通信了。

通过上面的PackageManager案例可以分析,我们在使用系统中的服务的时候的流程都是如此:

每个应用在使用系统服务的时候,都会走这么几步:

1、调用getSystemService(String serviceName)方法获取服务对象

2、而getSystemService一般都是在ContextImpl类中实现的,其实是调用了ServiceManager的getService方法

3、调用ServiceManager的getService方法获取远端服务的IBinder对象

4、有了远端服务的IBinder对象之后,在使用远端服务的中间者类Stub进行转化对象asInterface方法

5、因为系统中的服务获取都是肯定是跨进程的,远端服务都是在system_server进程中的,所以asInterface方法中返回的是Proxy代理对象,也就是本地端的中间者。

6、最后返回的对象其实就是这个Proxy对象,而这个对象内部使用了静态代理方式,内部有一个来自远端的mRemote变量即IBinder对象。然后直接调用方法其实就是调用mRemote的transact方法进行通信了。

所以在这个过程中可以看到有两个对象很重要,一个是ServiceManager,一个是IBinder对象。下面再来一一介绍

五、服务大管家ServiceManager

那么现在又有几个问题了,这个ServiceManager是干嘛的?如何能够通过服务描述符就可以获取到对应服务的IBinder对象?这个就要看一下ServiceManager的功能了:

首先看看他获取远端服务的IBinder对象方法,他本身会维护一个IBinder缓存池,也是为了效率高考虑,对于一个应用频繁的使用一些服务的话效率就会高很多。

然后最核心的获取服务的方法是getIServiceManager方法:

这个方法看到了,又是一个远程通信获取Binder对象,而这次是IServiceManager对象了:

看到这里的ServiceManager也是通过远端服务获取到他的IBinder对象,然后在转化成本地对象进行使用的。那么刚刚看到系统的服务都是通过ServiceManager管理获取的,而现在ServiceManager本身是怎么获取到的IBinder对象的呢?这个就要从系统启动的时机看了,众所周知系统启动的时候是根据init.rc文件进行操作的:

这里会启动一个servicemanager服务,那么就要去service_manager.c程序中的入口程序看了:

这个入口其实包含了Binder机制的重要信息,而主要就是三件事:

1、打开底层的Binder驱动程序,这个后面介绍Binder机制在介绍

2、通过向binder程序发送命令:BINDER_SET_CONTEXT_MGR,告诉binder程序,我要成为大管家

3、进入循环监听上层应用的服务请求处理,所以这里可以看到其实ServiceManager是一个守护进程在后台默默监听

在第二步中成为大管家的代码深入看一看:

其实这里的逻辑也是比较简单的,首先创建一个属于servicemanager的binder节点,然后在创建一个binder链表,而这个链表的作用就是存放上层中需要系统服务的所有binder对象的节点,这样ServiceManager就可以实现了服务的增加和查询操作了。

再来看看ServiceManager的添加服务操作:

添加服务比较复杂,首先查看这个服务有没有注册权限限制,不是所有的服务都能注册的,然后在查看这个服务是不是已经被注册过了,最后在通知binder驱动程序注册一个服务即可。

然后在来看看ServiceManager的查找服务功能:

查找服务就比较简单了,直接通过服务的描述符名称遍历binder链表节点即可。

1、Service Manager能集中管理系统内的所有服务,它能被施加权限控制,并不是任何进程都能注册服务的。
2、Service Manager支持通过字符串名称来查找对应的Service。
3、由于各种原因的影响,Server进程可能生死无常。如果有了Service Manager做统一的管理,那么Client只要向Service Manager做查询,就能得到Server的最新信息。

六、系统服务注册流程分析

下面来看一下一些系统服务是如何进程注册的,这里用MediaService来进行查看吧:

系统中的MediaService服务的启动也是在init.rc中的

查看main_mediaserver.cpp源码的main函数:

看到了系统的MediaServer依赖很多其他服务的,而这些服务必须要注册的,他们的注册操作都是在各自的初始化方法中的,这里用MediaPlayerService来看看注册操作:

看到熟悉的代码了把,这里通过ServiceManager来进行服务注册了,那么这里是如何获取到ServiceManager的?

看看ProcessState.cpp的源码:

看看getStrongProxyForHandle方法实现:

这里看到了,会使用IPCThreadState的transact方法和底层的Binder进行通信的,然后使用一个句柄handle构造一个BpBinder对象,而BpBinder对象其实就是native层实现的Binder对象,以后只要看到Bp开头的就是代理对象对应Java层的Proxy对象,Bn开头的就是native对象对应Java层的Stub对象。

在上面分析servicemanager的时候知道会维护一个binder节点链表,那里其实就有一个每个binder对应句柄handle,而后续进行通信的话都是通过这个句柄来标识是哪个服务的binder对象了,这样也就在通信的时候不会发生紊乱了,而servicemanager的句柄handle就是0。还有一个知识点就是可以看到IPC通信的时候传输数据使用的就是Parcel类,这个类就是为了跨进程通信产生的,他有一个方法readStrongBinder,就是可以从Parcel的数据中获取到Binder对象,这个也是在跨进程中传递Binder对象的核心地方。

好了,上面就通过系统的mediaserver服务来讲解了系统服务的注册流程:

到这里就分析完了Android中的远程服务调用机制逻辑以及ServiceManager这个服务大管家的作用:

1、首先跨进程通信的话,肯定会有两个对象:一个是本地端的中间者Proxy对象,一个是远程端的中间者Stub对象

2、Proxy对象通过静态代理模式维持一个远端传递过来的Binder对象,而Stub对象可以把远端传递过来的Binder对象转化成一个实际服务对象给应用使用

3、Android中在使用系统服务的时候通过getSystemService方法获取到的其实都是Stub把远端的Binder转化的对象,因为系统服务都是在system_server进程中,所以肯定是跨进程获取对象的,那么这个Binder对象其实就是上面的Proxy对象

4、系统的服务都是在一个指定的系统进程中system_server

5、服务大管家ServiceManager在系统启动的时候也是先获取自生的Binder对象,然后转化成实际操作对象,然后才可以操作系统服务的注册和查询功能

下面是系统一些服务的注册流程:

上面已经介绍了远程五福调用机制以及ServiceManager的实现原理,下面就要看看另外一个重点,也是上面提到的一个重要对象Binder,准确来说这个是Binder机制,在Android中Binder机制最复杂的一个架构系统了,它的设计很复杂,所以有很多同学在了解Binder机制的时候,总是看着看着就晕了,今天我们就直说重点,而且说得要相对明了简单。

七、Binder机制解析

第一、Android中的IPC为何要采用Binder机制

Binder是Android系统进程间通信(IPC)方式之一。Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection):管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。

Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID,PID身份,既支持实名Binder也支持匿名Binder,安全性高。对Binder而言,Binder可以看成Server提供的实现某个特定服务的访问接入点, Client通过这个‘地址’向Server发送请求来使用该服务;对Client而言,Binder可以看成是通向Server的管道入口,要想和某个Server通信首先必须建立这个管道并获得管道入口。

第二、Android中的Binder实现原理

其实Android中的Binder通信都是通过虚拟驱动设备程序/dev/binder来实现的,我们知道一些硬件都会对应一个驱动程序,而binder驱动程序没有对应的硬件,所以叫做虚拟驱动设备程序,其实他就是一个字符驱动设备,或者叫做miscdevice混杂设备驱动。

其实混杂驱动设备是字符设备的一种,它们共享一个主设备号(10),但次设备号不同,所有的混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的miscdevice设备。例如:触摸屏,LED,按键,串口。即:为了节约主设备号,将某些设备用链表的形式连接在一起,最后通过查找次设备区分。这里用主设备无法匹配出设备驱动,只能找到链表,再通过次设备号,才能找到设备驱动。而之前所学的,一般字符设备,通过主设备号,就能找到设备驱动了。我们可以通过命令查看/dev/binder驱动的主设备号:

第三、Android中Binder通信机制

先来看一张图,我们可以大体的了解到了客户端和服务端通过Binder驱动进行通信

首先不管是客户端进程还是服务端进程都是在用户空间的,而binder驱动是在内核空间的,通信的数据是有规定格式也叫作IPC数据,既然是一种通信机制,肯定是需要协议,数据格式等基础结构信息的:

上面在分析了ServiceManager的启动的时候说到了,第一步是打开驱动程序,具体打开函数在binder.c中:

在使用一个驱动之前,肯定要先打开驱动,然后把驱动程序映射到内存中,接着借助IPCTreadState.cpp和binder驱动进行通信了:

所以看到这里IPCThreadState也是需要进入后台进行监听的,处理来自客户端和服务端的数据传输消息

最后再来看一下通信时序图。

到这里我们就介绍完了Binder机制了,关于Binder机制最好不要看太深,因为越深你觉得越复杂越难理解,其实你只要了解到他是一个通信工具,通信采用的是驱动操作,通过传输IPC数据来进行通信即可。其他的关于他的详细数据格式和通信协议,感兴趣的同学可以了解一下,但是太过复杂而且在实际中也没多大用途,所以这里就不介绍了。

八、技术点概要

1、理解远程服务通信机制

通过案例先了解到本地端和服务端跨进程通信,主要就是借助Binder进行功能调用,而在这里主要有两个核心类,一个是Stub类,这个类是继承了Binder类具备了将远程传递的Binder对象转化成本地实际对象asInterface方法即可,同时实现了IXXX接口,需要实现AIDL中的功能方法,还有一个类就是Proxy类,实现了IXXX接口,同时内部保留着远端传递的Binder对象,然后通过这个对象调用远端方法。这里Stub类就是服务端的中间者,而Proxy就是本地端的中间者。

2、系统服务调用流程

通过分析了跨进程通信机制原理之后,再去看看Android系统中在使用一些服务的时候,通过getSystemService方法获取服务对象,其实这内部就是通过跨进程获取到了远端服务的Binder对象,然后转化成系统服务对象给应用调用,而这些系统服务的Binder对象在系统启动的时候服务会自动注册到ServiceManager中。

3、服务大管家ServiceManager

在整个远程服务调用过程中两个重要对象,一个是Binder对象,一个就是ServiceManager类,这个类是管理系统服务的类,他可以注册服务,查询服务,系统服务在系统启动的时候会通过addService进行服务注册,然后应用就可以通过getService进行服务查询,而在这个过程中,底层会维护一个这些服务的binder链表结构,同时每个服务的binder对象都一个句柄handle,通过这个句柄来表示通信标识,这样通信才不会紊乱。

4、底层通信核心Binder

最后分析了底层真正实现跨进程通信的机制Binder,其实是通过虚拟驱动程序/dev/binder进行通信的。一个通信机制肯定有通信协议,传输的数据结构,但是这里并没有介绍这些知识,原因是我们后面的需求并不会用到这些,其次是这些知识点太详细介绍也不好,因为会越看越乱。

九、总结

本文介绍的东东有点多,但是如果掌握了Android中的Binder机制和远程服务调用机制对后面拦截系统api做了铺垫,说到结束了才告诉大家为什么要介绍这个知识点,是因为最近在研究如何拦截系统启动Activity的事,那么就必须了解Activity的启动流程,但是在这个过程中有一个对象就是ActivityManagerService,而他就和Binder以及远程服务调用机制紧密联系了,如果不了解Binder机制,后面工作是没办法进行的,好了,说到最后再来一张神图算是总结了本文内容:

这张图非常好的表达了Android中应用使用系统服务的一个流程,也是最好的最全的解释了。看懂这张图之后,那么对Android中的binder机制和远程服务调用机制就可以掌握了,可以进行后续的拦截操作了。

更多内容:点击这里

关注微信公众号,最新技术干货实时推送

时间: 09-09

Android系统篇之----Binder机制和远程服务调用机制分析的相关文章

Android系统篇之----解读AMS远端服务调用机制以及Activity的启动流程

一.为何本文不介绍Hook系统的AMS服务 在之前一篇文章中已经讲解了 Android中Hook系统服务,以及拦截具体方法的功能了,按照流程本文应该介绍如何Hook系统的AMS服务拦截应用的启动流程操作,但是本文并不会,因为在介绍这个知识点之前,还有一件大事要做,那就是得先分析一下Android中应用的启动流程,如果这个流程不搞清楚的话,后面没办法Hook的,因为你都找不到Hook点,当然Hook代理对象倒是很容易获得,如果没有Hook点,是没办法后续的操作的,所以得先把流程分析清楚了,当然现在

Android系统篇之----编写系统服务并且将其编译到系统源码中

在之前已经介绍了一篇关于如何编写简单的驱动以及访问该驱动的小程序,最后将程序编译到Android内核源码中通过程序访问驱动验证是可以通过的,那么本文就继续这个知识点,把这个驱动程序通过JNI连接创建一个系统服务,提供给上层应用访问改服务功能,可以看到前一篇介绍驱动程序的功能是属于内核层的,而本文介绍的内容是Framework层的知识. 声明:本文内容参考罗升阳的书籍:<Android系统源代码情景分析> 如果想了解更详细的内容非常建议购买此书 非常感谢罗神的这本书,给我带来很多未知的知识,大神

Android系统篇之—-编写系统服务并且将其编译到系统源码中【转】

本文转载自:http://www.wjdiankong.cn/android%E7%B3%BB%E7%BB%9F%E7%AF%87%E4%B9%8B-%E7%BC%96%E5%86%99%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1%E5%B9%B6%E4%B8%94%E5%B0%86%E5%85%B6%E7%BC%96%E8%AF%91%E5%88%B0%E7%B3%BB%E7%BB%9F%E6%BA%90%E7%A0%81/ 在之前已经介绍了一篇关于 如何编写简单的

Android系统篇之----免root实现Hook系统服务拦截方法

技术概念来源:[ 360开源插件框架,项目地址:https://github.com/DroidPluginTeam/DroidPlugin ] 一.Binder机制回顾 在之前一篇文章中介绍了 Android中的Binder机制和系统远程服务调用机制,本文将继续借助上一篇的内容来实现Hook系统服务拦截指定方法的逻辑,了解了上一篇文章之后,知道系统的服务其实都是一个远程Binder对象,而这个对象都是由ServiceManager大管家管理的,用户在使用系统服务的时候,会通过指定服务的Stub

Android系统篇之—-编写简单的驱动程序并且将其编译到内核源码中【转】

本文转载自:大神 通过之前的一篇文章,我们了解了 Android中的Binder机制和远程服务调用 在这篇文章中主要介绍了Android中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一下如何通过编译Android源码来手动添加一个系统服务,让编译之后的Android系统中存在我们的这个服务,每个应用都可以调用.但是本文得先介绍一下如何编写一个简单的驱动程序,先介绍了驱动程序,然后在通过JNI让framework层访问这个驱动程序,在通过注册服务来封装这个功能,最后在把这个服务添加

Android系统篇之----编写简单的驱动程序并且将其编译到内核源码中

通过之前的一篇文章,我们了解了 Android中的Binder机制和远程服务调用 在这篇文章中主要介绍了Android中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一下如何通过编译Android源码来手动添加一个系统服务,让编译之后的Android系统中存在我们的这个服务,每个应用都可以调用.但是本文得先介绍一下如何编写一个简单的驱动程序,先介绍了驱动程序,然后在通过JNI让framework层访问这个驱动程序,在通过注册服务来封装这个功能,最后在把这个服务添加到系统中,实现上层

Android系统篇之----Android中的智能指针

一.前言 今天我们开启Android系统篇的文章了,其实一直想弄,只是之前一直没有太多深入的了解,最近又把这块拿出来好好看了一下,所以想从新梳理一下,来看看Android中的这块知识,首先我们今天来看一下:Android中的智能指针的概念,为什么说先看一下智能指针这个知识呢?因为我们在看Android源码的时候,会发现几乎好多地方都用到了这个东东,所以我们在介绍后面的知识点,先来看看这个吧. 二.问题 那么Android中的智能指针是个什么东西呢?我们知道Android用的Java语言开发的,J

Android系统中基于Binder的IPC流程框架分析

前言: Activity.Service.BroadcastReceiver.Content Provider是Android的四大应用程序组件,构成一个完整的应用程序的这些组件可以在同一个进程,也可以不在同一个进程,而当这些组件不在同一个进程,需要进行数据交互时就需要一种IPC(Inter-Process Communication)进程间通信机制来完成,而Binder就是提供了IPC功能的一个框架.实现IPC的整个Binder框架包含几个重要组成部分,它们分别是Binder Driver.C

理解Android系统的进程间通信原理(二)----RPC机制

理解Android系统中的轻量级解决方案RPC的原理,需要先回顾一下JAVA中的RMI(Remote Method Invocation)这个易于使用的纯JAVA方案(用来实现分布式应用).有关RMI的相关知识,可以通过下图来归纳: Android中的RPC也是参考了JAVA中的RMI方案,这里我们再详细了解一下RPC的实现过程. Android中的RPC机制是为了实现一个进程使用另一个进程中的远程对象,它使用了Android自己的AIDL(接口定义语言),使用户很方便地定义出一个接口作为规范,