360 Android插件(DroidPlugin)无需安装运行APK的原理是什么,用途都有哪些?

今天上网偶尔浏览到GibtHub上一个叫做DroidPlugin的360手机助手插件,不明觉厉,但很感兴趣。这个插件,对我们普通开发者,有什么价值和意…
关注者
477
被浏览
51,800

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的开源!