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

2018-04-13 | Android | #Android #Binder #IPC #读书笔记

第二篇读书笔记

拜读姜维大神的Android系统篇之—-免root实现Hook系统服务拦截方法

梳理了下思路,解决了疑惑

我们使用剪切板服务的时候是调用了ContextImplgetSystemService方法

ContextImplgetSystemService方法

    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

该方法将返回Object类型对象,我们将它强制转换为一个ClipboardManager,也就是说它返回了一个ClipboardManager供我们使用。而这个方法最终将构造一个ClipboardManager,在ClipboardManager构造的过程中,将获取远端Binder并调用IClipboard.StubasInterface方法转化为本地代理对象保存在其中,

ClipboardManager的一个构造函数

    /** {@hide} */
    public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
        mContext = context;
        mService = IClipboard.Stub.asInterface(
                ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
    }

获取远端Binder的操作其实是调用了ServiceManager中的getService方法,它返回的是一个远端Binder,实际上也就是一个BinderProxy(当然ServiceManager会把这个远端对象缓存到sCache中以应对频繁调用),姜维的文章里就是从这里切入,第一步先动态代理了这个BinderProxy

这里需要铭记一点,远端Binder需要调用StubasInterface方法转化为本地代理对象才能使用(上面说到在ClipboardManager的构造函数中,这一步骤ClipboardManager帮我们封装了这一操作)

ServiceManager中的getService方法

    /**
     * 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
     */
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(getIServiceManager().getService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

下面继续解析姜维的文章中hook的流程,在上一步的动态代理之后,拦截了被代理对象(BinderProxy对象)的queryLocalInterface方法,下面是

BinderProxyqueryLocalInterface的实现

    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }

可以看见它直接返回了null,而这个方法是在哪里被调用的呢,反编译framework.jar发现,是在IClipboard.Stub中,这里IClipboard就是用aidl生成的,和我们自己生成的差不多 看看

IClipboard.StubasInterface方法

    public static IClipboard asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin == null || !(iin instanceof IClipboard)) {
            return new Proxy(obj);
        }
        return (IClipboard) iin;
    }

回到正题,姜维的文章里拦截了queryLocalInterface以后,一开始我以为它又动态代理了一个叫做base的对象,因为这里new了一个HookBinderInvocationHandler,传入的第一个参数就是base突然有点蒙这个base是哪里冒出来的,看看上下文,发现是在第一个动态代理的Handler的构造函数里,传入了一个rawBinder,赋值给了成员变量base了,而这个rawBinder,就是第一次代理中,被代理的那个远端Binder,我就有点纳闷了,代理两次干啥?,仔细想,这只是个构造函数啊,我想传进去什么和我要动态代理什么对象没有关系呀。 于是翻回去看,动态代理的接口是this.iinterface,看了下第一次动态代理的Handler的构造函数,看到

this.iinterface = Class.forName("android.content.IClipboard")

仔细想想,这是要搞出来一个IClipboard啊,其实这个IClipboard我们前文接触过了,这里贴上IClipboard部分源码(主要看结构)

package android.content;
······
public interface IClipboard extends IInterface {

    public static abstract class Stub extends Binder implements IClipboard {
······
        private static class Proxy implements IClipboard {
······

梳理一遍, 第一次动态代理了远端Binder,Handler是IClipboardHookBinderHandler, 在第一次代理的Handler里面,拦截了queryLocalInterface方法, 这个方法是在asInterface里面调用的, 拦截以后,开始第二次动态代理, 用IClipboard这个接口合成了一个代理对象,Handler是HookBinderInvocationHandler, 把这个合成的代理对象return了!!!

没错,这里是关键,它直接把它作为queryLocalInterface方法的返回值return

看一下原来的BinderProxy中queryLocalInterface的实现

再看一下IClipboard.Stub的asInterface方法

asInterface方法里,我们合成的代理对象,赋值给了iin,接下来

        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin == null || !(iin instanceof IClipboard)) { //关键!!!!!
            return new Proxy(obj); //没走这!!!
        }
        return (IClipboard) iin; //走了这里,我们合成的代理对象被强制转换以后直接返回了,被用来之后进行剪切板的一些操作

哇,几乎哭出来,看了那么久终于懂了关键部分,为什么作者不标记一下呢 /(ㄒoㄒ)/~~

我们比较一下:

hook前:

[调用 getSystemService ]
 --> [ ClipboardManager 的构造函数]
 --> [间接调用了 ServiceManager 中的 getService ]
 --> [获得远端 Binder 对象]
 --> [调用 IClipboard.Stub 的 asInterface 并把远端对象传入]
 --> [获得 IClipboard.Stub.Proxy 对象]
 --> [后续使用]

hook后:

[Hook开始]
 --> [主动反射调用 ServiceManager 中的 getService 并动态代理远端对象]
 --> [正常调用开始]
 --> [调用 getSystemService ]
 --> [ ClipboardManager 的构造函数]
 --> [间接调用了 ServiceManager 中的 getService ]
 --> [获得第一次动态代理生成的对象]
 --> [调用 IClipboard.Stub 的 asInterface 并把远端对象传入]
 --> [拦截 queryLocalInterface 并合成 IClipboard 接口的代理对象]
 --> [返回合成的代理对象]
 --> [后续使用]

就这样,两次动态代理,第一次代理远端对象,拦截queryLocalInterface方法,第二次动态代理合成了一个实现了IClipboard接口的对象,骗过了ClipboardManager