DroidPlugin源码分析Hook过程

插件运行环境初始化过程中我们知道,Hook的初始化是在PluginHelper的initPlugin函数中通过调用PluginProcessManager.installHook来实现的。而在分析DroidPlugin Hook过程之前需要先简单了解一下Java的动态代理。

Java动态代理与之相关的一个类Proxy,一个接口InvocationHandler,一个函数invoke他们之间的关系。就通过DroidPlugin 的BinderHook类的部分代码来解释一下他们的关系;

abstract class BinderHook extends Hook implements InvocationHandler {
    private Object mOldObj;
    public BinderHook(Context hostContext) {
        super(hostContext);
    }
    @Override
public Object invoke(Object proxy, Method method, Object[] args){
        return method.invoke(mOldObj, args);
    }
    @Override
    protected void onInstall(ClassLoader classLoader) throws Throwable {
        Object proxiedObj = Proxy.newProxyInstance (clazz.getClassLoader(), ifs, this);  
    }
}

代理对象就是通过Proxy.newProxyInstance来创建的,其中clazz.getClassLoader(),是一被代理对象的类加载器,ifs代理对象需要代理的接口,this是BinderHook实现InvocationHandler的子类实例。
当调用proxiedObj所代理的某一个接口函数时,实现了InvocationHandler 接口的BinderHook的invoke会被调用,这时可以通过method的名字来判断,此函数是否需要对原来的代码做一些修改,比如说Activity是需要在AndroidMainfest文件中申明才能启动的,这个时候我们就可以在method名字为startActivity的函数里面对参数中的要启动Activity修改成我们在AndroidMainfest中预定义的Activity。

其实Hook就是动态代理来实现的,比如说这样一个场景:要Hook 系统ActivityManagerService服务的StartActivity函数,并修改Intent参数的内容。

第一步:准备一个实现了InvocationHandler接口的对象BinderHook,
第二步:context.getSystemService(Context.ACTIVITY_SERVICE)获取到ActivityManagerService服务对象,并把这个对象保存到BinderHook的成员变量mOldObj中
第三步: 通过Proxy.newProxyInstance创建ActivityManagerService服务对象代理对象asmProxy;
第四步: 也是最重要的一步,用asmProxy替换掉通过context.getSystemService获取的ActivityManagerService服务对象,这样以后程序每次通过context.getSystemService获取到的都是asmProxy。
当我们调用asmProxy.startActivity的时候,就会调用到BinderHook的invoke函数里面,然后我们通过函数的method 判断这个函数的名字是不是startActivity,如果是就从参数里面取出intent修改内容。再通过mOldObj调用它的startActivity函数。

对Hook有了大概的了解,接下来以IClipboardBinderHook为例看一下Hook相关类图:
这里写图片描述
通过类图先大概分析一下各个类之间的关系和作用:
Hook类 mEnable boolean类型 Hook开关决定是否执行Hook后的函数。
mHookHandles BaseHookHandle类 分发处理的作用。

BinderHook类 继承自Hook, 实现了InvocationHandler接口。
mOldObj Object类型 被Hook的真实对象。

IClipboardBinderHook类 继承自BinderHook, 主要是创建mOldObj, 创建要Hook的服务,创建分发对象IClipboardHookHandle。

BaseHookHandle类 sHookedMethodHandlers map对象 以要函数名为key, 以函数对应的处理对象(HookedMethodHandler) 为Value。它就是通过函数名在这个map里面找到对应的处理对象完成分发的。

HookedMethodHandler类 mFakedResult Object类型 保存函数处理后的结果。
mUseFakedResult boolean 类型 如果是处理后的结果,则为真。
它为修改原来结果提供逻辑实现。

MyBaseHookedMethodHandler类 继承自HookedMethodHandler 因为HookedMethodHandler提供了逻辑实现,这里主要为修改原来的结果提供具体的实现。

setPrimaryClip 和getPrimaryClip 在剪贴板服务中只是空实现。

有了Hook这些基本的了解以后,下面开始分析PluginProcessManager.installHook函数:
这个函数内部只是调用了HookFactory的同名函数installHook

第一步: HookFactory. installHook函数如下:

public final void installHook(Context context, ClassLoader classLoader){
        installHook(new IClipboardBinderHook(context), classLoader);
        installHook(new ISearchManagerBinderHook(context), classLoader);
        installHook(new INotificationManagerBinderHook(context), classLoader);
         ……(省略其他相同代码)
}

