360 Android插件(DroidPlugin)无需安装运行APK的原理是什么,用途都有哪些?
12 个回答
是时候回答一下这个问题了 - -!
我写了一系列的文章来介绍DroidPlugin,到目前为止最核心的技术已经讲解完毕:
不得不说,这是一个非常优秀的插件框架;值得每一个想要深入FrameWork层的人学习。
我还只看了Hook部分的代码,自己写了篇总结,分享下吧。
一、入口
所有的Host Application都要继承于com.morgoo.droidplugin.PluginApplication,仅仅一步,PluginApplication类为我们完成了所有工作。
初始化(Application.onCreate)中调用PluginHelper.getInstance().applicationOnCreate。
我们的Host Application的初始化工作是在PluginHelper完成的,注意PluginHelper继承于ServiceConnection!,那自然要看onServiceConnected发生了什么,
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
PluginProcessManager.setHookEnable(true, true);
}
仅仅是标记所有Hook的enable flag为true。
下面开始探索PluginHelper.getInstance().applicationOnCreate
二、进程管理器安装钩子
框架首先委托HookFactory安装钩子:
HookFactory.getInstance().installHook(hostContext, null);
此处我们可以看到安装了很多钩子
而我们在源码中可以看到,installHook主要是调用了
hook.onInstall(ClassLoader );
上面的这些IClipboardBinderHook等具体钩子均继承于BinderHook,而BinderHook继承于Hook,Hook里面有两个重要的abstract方法:
protected abstract BaseHookHandle createHookHandle();
protected abstract void onInstall(ClassLoader classLoader) throws Throwable;
而BinderHook就实现了Hook的onInstall方法!
我们看到了新的类,ServiceManagerCacheBinderHook!
在分析这个类之前,我要简单说一下系统Service的Binder获取机制,其实我们每次获取系统Service的Binder时,Android会首先尝试从一个HashMap来读取,这个HashMap叫做sCache,Key是服务的名称。
下面,进入分析ServiceManagerCacheBinderHook的正题!
ServiceManagerCacheBinderHook.onInstall尤为关键。
在ServiceManagerCacheBinderHook.onInstall中首先从android.os.ServiceManager通过反射读出sCache(上文中说过,sCache就是一张存放Binder对象的缓存哈希表)。
下面,一步步分析ServiceManagerCacheBinderHook.onInstall做了什么。
1、 读取正常的ServiceIBInder
读取正常的Service IBinder有两个作用,其一是为了将系统的Ibinder缓存起来,以备其他用途;其二是获取Service相应IBinder的Class,我们知道了IBinder的具体类型才能在下一步中进行偷梁换柱!
这个clazz就是IBinder的具体Class。
2、 偷梁换柱!
当然是对sCache偷梁换柱!我们对上一步的clazz进行动态代理!将进行代理过的IBinder放入sCache中!
最后,我们将做过手脚的代理对象缓存起来!
3、 对Binder进行Hook!
在上一步分析中,我们可以看到,最终IBinder是通过动态代理的方式来进行偷梁换柱的,那具体的方法执行必定在invoke中。
果然,当IBinder在调用方法时受到了劫持,在mHookHandles中查找方法相应的HookedMethodHandler,最终调用劫持后的方法!
那么问题来了,mHookHandles从哪儿来的呢?
mHookHandles的赋值操作发生在Hook.createHookHandle()中,而这个方法是抽象的!
我们在具体HookHandler类中对Service Ibinder里面的方法进行复写,这样做只不过为了通过动态代理的方式进行松耦合!(你如果对每个Service IBinder进行继承并复写相关方法,当然也可以)
4、 对Binder对应的Proxy进行Hook!
注意,在getOldObj()函数中,我们获取到正常的Binder Proxy对象!
以便于我们下面对Proxy也进行Hook
5、 最重要的一点:我们为什么要对Binder以及Proxy进行Hook?
因为我们的插件并没有被真正的安装!并没有自己独立的进程!也没有自己独立的Context!所以,我们要将插件所有的工作委托到Host Application来做!调用方法时,所有Context参数即为我们Host Application的Context,所有PackageName参数即为我们Host Application的PackageName,我们通过动态代理在调用这些方法之前将Context/PackageName参数全换掉,达到瞒天过海的目的!
至此,我们对Hook的分析可以到一段落了。
三、判断是Host还是Plugin以决定是否进行Hook
四、初始化包管理器Service
这只是对Hook部分的分析。
DroidPlugin还有自己的AMS和PluginPackageManager系统,这两部分更为艰深,我也在学习中。
最后,非常感谢奇虎360能开源出这么诚意满满而且代码质量非常高的框架,供我们参考和学习,从源码中可以看出,作者花费了很多力气来翻看源码以寻找Hook点,也花费很多精力来做一些特殊机型的适配。
感谢奇虎360对DroidPlugin的开源!