360 DroidPlugin框架 Hook代理的实现方式

前几天看到一个开源的Android 动态加载的框架,就是360团队开源的DroidPlugin(小弟在这里向前辈们的开源精神点个赞!)个人对插件化开发很感兴趣,于是就clone了一份源码读了一下,感觉受益匪浅,下面来介绍一下DroidPlugin中的对系统API进行代理,实现对程序请求的拦截和处理


DroidPlugin简介我就不废话了,这篇文章重点讲框架中的Hook(欺瞒系统api)是怎么实现的,如何做到在系统api得到请求之前进行拦截并做出处理的,建议读者朋友也clone一份源码,可以实际去看看实现过程

DroidPlugin源码:https://github.com/Qihoo360/DroidPlugin

前面所说的hook其实就是Java的代理设计(没有了解过的朋友可以去了解一下 java设计模式的代理模式,重点了解动态代理)
它的实现方式是通过反射得到系统api的方法,然后在方法执行之前做出自己的逻辑,之后可以决定 继续执行原方法 或 放弃执行仅使用自己的逻辑,然后将这些被处理过之后的方法逻辑单独封装成一个“方法类”,然后创建这些方法的实例(被伪装后的方法,之后说到的“代理”和“伪装”基本是一个意思),再用这些实例替换掉系统api的中原来的方法(的逻辑),从而得到一个新的api实例(被伪装后的api对象),然后用这个伪装的实例去替换掉当前系统中已创建的实例(需要代理的api的实例在初始化时被以ApplicationContext的身份创建),之后在程序中向api发送请求自然会被这个实例收到,然后这个请求就会按照之前设计好的逻辑被处理。

下面来看看源码目录:

这里写图片描述

本片文章主要介绍的是”com.morgoo.draoidplugin.hook”这个包的内容,其子包分别为:

packages:
binder:实现对不同用途Binder的代理
handle:伪装方法类,代理过程的实现,即对原方法的执行和结果处理的过程
proxy:代理类,即使用 伪装api 替换 系统api 的执行者
xhook:数据库的代理

CLass:
BaseHookHandle:所有伪装类的基类,负责规范伪装类对方法逻辑处理的过程
Hook:所有代理类的基类,负责规范对api的代理过程
HookFactory:代理类工程,负责统一管理被创建的代理类实例

(我会尽量把注释写的详细,过程也会讲的很细)
首先看所有代理类的基类 Hook.java 源码如下:

public abstract class Hook {
    //是否需要使用使用代理功能
    private boolean mEnable = false;

    protected Context mHostContext;
    //方法代理实例管理类,负责存储当前的代理类需要的伪装方法
    protected BaseHookHandle mHookHandles;

    public void setEnable(boolean enable, boolean reInstallHook) {
        this.mEnable = enable;
    }

    public final void setEnable(boolean enable) {
        setEnable(enable, false);
    }

    public boolean isEnable() {
        return mEnable;
    }

    //创建时得到Context  并 将api中需要的伪装方法创建出来
    protected Hook(Context hostContext) {
        mHostContext = hostContext;
        //在Hook的实现类被创建实例时,会将其封装的代理方法创建实例并保存在一个Map中
        mHookHandles = createHookHandle();
    }

    //将具体实现类封装的代理方法创建实例并保存在一个Map中
    protected abstract BaseHookHandle createHookHandle();

    //执行伪装 即 用伪装对象替换掉原api实例
    protected abstract void onInstall(ClassLoader classLoader) throws Throwable;
    //取消伪装
    protected void onUnInstall(ClassLoader classLoader) throws Throwable {

    }
}

我们先从onInstall()方法说起,这是一个抽象方法,所以每一个Hook的实现类都需要实现它,这个方法是对一个api进行代理操作的入口,在Application创建时 会对所有的hook类进行初始化,就是使用HookFactory(这个类主要就是对各个不同的代理类做集中管理) 调用所有hook类的onstall(),这样一来,程序一运行起来,所有想要代理的api都会被被创建实例并完成伪装。

