背景分析
最近在学习Android非侵入Hook机制,
6月30日,360开源RePlugin,安卓进入“全面插件化”时代:https://www.itcodemonkey.com/article/278.html
同一天,滴滴开源Android端插件化框架VirtualAPK:https://www.itcodemonkey.com/article/277.html
然后本人发现竟然可以不在清单文件中注册就可以启动Activity,还有这种操作?哈哈。后面会有单独的文章介绍这种骚操作。
在搜索中发现了这篇博客,
Android系统篇之—-Binder机制和远程服务调用机制分析。本文大部分转载自原来博客,结合自己的分析,是研究Hook机制前的知识储备。
Android中远程服务调用分析
1.定义一个AIDL文件:Demo.aidl
类似于定义接口类型,这个AIDL文件将在本地和远端都要使用到
package com.agehua.aidldemo; |
2.定义远程服务
在远程服务中的onBind方法,实现AIDL接口的具体方法,并且返回Binder对象//远程服务,应该定义在另个一进程中
public class DemoService extends Service {
public IBinder onBind(Intent intent) {
//返回远程的Binder对象,并且实现类
return new Demo.Stub() {
public int sendData(String data) throws RemoteException {
return 0;
}
public String getData() throws RemoteException {
return "";
}
}
}
}
接口方法的具体传递实现都是在远端服务中。
3.本地创建连接对象
本地创建一个服务连接对象,实现ServiceConnection接口,在连接成功之后,会得到一个远端传递过来的Binder对象,就是上面的远端服务onBind方法返回的,得到Binder对象之后在进行转化就可以得到AIDL对象,然后即可调用方法。
//连接远程服务的回调 |
连接成功后,从远端服务中获取到了Binder对象,然后在转化成本地接口对象,即可调用方法。
4、连接服务
连接服务也是比较简单的,这时候把上面的连接对象传递进去即可
Intent intent = new Intent(this, DemoService.class); |
AIDL实现机制分析
上面的步骤就可以实现一个远程服务调用了。但是有一个核心的地方就是Demo.Stub类,这个类起着重要的作用,下面来分析一下它的实现:
每次定义了AIDL接口文件之后,编译一下就会在build/generated/source/目录中产生对应的java文件了:
package com.agehua.aidldemo; |
1、AIDL接口必须实现IInterface接口
IInterface接口包含一个asBinder()方法,由这个方法进行转化对象功能,把当前的AIDL对象转化成一个IBinder对象。package android.os;
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
2、AIDL接口中肯定有一个静态实现类Stub
这个类必须实现Binder类,以及本身的AIDL接口类型。那么这个类就具备了Binder类中的四个功能:
1.可以将Binder对象转化成AIDL对象,调用asInterface方法,可以看到这个方法其实和上面的asBinder方法对立的
2.通信方法onTransact实现,这个方法是最核心的用于通信之间的逻辑实现
3.通过queryLocalInterface方法可以根据类的描述符(字符串可以唯一标识这个远端服务的名称即可)获取到对应的AIDL对象(其实是IInterface类型的)
4.在构造方法中必须调用Binder中的attachInterface方法把当前服务对象和描述符进行关联
3、Stub类只是中间者,由Proxy类生成服务端的代理
为什么说是由Proxy类生成服务端的代理的呢?
因为在上面的DemoConnection类中,生成本地Demo对象,是调用了Demo.Stub.asInterface(IBinder)这个方法。
前面提到,服务端和客户端不在同一个进程的时候,asInterface()方法实际上调用了Demo.Stub.Proxy(IBinder)这个方法。
而且Demo.aidl中定义的抽象方法,具体都是由Proxy类去实现的。
Stub类,其实只是远端服务Binder对象的一个中间者,下面看代码:
//实现了aidl接口类。 |
Proxy是Stub类中的一个静态类,Proxy对象就是远端传递过来的Binder对象在本地的代理。这里用到的是静态代理模式。
在服务连接成功后,在onServiceConnected()方法中,返回一个服务端Binder对象,本地通过asInterface()方法生成的一个代理;Demo demo = Demo.Stub.asInterface(IBinder);
这个demo对象,就是客户端这边用户和服务端交互的中间者。我们在前面的Stub类的asInterface()方法实现中可以看到:
借助queryLocalInterface()方法根据服务描述符来获取对象,会把远端传递过来的Binder对象转化成一个本地对象:public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
而这个mOwner和mDescriptor之间的对应关系就在attachInterface方法中进行初始化的,也就是在Stub类的构造方法中public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
那么现在就清楚了,如果客户端和服务端是在一个进程中,那么其实queryLocalInterface获取的就是Stub对象,如果不在一个进程queryLocalInterface查询的对象肯定为null,因为new Demo.Stub()和Demo.Stub.asInterface(IBinder)方法分别是在远端进程和本地进程中调用的,在不同进程有不同虚拟机,肯定查不到mOwner对象的,所以这时候其实是返回的Proxy对象了。
通过上面的讲解之后,发现多进程服务通信基准就是借助Binder对象,先传递Binder对象,然后在把Binder转成可以使用的原生对象即可调用了,而对于Stub类和Proxy类其实就是相当于是服务端和客户端的中间者,把一些逻辑封装起来,这种设计也会显得不是那么凌乱:
分析系统服务调用流程
其实系统中的一些服务使用的时候其实也是跨进程使用,比如下面来看一下著名的PackageManager,IPackageManager,PackageManagerService体系:
PackageManagerService是Android系统中最常用的服务之一。它负责系统中Package的管理,应用程序的安装、卸载、信息查询等。PackageManager获取的信息即来自AndroidManifest.XML
interface IPackageManager { |
上面代码在谷歌的源码中查到,详情点击链接
因为我们还没有编译源码,所以看不到IPackageManager.java,这里可能需要AIDL工具单独编译才能看到了:public interface IPackageManager extends android.os.IInterface {
//定义内部类Stub,派生自Binder,实现IPackageManager接口
public static abstract class Stub extends android.os.Binder implements android.content.pm.IPackageManager {
private static final java.lang.String DESCRIPTOR = "android.content.pm.IPackageManager";
publicStub() {
this.attachInterface(this,DESCRIPTOR);
}
//......
//定义Stub的内部类Proxy,实现IPackageManager接口
private static class Proxy implements android.content.pm.IPackageManager{
//通过mRemote变量和服务端交互
private android.os.IBinder mRemote;
Proxy(android.os.IBinderremote) {
mRemote = remote;
}
//......
}
//......
}
这里看到了熟悉的远端服务中间者Stub和本地端的中间者Proxy类了,而这两个类的规则都和上面一样的。
下面来看一下远端服务实现代码PackageManagerService.java,(这个类就可以在IDE中看到了):public class PackageManagerService extends IPackageManager.Stub {
static final String TAG = "PackageManager";
static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
//...
}
实现了上面的的Stub类功能。
下面我们再走一遍获取PackageManager的流程:PackageManager pm = getPackageManager();
而这个getPackageManager方法是在ContextImpl.java中实现的:
|
具体内容实现是在ActivityThread.getPackageManager()方法中:public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
看到了吧,IPackageManager.Stub.asInterface(b)中的参数b由ServiceManager.getService方法获取到。然后在使用Stub的asInterface方法进行转化成本地的PackageManager对象,其实就是那个Proxy对象。然后就可以通过PackageManager来调用方法和远端的PackageManagerService服务进行通信了。
而DemoService是在DemoConnection(继承自ServiceConnection)的onServiceConnected回调中得到远端的IBinder对象,然后获得Proxy对象。
这里与自定义远程服务然后调用的区别就是,系统的远程服务都是由ServiceManager保存的,也就是由系统去创建和管理;而自定义的远程服务由开发者去创建、维护和销毁
通过上面的PackageManager案例可以分析,我们在使用系统中的服务的时候的流程都是如此:
总结一下,每个应用在使用系统服务的时候,都会走这么几步:
- 1、调用getService(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对象。下篇文章再来一一介绍
参考文献:
Android系统篇之—-Binder机制和远程服务调用机制分析
《Android上玩玩Hook?》: http://blog.csdn.net/yzzst/article/details/47318751
《进击的Android注入术<一>》:
http://blog.csdn.net/l173864930/article/details/38455951
极客学院——深入理解Android卷②:
http://wiki.jikexueyuan.com/project/deep-android-v2/powermanagerservice.html
本文链接:http://agehua.github.io/2017/07/08/android-binder-principle/