0%

VirtualApp Hook 框架分析

作者:Oz

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

概述

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*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(currentActivityThreadObject);
// 注入Instrumentation代理对象
if(!(instrumentationObject instanceof InstrumentationDelegate)) {
instrumentationField.set(activityThreadClass, new InstrumentationDelegate((Instrumentation)instrumentationObject));
}
}
}

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

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

Hook 框架

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

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

设计类图

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

VA Diagram

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

类图解析

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

1
2
3
4
5
6
7
public interface Injectable {

void inject() throws Throwable;

boolean isEnvBad();

}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 的实现类可以理解为代理函数的类象化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 集合,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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 服务的代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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 接口完成注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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 完成注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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;
}

}

过程梳理

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

va hook

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

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

最后的闲扯

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

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