Hook的实现 我就以对 IActivityManager 的代理的过程来介绍,因为实现的代理较多,所以框架中大部分代理类使用了 “动态代理” ,这里就介绍动态代理的实现, 首先来看看实现动态代理的基类 ProxyHook,源码如下:

public abstract class ProxyHook extends Hook implements InvocationHandler {
    //被代理的对象
    protected Object mOldObj;

    public ProxyHook(Context hostContext) {
        super(hostContext);
    }

    //得到代理对象
    public void setOldObj(Object oldObj) {
        this.mOldObj = oldObj;
    }

    //实现方法代理
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        try {
            //如果为false表示不需要使用代理,则直接执行原方法
            if (!isEnable()) {
                return method.invoke(mOldObj, args);
            }
            //在被封装过的代理方法中 查找此方法的代理类
            HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
            //如果有此方法的代理那么执行找到的方法
            if (hookedMethodHandler != null) {
                return hookedMethodHandler.doHookInner(mOldObj, method, args);
            }
            //否则正常执行原方法
            return method.invoke(mOldObj, args);

        } catch (InvocationTargetException e) {
           //略... 这里主要介绍实现方式,不考虑其他情况
        }
    }
}

ProxyHook类是一个抽象类,继承于Hook并实现了InvocationHandler接口(动态代理接口),表示它是一个动态代理类,但它并没有实现Hook类的的onstall()方法,所以它将onstall()继续下放到其子类中实现,保持Hook规定的代理入口。

ProxyHook的作用是动态的去处理不同子类的代理需求,invoke()方法是从InvocationHandler 接口实现的方法,其作用就是改写被代理类中的方法逻辑,并返回一个新的实例,这个实例就是伪装过的类的对象,它的方法的 执行 和 结果 都由我们来控制,这就实现了代理。

在讲它的实现类 之前需要先讲解一下HookedMethodHandler 类,这个类就是实现改写方法逻辑的类,在上边的代码中可以看到这一句:

HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);

这句代码的作用就是根据invoke()方法的参数method(方法),去我们准备好的所有代理方法中去找(mHookHandles是一个存储伪装方法的map),如果找到同名同参的方法,就表示之前有准备代理这个方法,这时就可以放弃原方法,执行刚刚找到的方法,如此便使用了这个方法新的逻辑,接着我们看看HookedMethodHandler 类的源码:

public class HookedMethodHandler {

    private static final String TAG = HookedMethodHandler.class.getSimpleName();
    protected final Context mHostContext;

    private Object mFakedResult = null;
    private boolean mUseFakedResult = false;

    public HookedMethodHandler(Context hostContext) {
        this.mHostContext = hostContext;
    }

    /**
     *  对指定的方法进行代理
     *  receiver:方法所属对象
     *  method: 方法名
     *  args[]:方法参数
     * */
    public synchronized Object doHookInner(Object receiver, Method method, Object[] args) throws Throwable {
        long b = System.currentTimeMillis();
        try {
            mUseFakedResult = false;//是否使用伪装结果,默认为否
            mFakedResult = null;//伪装方法返回的结果
            //是否执行原方法(执行自己的逻辑,完成后返回一个boolean指示是否还需要执行原方法,true为不执行)
            boolean suc = beforeInvoke(receiver, method, args);
            Object invokeResult = null;
            if (!suc) {
            //如果为false,表示需要执行原方法,得到原方法的返回值
                invokeResult = method.invoke(receiver, args);
            }
            //原方法执行之后的操作
            afterInvoke(receiver, method, args, invokeResult);
            //是否使用代理之后的伪造结果
            if (mUseFakedResult) {
                return mFakedResult;
            } else {
                return invokeResult;
            }
        } finally {
            long time = System.currentTimeMillis() - b;
            if (time > 5) {
                Log.i(TAG, "doHookInner method(%s.%s) cost %s ms", method.getDeclaringClass().getName(), method.getName(), time);
            }
        }
    }

    public void setFakedResult(Object fakedResult) {
        this.mFakedResult = fakedResult;
        mUseFakedResult = true;
    }

