Binder

Android进程间通信(IPC)机制Binder简要介绍和学习计划

个人感觉其实Binder机制应该是seandroid实现的核心载体,我们如何实现策略和上下文的检测都是通过Binder机制实现的。
这里写图片描述

看了下源码,好像大体的原理比较简单。不过这里面有很多细节,如果做起来肯定是一个巨大的工程。
我这里还是参看了老罗的分析过程:

    1. 打开/dev/binder文件:open("/dev/binder", O_RDWR);

    2. 建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

    3. 通知Binder驱动程序它是守护进程:binder_become_context_manager(bs);

    4. 进入循环等待请求的到来:binder_loop(bs, svcmgr_handler);

我们忽略其中各种的代码实现细节,因为如果没有目的的看确实好费劲,先有个大体的认识吧。通过以上流程,我们看到我们的ServiceManager的启动过程,其实到了内核中呢,程序的本质往往就会比较清晰。我们看到的大部分就是单纯的读写操作,映射关系等。

我们可以猜测到,这里我们Service manager通过不断循环检查我们client传递过来的数据的合理新和合法性,把Server的通过检测句柄传递给client。Server通过在Server Manager注册相应的调用实现被管理的相关过程。这一切的一切,其实还是比较清晰的,难点就是各种数据就够和权限检测机制的设计了,这需要强大的功底,可惜我没有那个功底,不然也不会自己私下研究了。不过我个人侧重于重点突击。

这里无非就是查询Binder句柄,传输找到service,还要通过Server Manager的管理。我现在需要找个传输实例一步一步的把整个过程走一边。

这里有个AIDL开发的例子,

public IBinder onBind(Intent intent) {
        return new IAdditionService.Stub() {
            /*
             * Implement com.android.hellosumaidl.IAdditionService.add(int, int)
             */
            @Override
            public int add(int value1, int value2) throws RemoteException {
                return value1 + value2;
            }
        };
    }

个人理解就是在一个服务接口,主程序里声明调用即可。
这个东西就是进程间通信的核心。但可惜客户端服务器写到一起了,没关系,这里还有个分开写的原则就是要在客户端定义一个相同的AIDL文件,通过过滤标签,然后就可以实现调用。好像也比较简单。这里我注意到了一个
service=LocalService.Stub.asInterface(binder);
的方法。这个方法在asinterface在源码中似乎看到过。我们可以断定也就是突破口。

这里复习下代理的概念,静态代理简单说其实就是把代理对象的方法从新封装在新的代理对象中,然后通过操作代理对象实现原对象的操作。动态代理就是利用反射机制封装了一套可以动态获取对象和其方法的代理模型,通过代理模型可以以参数的形式实现任意对象的代理对象的动态封装调用。不知说明白了么,就是实现的代理过程相似,但被代理的类是通过参数传递可以变化的。这应该就清楚了。

再回到上面的分析,AIDL在实现的过程中会自动生成响应的.java文件,可个文件其实就是我们直接和Binder通信的文件。通过查询binder中对应的服务句柄,我们就可以得到服务。服务会被保存在mRemote变量中。

文件中有这么一句调用 mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);其实就是调用了Server中的函数。

在服务端的实现如下:

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
    {  
        switch (code)  
        {  
            case INTERFACE_TRANSACTION:  
            {  
                reply.writeString(DESCRIPTOR);  
                return true;  
            }  
            case TRANSACTION_start:  
            {  
                data.enforceInterface(DESCRIPTOR);  
                java.lang.String _arg0;  
                _arg0 = data.readString();  
                boolean _result = this.start(_arg0);  
                reply.writeNoException();  
                reply.writeInt(((_result)?(1):(0)));  
                return true;  
            }  

处理完毕,将返回数据写入到reply包中。

那么客户端是如何获取Service的句柄的呢?
在写程序的时候,经常会使用getSystemService这个函数来获取一个XXXManager,例如我需要获得一个InputMethodManager,那么我会这么做:
InputMethodManagerwm=(InputMethodManager)context.getSystemService(“input_method”);

通过层层的代码回溯,最终发现
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
我们找到了服务,其实不管你封装多少,明确我们的目的,一直找下去就对了。

(1) Server所在进程启动后,需要将其中的Servic注册到ServiceManager,这样Client就可以通过ServiceManager找到对应的Binder引用,而ServiceManager自身的Binder引用时通过BinterInternal.getContextObject()函数获得

(2) 我们在写程序时,通过getSystemService获得XXXManager,在XXXManager中保存了一个IXXX变量,这个变量可能是Proxy类型,也可能是Stub类型,这个过程被asInterface函数屏蔽了。目的分别是实现远程调用和本地调用。

  • 这里可能还涉及到内核中的iO控制码,我个人理解比较简单。就是把控制码定义为特定的参数,我们在内核主线程中执行控制码对应的分发函数,内核编程的关键就是完成这个switch实现的分发函数。

binder_ioctl

然后呢,这个就是io控制函数,我相信这里面一定有这Binder通信的核心实现原理。
当然,其实我更关注这里有啥漏洞,不管暂时先看个大概吧。
frameworks/base/cmds/servicemanager/binder.c 这里有个looper,是有调用ioctl的。

猜你喜欢

转载自blog.csdn.net/bme314/article/details/79539700
今日推荐