VirtualApp Hook 框架分析

作者: imilk 版权声明:本文图文为博主原创,转载请注明出处。 1、概述 对于插件化框架 Hook 机制是一个核心,那到底

作者: imilk

版权声明:本文图文为博主原创,转载请注明出处。

1、概述

对于插件化框架 Hook 机制是一个核心,那到底 Hook 是什么呢?怎么去理解插件化中的 Hook 呢?在我看来插件化中的 Hook 机制就是通过 反射注入动态代理 来实现的。

先来说说何为 反射注入 ,大家都知道依赖注入,其实反射注入算是依赖注入的一种,顾名思义,通过反射的方式将依赖对象注入目标对象。举个例子,想要替换掉 ActivityThread 中的 mInstrumentation

/*android.app.ActivityThread.java*/public final class ActivityThread { Instrumentation mInstrumentation; public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } ...}//Instrumentation代理类public class InstrumentationDelegate extends Instrumentation { private Instrumentation base; public InstrumentationDelegate(Instrumentation base) { this.base = base; }}public class ReflectInject{ public void reflectInject() throws Exception { // 根据全类名获取Class Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); // 获取无参的currentActivityThread函数 Method currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread"); // 调用currentActivityThread函数获取当前ActivityThread对象 currentActivityThreadMethod.setAccessible(true); Object currentActivityThreadObject = currentActivityThreadMethod.invoke(null); // 获取mInstrumentation字段 Field instrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); // 破坏封装获取对象 instrumentationField.setAccessible(true); Object instrumentationObject = instrumentationField.get(null); // 注入Instrumentation代理对象 if(!(instrumentationObject instanceof InstrumentationDelegate)) { instrumentationField.set(activityThreadClass, new InstrumentationDelegate((Instrumentation)instrumentationObject)); } }}

以上就是对于 mInstrumentation 的反射注入,当然凭借封装可以有更优雅的实现,这里为了方便展示过程粗暴直接。

关于 动态代理 大家可以参考 彻底理解 Java 动态代理 这篇文章,写得十分清晰。文章最后也提到了动态代理的局限性,动态代理无法支持对于非接口的类进行代理,所以在 Hook 时一般结合静态代理来特殊处理需要代理的类,比较典型的例子是 android.app.Instrumentation 的代理。好在 Android 系统服务大都通过 Binder 机制来实现的,而 Binder 机制的 C/S 架构对于接口的支持天然的好,这对于整个 Hook 框架中代理类实现的工作量来说就大大的减少了。

2、Hook 框架

我们知道 Hook 本身依赖反射机制,从上面示例上也可以看出,直接使用大量反射导致代码可读性、维护性变得非常差,从代码美观可读性、易维护性上来看,一个可读性强易维护的 Hook 框架显得尤为重要,目前众多开源的插件化框架中 VirtualApp 的 Hook 框架是最优秀的。为什么这么说呢,作者使用了基于注解的反射注入技术,合理的框架设计使得虽然 Hook 的对象非常多,代码却井井有条,不得不赞叹作者 lody 的巧妙构思,让人受益良多。

以下分析基于 master 分支 c493161 版本。

2.1 设计类图

又到了祭出法宝的时候了,废话不多说先看设计类图:

VirtualApp Hook 框架分析

点击放大查看高清无码大图

2.2 类图解析

首先作者设计了两个接口,一个是 Injectable ,这个接口比较简单,使实现这个接口的类都具备的注入的能力;

public interface Injectable { void inject() throws Throwable; boolean isEnvBad();}

另一个是 IHookObject ,使实现这个接口的类具备管理代理类的 Hook 函数 能力。

public interface IHookObject { void copyHooks(IHookObject from); Map<String, Hook> getAllHooks(); Hook addHook(Hook hook); Hook removeHook(String hookName); void removeHook(Hook hook); void removeAllHook(); <H extends Hook> H getHook(String name); Object getProxyInterface(); Object getBaseInterface(); int getHookCount();}

上面我们提到了 Hook 函数 这个概念,怎么理解这个概念呢,因为动态代理的调用是函数级别的,所以 Hook 相当于替换函数实现。再来看 Hook 这个抽象类,这个类定义了 Hook 的处理时机,以及提供一些 Hook 环境的依赖,实现类通过指定代理函数名,可以根据需要在 beforeCallcallafterCall 执行逻辑处理。所以, Hook 的实现类可以理解为代理函数的类象化。

public abstract class Hook { private boolean enable = true; public abstract String getName(); public boolean beforeCall(Object who, Method method, Object... args) { return true; } public Object call(Object who, Method method, Object... args) throws Throwable { return method.invoke(who, args); } public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable { return result; } public boolean isEnable() { return enable; } public void setEnable(boolean enable) { this.enable = enable; } ... @Override public String toString() { return "Hook${ " + getName() + " }"; }}

来看看抽象类 HookDelegate ,它是 IHookObject 的接口实现,在构造中通过 HookHandler 完成了动态代理,内部维护了 Hook 集合,代码如下。