这里以IClipboardBinderHook为例继续分析installHook函数:

A 首先创建一个IClipboardBinderHook类的对象。
通过分析IClipboardBinderHook以及其父类BinderHook和Hook的构造函数,除了保存Context外在Hook的构造函数中调用了由IClipboardBinderHook实现的createHookHandle()函数。

    protected BaseHookHandle createHookHandle() {
        return new IClipboardHookHandle(mHostContext);
}

这个函数创建了一个IClipboardHookHandle对象。

B 通过分析IClipboardHookHandle以及父类BaseHookHandle构造函数,除了保存Context外还调用了init()函数:

    protected void init() {
        sHookedMethodHandlers.put("setPrimaryClip", new setPrimaryClip(mHostContext));
        sHookedMethodHandlers.put("getPrimaryClip", new getPrimaryClip(mHostContext));
        sHookedMethodHandlers.put("getPrimaryClipDescription", new getPrimaryClipDescription(mHostContext));
……(省略其他相同代码)
}

首先创建setPrimaryClip对象,然后以”setPrimaryClip”函数名为key保存到sHookedMethodHandlers中。这里主要是为Hook对应函数的分发处理做准备。当调用到剪贴板服务的setPrimaryClip函数式,就会从sHookedMethodHandlers以函数名取出setPrimaryClip对象做对应的处理。
`
第二步HookFactory 的同名函数installHook(Hook hook, ClassLoader cl)

    public void installHook(Hook hook, ClassLoader cl) {
            hook.onInstall(cl);
            synchronized (mHookList) {
                mHookList.add(hook);
            }
}

这个函数就是调用传入hook对象的onInstall函数,然后把hook对象加入到mHookList中。

第三步 BinderHook的onInstall函数:

    protected void onInstall(ClassLoader classLoader) throws Throwable {
        new ServiceManagerCacheBinderHook(mHostContext, getServiceName()).onInstall(classLoader);
        mOldObj = getOldObj();
        Class<?> clazz = mOldObj.getClass();
        List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
        Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
        Object proxiedObj = MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);
        MyServiceManager.addProxiedObj(getServiceName(), proxiedObj);
}

A 在这个函数里面首先创建一个ServiceManagerCacheBinderHook对象,并调用它的onInstall函数。
ServiceManagerCacheBinderHook的构造函数和前面IClipboardBinderHook构造函数类似,只是保存了要hook的serviceName,并打开hook开关,然后创建了一个ServiceManagerHookHandle对象并初始化了对” queryLocalInterface”接口函数的处理对象queryLocalInterface类的实例,

B ServiceManagerCacheBinderHook的onInstall函数:

    protected void onInstall(ClassLoader classLoader) {
        Object sCacheObj = FieldUtils.readStaticField(ServiceManagerCompat.Class(), "sCache");
        if (sCacheObj instanceof Map) {
            Map sCache = (Map) sCacheObj;
            Object Obj = sCache.get(mServiceName);
                sCache.remove(mServiceName);
                IBinder mServiceIBinder = ServiceManagerCompat.getService(mServiceName);
                if (mServiceIBinder != null) {
                    MyServiceManager.addOriginService(mServiceName, mServiceIBinder);
                    Class clazz = mServiceIBinder.getClass();
                    List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
                    Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
                    IBinder mProxyServiceIBinder = (IBinder) MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);

                    sCache.put(mServiceName, mProxyServiceIBinder);
                    MyServiceManager.addProxiedServiceCache(mServiceName, mProxyServiceIBinder);
                }
        }
    }

首先是通过FieldUtils.readStaticField获取ServiceManagerCompat内的静态成员变量sCache,其实ServiceManagerCompat.Class()获取的就是ServiceManager.class,也就是说这里sCache就是ServiceManager的静态成员变量。(sCache是做什么的等先把这段代码分析清楚再来解释)

然后检查sCache是否包含mServiceName(构造函数保存的)为key的IBinder服务代理对象。如果有就先删除掉,然后再以mServiceName为参数,通过ServiceManager的getService获取IBinder服务代理对象mServiceIBinder。并把这个对象保存到MyServiceManager的成员变量mOriginServiceCache中。

接下来就获取mServiceIBinder的Class在获得需要动态代理的接口,以this(ServiceManagerCacheBinderHook)为invocationHandler,调用MyProxy.newProxyInstance创建出mProxyServiceIBinder动态代理对象,并以mServiceName为可以将动态代理对象保存到sCache中。最后再把mProxyServiceIBinder保存到MyServiceManager的成员变量mProxiedServiceCache中。

到此先总结一下A B这两段做什么:
1: 在实现了InvocationHandler的类ServiceManagerCacheBinderHook的构造函数中创建了ServiceManagerHookHandle对象并指定了函数名字为” queryLocalInterface”的处理对象.
2: 通过ServiceManagerCacheBinderHook类的对象为参数创建的动态代理对象mProxyServiceIBinder 被放到了sCache中.

也就是说下一次通过mServiceName从sCache中取出来的就是mProxyServiceIBinder,当调用mProxyServiceIBinder. queryLocalInterface函数时,就会调用到ServiceManagerHookHandle为” queryLocalInterface”指定的类对象中。

C 继续分析BinderHook的onInstall函数下面的代码

        mOldObj = getOldObj();
        Class<?> clazz = mOldObj.getClass();
        List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
        Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
        Object proxiedObj = MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);
        MyServiceManager.addProxiedObj(getServiceName(), proxiedObj);

1: mOldObj前面有过解释主要保存被代理的对象,这里就是剪贴板服务的代理对象。
IClipboardBinderHook的getOldObj()函数:

    public Object getOldObj() throws Exception{
        IBinder iBinder = MyServiceManager.getOriginService(CLIPBOARD_SERVICE);
        return IClipboardCompat.asInterface(iBinder);
}

首先通过MyServiceManager.getOriginService(CLIPBOARD_SERVICE)获取剪贴板服务的iBinder对象,这个对象就是在ServiceManagerCacheBinderHook的onInstall函数中放进去的被代理的对象mServiceIBinder,然后通过IClipboardCompat.asInterface(iBinder)创建剪贴板服务的代理对象。

2: 获得mOldObj及剪贴板服务代理对象以后,就是通过他的类获取要动态代理需要代理的接口,接着以实现了InvocationHandler接口的类IClipboardBinderHook为参数调用MyProxy.newProxyInstance创建剪贴板服务代理对象的动态代理对象proxiedObj。然后调用MyServiceManager.addProxiedObj函数把proxiedObj以serviceName为key保存到mProxiedObjCache.

至此总结一下第一步和第三步的C段代码做了什么:
1 在实现了InvocationHandler接口的类IClipboardBinderHook的构造函数中,创建了IClipboardHookHandle对象,在IClipboardHookHandle对象中初始化了剪贴板服务的各个函数名字对应的处理对象。

2 获取了剪贴板服务的代理对象,并保存在IClipboardBinderHook的成员变量mOldObj中,并以实现了InvocationHandler接口的类IClipboardBinderHook的对象为参数创建剪贴板服务的动态代理对象proxiedObj。然后调用MyServiceManager.addProxiedObj以serviceName为key保存在MyServiceManager的成员变量mProxiedObjCache中。

如果我们从MyServiceManager的成员变量mProxiedObjCache获取到proxiedObj,然后通过proxiedObj调用剪贴板服务的某一个函数接口是,就会调用IClipboardHookHandle为这个函数指定的处理对象来处理。
也就是说,当如果我们让通过ServiceManager的getService返回的是存在mProxiedObjCache 变量中的proxiedObj对象,那么剪贴板服务就算是被劫持了!

如何让ServiceManager的getService函数返回我们劫持的剪贴板服务呢?
其实ServiceManagerCacheBinderHook的onInstall函数中已经帮我们做了这个事情,
不信?现在调用就去ServiceManager.getService(“clipboard”)来获取剪贴板服务。

第四步: 剪贴板服务的获取是通过ClipboardManager中的getService函数:

    public class ClipboardManager {
        private static IClipboard sService;
        private Context mContext;
        static private IClipboard getService() {
            if (sService != null) {
                return sService;
            }
            IBinder b = ServiceManager.getService("clipboard");
            sService = IClipboard.Stub.asInterface(b);
            return sService;
        }
    ……省略部分代码
    }

A 如果sService为空的情况下,首先会调用ServiceManager.getService(“clipboard”);来获取剪贴板服务的IBinder对象。

B ServiceManager.getService函数:

    public static IBinder getService(String name) {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        return null;
}

sCache原来是缓存服务的IBinder对象的,而且此时从sCache中是可以找到剪贴板服务名字(serviceName)对应的IBinder对象的,因为第三步中:在ServiceManagerCacheBinderHook的onInstall函数中,我们创建了这个IBinder的动态代理对象mProxyServiceIBinder并且替换了原本缓存在sCache 的IBinder对象。
代码如下:sCache.put(mServiceName, mProxyServiceIBinder);
也就是说这里获取到的service 不会空,而且就是mProxyServiceIBinder这个动态代理对象。

C 这样就回到ClipboardManager 的getService函数中,调用IClipboard.Stub.asInterface(b)来获取剪贴板服务的代理对象。
asInterface函数如下:

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

这个函数里面会调用obj.queryLocklInterface函数来获取剪贴板服务对象,如果返回为空则以obj为参数创建一个剪贴板服务的代理对象。

D queryLockInterface函数:
Obj就是刚刚从ServiceManager的成员变量sCache中获取的mProxyServiceIBinder这是一个动态代理对象,当我们调用它的queryLockInterface函数时,会调用到ServiceManagerCacheBinderHook的invok函数:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            IBinder originService = MyServiceManager.getOriginService(mServiceName);
            if (!isEnable()) {
                return method.invoke(originService, args);
            }
            HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
            if (hookedMethodHandler != null) {
                return hookedMethodHandler.doHookInner(originService, method, args);
            } else {
                return method.invoke(originService, args);
            }
}

1 这个函数首先通过mServiceName获取保存在MyServiceManager的被代理对象originService,如果Hook开关关闭了,则直接调用originService的queryLockInterface函数。

2 如果Hook开关打开,这调用mHookHandles. getHookedMethodHandler()看函数名为queryLockInterface有没有对用的处理对象。第三步A段代码分析中有分析过,在ServiceManagerCacheBinderHook的构造函数中创建了mHookHandles这个对象并初始化了对” queryLocalInterface”接口函数的处理对象queryLocalInterface类的实例,
也就是说这里会调用queryLocalInterface类是例的doHookInner函数;

3 queryLocalInterface是继承自HookedMethodHandler,他的doHookInner函数如下

public synchronized Object doHookInner(Object receiver, Method method, Object[] args) throws Throwable {
            mUseFakedResult = false;
            mFakedResult = null;
            boolean suc = beforeInvoke(receiver, method, args);
            Object invokeResult = null;
            if (!suc) {
                invokeResult = method.invoke(receiver, args);
            }
            afterInvoke(receiver, method, args, invokeResult);
            if (mUseFakedResult) {
                return mFakedResult;
            } else {
                return invokeResult;
            }
}

mUseFakedResult 标记劫持服务的函数后是否有自己的处理。
mFakedResult保存自己处理的结果.
初始化这两个变量之后,调用beforeInvoke():

4 queryLocalInterface的beforeInvoke函数:

protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) {
Object proxiedObj = MyServiceManager.getProxiedObj(mServiceName);
    setFakedResult(proxiedObj);
}

这里省略了部分代码,通过mServiceName从MyServiceManager的成员变量mProxiedObjCache中去取出 proxiedObj.其实取出来的proxiedObj对象就是在第三步C段分析中创建的剪贴板服务的动态代理对象proxiedObj。
接下来调用setFakedResult函数把proxiedObj保存到mFakedResult,设置mUseFakedResult为True;

5 回到HookedMethodHandler的doHookInner
在调用queryLocalInterface的afterInvoke,这里是空实现,什么都没有做。
这个时候因为mUseFakedResult 如是返回了mFakedResult对象。就这样ServiceManagerCacheBinderHook的onInstall函数帮我们完成了,通过ServiceManager 的getService函数返回剪贴板服务的动态代理对象。
至此剪贴板服务就被Hook了。

到这里这个Hook初始化的过程就分析完了。
最后总结一下Hook 剪贴板服务的流程,其实就两步:
1 首先把ServiceManager的成员变量sCache内部原本的剪贴板服务的IBinder对象替换成动态代理的IBinder对象。这样在调用动态代理对象IBinder对象的queryLocalInterface函数时,好返回我们动态代理的剪贴板服务代理对象。
2 获取剪贴板服务的代理对象,然后创建其动态代理对象,保存起来,等待动态代理IBinder对象调用queryLocalInterface来获取它。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值