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
到底啦