public abstract class HookDelegate<T> implements IHookObject { private static final String TAG = HookDelegate.class.getSimpleName(); private T mBaseInterface; private T mProxyInterface; /** * 内部维护的Hook集合 */ private Map<String, Hook> internalHookMapping = new HashMap<String, Hook>(); @Override public Map<String, Hook> getAllHooks() { return internalHookMapping; } public HookDelegate(Class<?>... proxyInterfaces) { // 获取接口,完成动态代理 mBaseInterface = createInterface(); if (mBaseInterface != null) { if (proxyInterfaces == null) { proxyInterfaces = HookUtils.getAllInterface(mBaseInterface.getClass()); } mProxyInterface = (T) Proxy.newProxyInstance(mBaseInterface.getClass().getClassLoader(), proxyInterfaces, new HookHandler()); } else { VLog.d(TAG, "Unable to build HookDelegate: %s.", getClass().getName()); } } public HookDelegate() { this((Class[]) null); } protected abstract T createInterface(); @Override public void copyHooks(IHookObject from) { this.internalHookMapping.putAll(from.getAllHooks()); } // 添加 Hook 函数 @Override public Hook addHook(Hook hook) { if (hook != null && !TextUtils.isEmpty(hook.getName())) { if (internalHookMapping.containsKey(hook.getName())) { VLog.w(TAG, "Hook(%s) from class(%s) have been added, can't add again.", hook.getName(), hook.getClass().getName()); return hook; } internalHookMapping.put(hook.getName(), hook); } return hook; } ... /** * @return 包装后的代理对象 */ @Override public T getProxyInterface() { return mProxyInterface; } /** * @return 原对象 */ @Override public T getBaseInterface() { return mBaseInterface; } ... private class HookHandler implements InvocationHandler { // 动态代理,通过函数名找到对应的 Hook 函数,完成 beforeCall、call、afterCall 的调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Hook hook = getHook(method.getName()); try { if (hook != null && hook.isEnable()) { if (hook.beforeCall(mBaseInterface, method, args)) { Object res = hook.call(mBaseInterface, method, args); res = hook.afterCall(mBaseInterface, method, args, res); return res; } } return method.invoke(mBaseInterface, args); } catch (InvocationTargetException e) { Throwable cause = e.getTargetException(); if (cause != null) { throw cause; } throw e; } } }}

对于 HookBinderDelegate 这个类,继承自 HookDelegate 扩展了 IBinder 接口,借此方便处理系统 Binder 服务的代理。

public abstract class HookBinderDelegate extends HookDelegate<IInterface> implements IBinder { private IBinder mBaseBinder; public HookBinderDelegate(Class<?>... proxyInterfaces) { super(proxyInterfaces); init(); } public HookBinderDelegate() { super(); init(); } private void init() { mBaseBinder = getBaseInterface() != null ? getBaseInterface().asBinder() : null; addHook(new AsBinder()); } //此处通过反射替换系统服务 public void replaceService(String name) { if (mBaseBinder != null) { ServiceManager.sCache.get().put(name, this); } } //这里Hook asBinder函数,使该函数调用后返回代理Binder对象。 private final class AsBinder extends Hook { @Override public String getName() { return "asBinder"; } @Override public Object call(Object who, Method method, Object... args) throws Throwable { return HookBinderDelegate.this; } } ... public Context getContext() { return VirtualCore.get().getContext(); } ... @Override public IInterface queryLocalInterface(String descriptor) { return getProxyInterface(); } ... public IBinder getBaseBinder() { return mBaseBinder; }}

接在在来看 PatchDelegate 这个抽象类,它是 Injectable 的接口实现,依赖 @Patch@ApiLimit 注解将 Hook 类的添加进 Hook 集合;它的泛型为 IHookObject ,这就意味着 HookDelegate HookBinderDelegate 的实现类很容易通过泛型约束,并通过 inject 接口完成注入。

public abstract class PatchDelegate<T extends IHookObject> implements Injectable { protected T hookDelegate; protected Object baseObject; public PatchDelegate() { this(null); } public PatchDelegate(Object baseObject) { attachInterface(baseObject); } protected void attachInterface(Object baseObject) { this.baseObject = baseObject; this.hookDelegate = createHookDelegate(); onBindHooks(); afterHookApply(hookDelegate); } protected abstract T createHookDelegate(); protected void onBindHooks() { if (hookDelegate == null) { return; } // 通过 @Patch、@ApiLimit 注解将 Hook 函数添加至代理类的 Hook 集合 Class<? extends PatchDelegate> clazz = getClass(); Patch patch = clazz.getAnnotation(Patch.class); int version = Build.VERSION.SDK_INT; if (patch != null) { Class<?>[] hookTypes = patch.value(); for (Class<?> hookType : hookTypes) { ApiLimit apiLimit = hookType.getAnnotation(ApiLimit.class); boolean needToAddHook = true; if (apiLimit != null) { int apiStart = apiLimit.start(); int apiEnd = apiLimit.end(); boolean highThanStart = apiStart == -1 || version > apiStart; boolean lowThanEnd = apiEnd == -1 || version < apiEnd; if (!highThanStart || !lowThanEnd) { needToAddHook = false; } } if (needToAddHook) { addHook(hookType); } } } } private void addHook(Class<?> hookType) { try { Constructor<?> constructor = hookType.getDeclaredConstructors()[0]; if (!constructor.isAccessible()) { constructor.setAccessible(true); } Hook hook; if (constructor.getParameterTypes().length == 0) { hook = (Hook) constructor.newInstance(); } else { hook = (Hook) constructor.newInstance(this); } hookDelegate.addHook(hook); } catch (Throwable e) { throw new RuntimeException("Unable to instance Hook : " + hookType + " : " + e.getMessage()); } } public Hook addHook(Hook hook) { return hookDelegate.addHook(hook); } protected void afterHookApply(T delegate) { } @Override public abstract void inject() throws Throwable; public Context getContext() { return VirtualCore.get().getContext(); } public T getHookDelegate() { return hookDelegate; }}

最后再来说说 PatchManager ,这个类顾名思义就知道是补丁的管理类,在这里将各个 Patch 完成注入。

public final class PatchManager { private static final String TAG = PatchManager.class.getSimpleName(); private Map<Class<?>, Injectable> injectTable = new HashMap<>(12); private PatchManager() { } public static PatchManager getInstance() { return PatchManagerHolder.sPatchManager; } void injectAll() throws Throwable { for (Injectable injectable : injectTable.values()) { injectable.inject(); } // XXX: Lazy inject the Instrumentation, // It is important in many cases. addPatch(AppInstrumentation.getDefault()); } public boolean isInit() { return PatchManagerHolder.sInit; } public void init() throws Throwable { if (PatchManagerHolder.sInit) { throw new IllegalStateException("PatchManager Has been initialized."); } injectInternal(); PatchManagerHolder.sInit = true; } private void injectInternal() throws Throwable { if (VirtualCore.get().isMainProcess()) { return; } if (VirtualCore.get().isServerProcess()) { addPatch(new ActivityManagerPatch()); addPatch(new PackageManagerPatch()); return; } if (VirtualCore.get().isVAppProcess()) { addPatch(new LibCorePatch()); addPatch(new ActivityManagerPatch()); addPatch(new PackageManagerPatch()); addPatch(HCallbackHook.getDefault()); //以下省略诸多Path ... } } private void addPatch(Injectable injectable) { injectTable.put(injectable.getClass(), injectable); } public <T extends Injectable> T findPatch(Class<T> clazz) { // noinspection unchecked return (T) injectTable.get(clazz); } public <T extends Injectable> void checkEnv(Class<T> clazz) { Injectable injectable = findPatch(clazz); if (injectable != null && injectable.isEnvBad()) { try { injectable.inject(); } catch (Throwable e) { e.printStackTrace(); } } } public <T extends Injectable, H extends IHookObject> H getHookObject(Class<T> patchClass) { T patch = findPatch(patchClass); if (patch != null && patch instanceof PatchDelegate) { // noinspection unchecked return (H) ((PatchDelegate) patch).getHookDelegate(); } return null; } private static final class PatchManagerHolder { private static PatchManager sPatchManager = new PatchManager(); private static boolean sInit; }}

2.3 过程梳理

如果感觉以上内容不好理解的话,下面的这幅图,可能会缓解这种不适感。

VirtualApp Hook 框架分析

AccountManagerService Hook 为例,①、② 两步将 AccountBinderDelegate 等 Binder 代理注入至 ServiceManager ,假设触发 ③ getService 获取 AccountManagerService 后,调用 ④ getPwd ,这时 ⑤ getPwd 将通过 HookHandler 动态代理调用到代理类 ⑥ getHook 查找到函数代理对象,然后 ⑦ invoke 完成 Hook 函数 即 getPassword 代理调用。

到这里,整个 Hook 框架大致上就说完了。当然诸多版本的 Rom(官方、第三方)适配还是一个庞大的工作量,这就体现了作者对整个 Android Framework 掌握的功力了,这里额外提一下,作者对于Framework 镜像的处理,也是相当的精妙,这也为整个 Hook 框架的代码可读性贡献了相当一部分的力量,详见项目的 mirror 包 。

3、最后的闲扯

在阅读源码以及优秀开源项目的时候,大多数人都会感到很难读的通,我的一个看法和切身体会是,就像你第一眼看到一个人,肯定感觉十分陌生,而相处过一段时间后,这种陌生感就会慢慢消失,进而你反而会很了解她,知道她的爱好,知道她喜欢吃什么。阅读源码也是这样,短时间内如果无法拿下,又很想理解它,那么就要多花些时间,去读,不断的读和理解,了解源码所涉及的知识,尝试去用自己的理解去揣摩作者的思路,这个期间你需要用适合自己学习的方式(比如画类图,流程图、时序图,只要这种方式对你是有效的不必拘泥于形式)去记录修正你的理解,不断的去逼近作者的想法和思路,这也是一个学习成长的过程。

道阻且长,行则将至。that’s all.

未登录用户
全部评论0
到底啦