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来获取它。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
大概半年之前,看过鸿洋大神的一篇博客 Android 自定义控件玩转字体变色 打造炫酷ViewPager指示器 他说大概想了32秒就知道了实现思路,这深深的刺痛了我。最近又看了一遍,决定做点什么 我要自定义的控件是一个盖世英雄, 它不仅仅是一个 Loading控件 ,同时还支持 进度条 (ProgressBar) 功能 。 它会在你需要的时候出现, 它支持 left , top , right , bottom 四个方向加载(变色),最重要的是,它可以是 文字 ,也可以是 图片 ,能够满足开发者一切需求。
首先为权限: uses-permission android:name="android.permission.INTERNET" / uses-permission android:name="com.android.vending.BILLING" / uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" / uses-permission android:name="android.permission.AC
Day01 html与css基础入门 1.html的常见标签和实战 1.1 a标签 a href=#> 1.2 img标签 img src="plmm.jpg" width="100px" height="200px" alt="图片信息丢失!"/// alt属性的值表示当图片找不到时显示的文字信息 1.3 列表标签 ol type="I" start="1" li我是天才1号/li li我是天才2号/li li我是天才3号/li li我是天才4号/li/olul type="circle" li我是逗逼

EventBus 源码分析 - 2016-07-23 14:07:10

0. 前言 EventBus 是一款针对Android优化的发布/订阅事件总线。主要功能是替代 Intent , Handler , BroadCast 在 Fragment , Activity , Service ,线程之间传递消息。优点是开销小,代码更优雅,以及将发送者和接收者解耦。此文将对最新的 EventBus 3.0 的源码进行简要的分析。 1. 用法回顾 EventBus 3.0 的用法较之前的版本有所变化,它使用了最近较为流行的注解形式取代以前的 onEvent 开头作为方法名,但使用步骤

Qt之资源系统 - 2016-07-23 14:07:10

简述 Qt 的资源系统用于存储应用程序的可执行二进制文件,它采用平台无关的机制。当你的程序总需要这样的一系列文件(图标、翻译文件等)并且不想冒丢失某些文件的风险时,这就显得十分有用。 资源系统基于 qmake、rcc(Qt 资源编译器) 和 QFile 之间的紧密合作。 简述 资源集合文件qrc 外部二进制资源 内编译资源 压缩 在程序中使用资源 在库中使用资源 更多参考 资源集合文件(.qrc) 与程序相关的资源在被指定在一个 .qrc 文件中,其基于 XML 的文件格式列出了磁盘上的文件,可以为它们指
一.前提条件 1.纯熟扎实的语言基础   如果你学java,却对反射、泛型、注解一直半解,还是不要去读什么框架了,回去把java基础打扎实反而对你自身更有益。 2.UML能力   在软件工程中,UML在软件的不同生命周期阶段扮演着非常重要的角色,没有好的UML水平,面对大型的项目源码会束手无策。 3.对业务的理解   如果你要阅读的项目业务性比较强,事先对业务有一定的了解是必须的。 4.设计模式、重构的掌握   编程语言什么的没什么好说。着重提一个:设计模式由于Android源代码用到各种各样的设计模式,
在上一篇博文《 LTE下行物理层传输机制(7)-DCI2格式和预编码矩阵的选择 》中已经提到,如果当前UE的传输模式是TM4,且可以执行空分复用(一个PDSCH信道传输2个TB块),那么需要采用DCI2格式来承载控制信息域,使用的预编码矩阵需要参考UE反馈的PMI值,因此属于闭环性质的空分复用。相应的,LTE系统中也有一种开环的空分复用: 如果当前UE的传输模式是TM3,且可以执行空分复用,那么此时PDCCH需要采用DCI2A格式发送,这时的空分复用就属于开环性质的空分复用,不需要参考UE反馈的PMI值。
好久没写android的博客,最近在做一个android的项目,里面用到我们经常用的一个控件就是对话框,大家都知道android自带的对话框是很丑的,android5.x之后除外.所以就出现了自定义view,自己定义美观的对话框.好我们就来自定义对话框. 整体思路:定义一个类然后去继承Dialog类,然后重写相应的构造器方法.大家都知道一般的对话框的创建过程都是来一个AlertDialog.Builder对象,然后使用一些set方法来设置标题内容以及设置一些自定义的view和点击的Button以及相应的点
1.android 的UI线程阻超过5秒就会引发ANR(Application not responding)异常,如果等待超过3秒,你就会失去用户。 2.在android中组件的启动线程被称为主线程(也称UI线程),一般不在这线程中进行耗时的工作,所以我们将线程分为两种,分别是main thread和worker thread,当应用程度运行是时,系统默认的启动线程就是主线程,主要用来加载UI,完成和用户间的交互,所有这些都在同一个线程中进行,所以不能在这个线程中进行耗时工作,不能阻塞UI,androi
接触过自定义控件的开发者一看,笑了,立马关了网页。但是…你真的知道怎么绘制居中文本吗? 我不会?开玩笑,不就是: X=控件宽度/2 - 文本宽度/2;Y=控件高度/2 + 文本宽度/2 好吧,那我试一下。 1.自定义控件基本步骤 自定义View的属性 在View的构造方法中获得我们自定义的属性 #重写onMesure # 重写onDraw OK,简单,直接干起来。 1. 自定义View的属性 按照最简单的来,属性有:文本,文本颜色,文本大小。 我们在 /value/attrs.xml 中这么写: ?xml