    /**
     * 在方法被调用之前执行,先执行自定义的逻辑,如果返回true,则不执行原始的方法,否则执行原始方法
     */
    protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
        return false;
    }
    /**
     * 在方法被调用之后执行
     */
    protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
    }

    public boolean isFakedResult() {
        return mUseFakedResult;
    }

    public Object getFakedResult() {
        return mFakedResult;
    }
}

这个类就是方法的封装类,它针对一个方法,它的每一个实现类都是一个方法的代理封装,它提供的beforeInvoke()、afterInvoke()由方法封装的具体实现类实现,作用分别是在原方法执行前后加上自己的处理(这就是代理模式的根本逻辑,控制一个方法的结果),它还提供一个结果处理方法doHookInner(),负责对beforeInvoke()、afterInvoke()的调用。

beforeInvoke()在执行自己的逻辑之后需要返回一个boolean值,用来指示是否还需要使用原方法,举个例子:

现在有一个方法,实现一个加法,返回结果:

public int sum(int a,int b){
    return a+b;
}

现在我为它写一个代理类,现在我想让这个方法只接受10以上的参数,不然返回-1,那么重写beforeInvoke()方法:

Class Sum extends HookedMethodHandler {
    @Override
     protected boolean beforeInvoke(Object receiver, Method method, Object[] args){
           //确定参数合法
           if (args != null && args.length > 2) {
                   //得到两个参数的值
                    Integer a= (Integer ) args[0];
                    Integer b= (Integer) args[1];
                    if(a>=10 && b>=10){
                        /**
                        *  如果参数都大于10返回true,这时在上层doHookInner()执行beforeInvoke()后得到了true的指示
                        *  那么会继续执行原方法,实现10+10的逻辑 然后返回20给用户
                        */
                        return true;
                    }else{
                        /**
                         * 如果有参数小于10,就给HookedMethodHandler中的mFakedResult赋值为-1
                         *  mUseFakedResult也同时被改为了false,这时在doHookInner()中执行beforeInvoke()后得到了false的指示
                         * 于是放弃执行原来的sum()方法,继续执行afterInvoke(),这里没有之后的逻辑处理,然后继续执行
                         * 判断mUseFakedResult != true成立,返回mFakedResult,用户就得到了-1
                         */
                        setFakedResult(-1);
                        return false;
                    }
            }
     }
}

这样我们就完成了对一个方法逻辑和结果的控制,这就是对一个方法的代理封装,现在我们来看源码中的实际应用,还是以IActivityManager的为例,找到handle包中的IActivityManagerHookHandle类,源码如下:

public class IActivityManagerHookHandle extends BaseHookHandle {

    private static final String TAG = IActivityManagerHookHandle.class.getSimpleName();

    public IActivityManagerHookHandle(Context hostContext) {
        super(hostContext);
    }

    private static class startActivity extends HookedMethodHandler {

        public startActivity(Context hostContext) {
            super(hostContext);
        }

    @Override
    protected void init() {
        //将封装好的startActivity()存入 从父类继承的Map
        sHookedMethodHandlers.put("startActivity", new startActivity(mHostContext));
         //将封装好的startActivityAsUser()存入 从父类继承的Map
        sHookedMethodHandlers.put("startActivityAsUser", new startActivityAsUser(mHostContext));
         //将封装好的startActivityAsCaller()存入 从父类继承的Map
        sHookedMethodHandlers.put("startActivityAsCaller", new startActivityAsCaller(mHostContext));
        //....略
    }
protected void doReplaceIntentForStartActivityAPILow(){//略...}
protected void doReplaceIntentForStartActivityAPIHigh(){//略...}
 @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //启动之前判断Activity的启动模式
            RunningActivities.beforeStartActivity();
            //sdk高版本和低版本分别处理
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                doReplaceIntentForStartActivityAPILow(args);
            }else{
                doReplaceIntentForStartActivityAPIHigh(args);
            }
            //在其父类中beforeInvoke默认返回false,表示不需要执行原有的startActivity()
            //这里用户的请求被上边两个方法处理后,将在这里结束
            return super.beforeInvoke(receiver, method, args);
        }
    }

}

在IActivityManagerHookHandle类中,有很多像startActivity一样内部类,都继承于HookedMethodHandler,选择性的实现了 beforeInvoke()和afterInvoke(),然后在其父类的init()方法中将这些类全部放入了名为sHookedMethodHandlers 的 map(sHookedMethodHandlers)中,它的每一个内部类都是一个封装好的方法

而 sHookedMethodHandlers 这个map 是IActivityManagerHookHandle从父类BaseHookHandle 中继承的成员。每一个BaseHookHandle的实现类,都会封装一些需要用到的方法,然后在init()中放入sHookedMethodHandlers,所以每一个伪装类被创建实例的时候,它封装的所有方法都会加入到map中,在需要使用的时候根据key来获取对象,现在回头看看ProxyHook,还记得的这句代码吗?

 //在被封装过的代理的方法 中查找此方法的代理类
HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);

现在来看这句代码的意思就很明确了,就是在map中查找有没有这个方法的代理封装,如果找到了,说明在之前为这个方法准备好了伪装对象,这里就可以放弃原方法的执行直接使用找到的伪装方法了(代理系统api并不是需要完全改写所有方法,只需要将我们会用到的方法选择性进行伪装,所以没有被代理的方法在这里会得到null,就会保持原有的逻辑执行)

这里要分清楚:
1. map中存储的是伪装类所有伪装方法的实例,一个元素就是一个方法
2. IActivityManagerHookHandle是并不是IActivityManager的伪装对象,它的只是将IActivityManager中我们需要代理的方法统一管理起来,在它被创建实例的时候会把自己的内部的所有方法类都创建实例然后放入map,这样map中就是IActivityManager所有需要伪装的方法

好了,现在准备工作都已经完成了,上面关于handle的内容都是为代理做的准备,把需要处理的方法全部封装好,然后统一保存起来备用(相当于JavaBean,也就是实体类,创建了很多实体类的对象,然后统一保存起来,现在就等被使用了)


下面就来讲解这些伪装对象是怎么被使用的,我们将分支退回到ProxyHook,回顾这个动态代理类的逻辑
1.通过setOldObj得到需要代理的对象(mOldObj),
2.在invoke()中,对mOldObj中的方法的结果进行处理,前提是是map(sHookedMethodHandlers)中有这个方法的伪装对象,如果没有,则正常执行原方法。

逻辑很清楚了,下面来看ProxyHook的具体实现类是怎么操作的,还是以IActivityManagerHook为例,代码如下:

public class IActivityManagerHook extends ProxyHook {

    private static final String TAG = IActivityManagerHook.class.getSimpleName();

    public IActivityManagerHook(Context hostContext) {
        super(hostContext);
    }

    @Override
    public BaseHookHandle createHookHandle() {
        return new IActivityManagerHookHandle(mHostContext);
    }

    @Override
    public void onInstall(ClassLoader classLoader) throws Throwable {
        //获取ActivityManagerNative的CLass
        Class cls = ActivityManagerNativeCompat.Class();
        //得到ActivityManagerNative实例中gDefault的值
        Object obj = FieldUtils.readStaticField(cls, "gDefault");
        //如果结果为null,创建一个实例,在进行获取
        if (obj == null) {
            //为gDefault创建一个实例
            ActivityManagerNativeCompat.getDefault();
            //再取一次gDefault的值
            obj = FieldUtils.readStaticField(cls, "gDefault");
        }
        //判断obj是否兼容IActivityManager
        if (IActivityManagerCompat.isIActivityManager(obj)) {
            //设置obj为代理对象(现在Obj就是系统的IActivityManager实例)
            setOldObj(obj);
            //得到obj的Class
            Class<?> objClass = mOldObj.getClass();
            //得到obj所有接口
            List<Class<?>> interfaces = Utils.getAllInterfaces(objClass);
            Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
            //执行代理,得到伪装后的实例
            Object proxiedActivityManager = MyProxy.newProxyInstance(objClass.getClassLoader(), ifs, this);
            //将ActivityManagerNativeCompat的gDefault字段替换为伪装后的字段
            FieldUtils.writeStaticField(cls, "gDefault", proxiedActivityManager);
            Log.i(TAG, "Install ActivityManager Hook 1 old=%s,new=%s", mOldObj, proxiedActivityManager);
        } else if (SingletonCompat.isSingleton(obj)) {
            //略...
    }
}

可以看到ProxyHook的实现类IActivityManagerHook,实现了由Hook类继承而来的createHookHandle() 和 onInstall() 方法,createHookHandle()负责准备工作,就是我们前面说的准备好伪装对象,onInstall() 负责执行替换的工作,就是操作方。

首先我们来看createHookHandle(),他是什么时候准备好伪装对象的? 再来重复一下,前面说了在程序启动创建Applcation的时候,通过HookFactory获得了所有伪装类的实例,并执行了它们的onInstall()方法,一步一步看,还是以IActivityManagerHook为例:

1.创建了实例:即调用构造方法,在构造方法有super(),即调用了父类ProxyHook的构造方法,ProxyHook中也调用了supper(),即Hook构造也被调用,在Hook的构造方法中调用了createHookHandle,并把结果赋给BaseHookHandle(保存伪装对象的map持有者)实例中的map中;这些都是在创建伪装类实例的时候完成的,这时每个伪装对象 的 伪装方法 都已经准备好了

————- IActivityManagerHook extends PeoxyHook ——————

public IActivityManagerHook(Context hostContext) {
        super(hostContext);
    }

   @Override
 public BaseHookHandle createHookHandle() {
        return new IActivityManagerHookHandle(mHostContext);
    }

—————— ProxyHook extends Hook ———————-

public ProxyHook(Context hostContext) {
        super(hostContext);
     }

————————— Hook ——————————–

protected Hook(Context hostContext) {
        mHostContext = hostContext;
        mHookHandles = createHookHandle();
    }

2.接着就是onInstall()方法,通过注释已经说明了整体过程,其中通过反射获取的gDefault字段就是系统的IActivityManager的实例,然后
1.通过setOldObj(obj)将其设置为了伪装对象
2.使用MyProxy.newProxyInstance(objClass.getClassLoader(), ifs, this)得到伪装后的api实例

/**
*   newProxyInstance对指定类型的指定方法 进行伪装
*   参数1:要使用的类加载器
*   参数2:被代理类实现的接口
*   参数3:实现了InvocationHandler接口的动态代理实例
*   
*   这里将会对objClass的所有方法使用InvocationHandler的invoke()对其方法结果进行处理
*   第三个参数使用this,应为它本身继承了ProxyHook类,ProxyHook由实现了InvocationHandler接口,所以这里实际使用的是ProxyHook实现的invoke()方法来进行方法伪装
*   最后会返回一个新的objClass实例,就是伪装后的实例,如此一来就调动了前面讲的一系列伪装操作,完成伪装并得到伪装后的api实例
*   然后需要做的就是将这个伪装的实例替换掉系统api的实例
*/
Object proxiedActivityManager = MyProxy.newProxyInstance(objClass.getClassLoader(), ifs, this);

3.使用FieldUtils.writeStaticField替换掉系统api的实例

/**
*   对一个对象中某个字段进行值替换
*   参数1:需要替换的字段类型
*   参数2:需要替换的字段名称
*   参数3:需要替换的新内容
*   这里将前面获得的系统IActivityManager的实例gDefault的值,替换为了刚刚得到的伪装对象proxiedActivityManager
*   这样用户在程序中对ActivityManager发送的请求就会被我们的proxiedActivityManager拦截到
*   然后在Handle中进行对请求我们自己的逻辑处理,至此,完成伪装
*/
FieldUtils.writeStaticField(cls, "gDefault", proxiedActivityManager);

有了这两步就完成了所有需要伪装的api的伪装工作,也就是说在程序的一开始,所有我们需要代理的api都已经完成了伪装工作!

猜你喜欢

转载自blog.csdn.net/Mr_Sk/article/details/50342857
360