我是靠谱客的博主 追寻发带,这篇文章主要介绍FastHook——巧妙利用动态代理实现非侵入式AOP一、概述二、Hook方法三、创建任意方法的代理方法四、使用五、参考FastHook系列,现在分享给大家,希望可以做个参考。

一、概述

FastHook框架要求调用者准备与原方法参数一致的Hook方法和Forward方法,这些限制将业务逻辑和Hook逻辑耦合在一起。若不了解FastHook原理,请移步FastHook——一种高效稳定、简洁易用的Android Hook框架

因此可能需要一种新实现方式,其可将业务逻辑和Hook逻辑解耦。一种简单的方案便是动态生成Hook方法和Forword方法。然而该方案对性能的影响比较大,动态生成dex文件并加载是耗时操作。

本文将介绍一种新的思路,统一Hook方法,利用动态代理创建Forward方法。该方案比上述方案更加高效、简洁

二、Hook方法

Hook方法必须能实现以下两点需求

  1. 能正确识别原方法
  2. 能正确解析原方法参数

2.1 ART方法调用约定

以32位arm平台为例

复制代码
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
/* * Quick invocation stub internal. * On entry: * r0 = method pointer * r1 = argument array or null for no argument methods * r2 = size of argument array in bytes * r3 = (managed) thread pointer * [sp] = JValue* result * [sp + 4] = result_in_float * [sp + 8] = core register argument array * [sp + 12] = fp register argument array * +-------------------------+ * | uint32_t* fp_reg_args | * | uint32_t* core_reg_args | * | result_in_float | <- Caller frame * | Jvalue* result | * +-------------------------+ * | lr | * | r11 | * | r9 | * | r4 | <- r11 * +-------------------------+ * | uint32_t out[n-1] | * | : : | Outs * | uint32_t out[0] | * | StackRef<ArtMethod> | <- SP value=null * +-------------------------+ */ 复制代码
  1. r0寄存器存放被调用方法ArtMethod指针
  2. r1~r3存放前3个参数
  3. 从(sp+指针长度)地址起,按照参数顺序依次存放参数

显而易见,只需将被调用方法ArtMethod指针与sp指针传入Hook方法即可。32位指针长度为4字节,将以int类型传入,一种返回类型对应一个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
private static Object hookHandleObject(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); return param.result; } private static boolean hookHandleBoolean(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Boolean) { return ((Boolean) param.result).booleanValue(); } return false; } private static byte hookHandleByte(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Byte) { return ((Byte) param.result).byteValue(); } return 0; } private static char hookHandleChar(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Character) { return ((Character) param.result).charValue(); } return 0; } private static short hookHandleShort(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Short) { return ((Short) param.result).shortValue(); } return 0; } private static int hookHandleInt(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Integer) { return ((Integer) param.result).intValue(); } return 0; } private static long hookHandleLong(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Long) { return ((Long) param.result).longValue(); } return 0; } private static float hookHandleFloat(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Float) { return ((Float) param.result).floatValue(); } return 0; } private static double hookHandleDouble(int targetArtMethod, int sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Double) { return ((Double) param.result).doubleValue(); } return 0; } private static void hookHandleVoid(int targetArtMethod, int sp) { hookHandle(targetArtMethod,sp); return; } 复制代码

64位指针长度为8字节,将以long类型传入,一种返回类型对应一个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
private static Object hookHandleObject(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); return param.result; } private static boolean hookHandleBoolean(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Boolean) { return ((Boolean) param.result).booleanValue(); } return false; } private static byte hookHandleByte(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Byte) { return ((Byte) param.result).byteValue(); } return 0; } private static char hookHandleChar(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Character) { return ((Character) param.result).charValue(); } return 0; } private static short hookHandleShort(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Short) { return ((Short) param.result).shortValue(); } return 0; } private static int hookHandleInt(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Integer) { return ((Integer) param.result).intValue(); } return 0; } private static long hookHandleLong(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Long) { return ((Long) param.result).longValue(); } return 0; } private static float hookHandleFloat(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Float) { return ((Float) param.result).floatValue(); } return 0; } private static double hookHandleDouble(long targetArtMethod, long sp) { FastHookParam param = hookHandle(targetArtMethod,sp); if(param.result != null && param.result instanceof Double) { return ((Double) param.result).doubleValue(); } return 0; } private static void hookHandleVoid(long targetArtMethod, long sp) { hookHandle(targetArtMethod,sp); return; } 复制代码

2.2 参数解析

ART类型

类型字节
boolean4
byte4
char4
short4
int4
long8
float4
double8
reference4

在ART里,除了long和double类型以8字节解析外,其余类型都以4字节解析

Java是没有指针概念的,所以必须将传入的指针转化为Java对象,而并没有直接将指针转化为Java的接口,只能先将指针转化为JNI对象jobject,从JNI返回Java时会将jobject转化为Java对象

  • ArtMethod指针与JNI对象jobject转化接口,由JNI提供
复制代码
1
2
3
static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) 复制代码
  1. ToReflectedMethod将ArtMethod指针转化为JNI对象jobject
  2. FromReflectedMethod将JNI对象jobject转化为ArtMethod指针
  • 对象实际指针与JNI对象jobject转化接口,JNI不提供,需要解析相应符号
复制代码
1
2
3
jobject NewLocalRef(mirror::Object* obj) ObjPtr<mirror::Object> Thread::DecodeJObject(jobject obj) 复制代码
  1. NewLocalRef将对象实际指针转化为JNI对象jobject,符号为_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE
  2. DecodeJObject将JNI对象jobject转化为对象实际指针,符号为_ZNK3art6Thread13DecodeJObjectEP8_jobject 按照上述规则,可以正确解析出方法参数
复制代码
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
private static FastHookParam parseParam(long sp, Class[] paramType, boolean isStatic) { FastHookParam param = new FastHookParam(); int offset = 0; List<Object> args = new ArrayList<Object>(); if(!isStatic) { param.receiver = getObjectParam(sp,offset); offset += 4; } if(paramType == null) return param; for(Class type : paramType) { if(type.equals(boolean.class)) { boolean b = getBooleanParam(sp,offset); args.add(new Boolean(b)); offset += 4; }else if(type.equals(byte.class)) { byte b2 = getByteParam(sp,offset); args.add(new Byte(b2)); offset += 4; }else if(type.equals(char.class)) { char c = getCharParam(sp,offset); args.add(new Character(c)); offset += 4; }else if(type.equals(short.class)) { short s = getShortParam(sp,offset); args.add(new Short(s)); offset += 4; }else if(type.equals(int.class)) { int i = getIntParam(sp,offset); args.add(new Integer(i)); offset += 4; }else if(type.equals(long.class)) { long l = getLongParam(sp,offset); args.add(new Long(l)); offset += 8; }else if(type.equals(float.class)) { float f = getFloatParam(sp,offset); args.add(new Float(f)); offset += 4; }else if(type.equals(double.class)) { double d = getDoubleParam(sp,offset); args.add(new Double(d)); offset += 8; }else if(type.equals(void.class)) { }else { Object obj = getObjectParam(sp,offset); args.add(obj); offset += 4; } } if(!args.isEmpty()) { param.args = args.toArray(new Object[args.size()]); } return param; } 复制代码
复制代码
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
jobject GetReflectedMethod(JNIEnv *env, jclass clazz, jlong art_method) { jobject result = (*env)->ToReflectedMethod(env,clazz,(void *)art_method,JNI_FALSE); return result; } jboolean GetBooleanParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jboolean result = (jboolean)ReadInt32((unsigned char *)sp + pointer_size_ + offset); return result; } jbyte GetByteParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jbyte result = (jbyte)ReadInt32((unsigned char *)sp + pointer_size_ + offset); return result; } jchar GetCharParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jchar result = (jchar)ReadInt32((unsigned char *)sp + pointer_size_ + offset); return result; } jshort GetShortParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jshort result = (jshort)ReadInt32((unsigned char *)sp + pointer_size_ + offset); return result; } jint GetIntParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jint result = (jint)ReadInt32((unsigned char *)sp + pointer_size_ + offset); return result; } jlong GetLongParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jlong result = (jlong)ReadInt64((unsigned char *)sp + pointer_size_ + offset); return result; } jfloat GetFloatParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jfloat result = (jfloat)ReadFloat((unsigned char *)sp + pointer_size_ + offset); return result; } jdouble GetDoubleParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { jdouble result = (jdouble)ReadDouble((unsigned char *)sp + pointer_size_ + offset); return result; } jobject GetObjectParam(JNIEnv *env, jclass clazz, jlong sp, jint offset) { void *obj = (void *)ReadInt32((unsigned char *)sp + pointer_size_ + offset); jobject result = new_local_ref_(env,obj); return result; } 复制代码

三、创建任意方法的代理方法

3.1 创建任意方法代理类

复制代码
1
2
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 复制代码

newProxyInstance为Java动态代理接口,可以明显看出Java对于动态代理的范围限制在了接口上,非接口方法不能代理

为了实现AOP,需要想办法绕过JAVA接口限制,实现创建任意方法的代理方法的功能。

复制代码
1
2
3
4
private static native Class<?> generateProxy(String name, Class<?>[] interfaces, ClassLoader loader, Method[] methods, Class<?>[][] exceptions); 复制代码

generateProxy进行实际代理类创建,可以接口方法最后是以Method对象的形式传入,做一个大胆的尝试,将Method[]替换为我们想要的任意方法,这样变实现了创建任意方法的代理方法的功能。如下所示:

复制代码
1
2
private static native Class<?> generateProxy(name, null,loader, methods,null); 复制代码

interfaces设置为null即可。

3.2 构造方法处理

构造方法的类型为Constructor,而不是Method,因此构造方法不能用generateProxy生成对应代理方法。需要想个办法将Constructor转为Method。

在ART里,所有方法对对应一个ArtMethod对象,构造方法也不例外。在前面的分析可知,ArtMethod对象和Java对象是可以相互转化的,如果ArtMethod是构造方法则转化为Constructor对象,反之,则转化为Method对象

如果想将Constructor对象转化为Method对象,可以这么做

  1. 获取Constructor对象
  2. 将其转化为ArtMethod对象
  3. 将ArtMethod设置为非构造方法,通过清除kAccConstructor标志位实现
  4. 将ArtMethod对象转化为Method对象
  5. 还原ArtMethod为构造方法
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jobject ConstructorToMethod(JNIEnv *env, jclass clazz, jobject method) { jobject result = NULL; void *art_method = (void *)(*env)->FromReflectedMethod(env, method); ClearArtMethodAccessFlag(art_method,kAccConstructor); result = (*env)->ToReflectedMethod(env,clazz,(void *)art_method,JNI_FALSE); return result; } void MethodToConstructor(JNIEnv *env, jclass clazz, jobject method) { void *art_method = (void *)(*env)->FromReflectedMethod(env, method); AddArtMethodAccessFlag(art_method,kAccConstructor); } 复制代码

至此,可以得到任意方法的代理方法,即Forward方法,在需要的时候反射调用即可。

3.3 代理方法调用

假设有一个Test类

复制代码
1
2
3
4
public class Test { public void test() {} } 复制代码

为其创建了代理类ProxyTest,如果需要调用原方法,只需调用ProxyTest的代理方法,并传入原方法参数。

复制代码
1
2
3
Method test = ProxyTest.class.getMethod("test"); test.invoke(testObject); 复制代码
  1. 通过反射获取代理方法test
  2. 反射调用代理方法,传入原方法参数,testObject为Test实例,即this参数

根据FastHook原理,这里调用必须是代理方法test,即Forward方法,不然将无法实现原方法的调用。而代理方法test是public方法,反射调用的实际方法将由testObject来决定,Test也有一个test方法,因此实际调用的将是Test类的test方法而不是ProxyTest的test方法

因此无论在什么情况下,必须保证反射调用代理方法时,调用的都是其本身,即代理方法必须是Direct方法

方法类型isDirect
statictrue
privatetrue
constructortrue

当代理方法不是构造方法时,强制将其设置为private方法,以实现静态分派代理方法

复制代码
1
2
3
4
5
6
void SetDirectMethod(JNIEnv *env, jclass clazz, jobject method) { void *art_method = (void *)(*env)->FromReflectedMethod(env, method); AddArtMethodAccessFlag(art_method,kAccPrivate); } 复制代码

代理方法test属于ProxyTest类,因此需要一个ProxyTest类型的实例,而现在传入的testObject是Test类型的,类型不匹配。要想办法让testObject继承与ProxyTest类

FastHook采用一个巧妙的办法,将ProxyTest的父类置空,让ART误认为这是Object类,众所周知,任何对象都继承与Object类,完美解决类型不匹配问题

复制代码
1
2
3
4
5
6
void PoseAsObject(JNIEnv *env, jclass clazz, jclass target_class) { int super_class = 0; void *art_target_class = decode_jobject_(CurrentThread(),target_class); memcpy((unsigned char *)art_target_class + kClassSuperOffset,&super_class,4); } 复制代码

四、使用

4.1 FastHookCallback

复制代码
1
2
3
4
5
public interface FastHookCallback { void beforeHookedMethod(FastHookParam param); void afterHookedMethod(FastHookParam param); } 复制代码
  • beforeHookedMethod:原方法调用前调用
  • afterHookedMethod:原方法调用后调用

4.2 FastHookParam

复制代码
1
2
3
4
5
6
7
public class FastHookParam { public Object receiver; public Object[] args; public Object result; public boolean replace; } 复制代码
  • receiver:this对象,static方法则为null
  • args:方法参数
  • result:方法返回值
  • replace:是否替换方法,如果为true,则不会调用原方法,并以result返回

4.3 接口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
/** * *@param className 目标方法类名 *@param classLoader 目标方法所在ClassLoader,如果为null,代表当前ClassLoader *@param methodName 目标方法方法名 *@param methodSig 目标方法参数签名,不包括返回类型 *@param callback hook回调方法 *@param mode hook模式 *@param jitInline 是否内联,false,禁止内联;true,允许内联 * */ FastHookManager.doHook(String className, ClassLoader classLoader, String methodName, String methodSig, FastHookCallback callback, int mode, boolean jitInline) 复制代码
  • className:目标方法类名
  • classLoader:目标方法所在ClassLoader,如果为null,代表当前ClassLoader
  • methodName:目标方法方法名
  • methodSig:目标方法参数签名,不包括返回类型
  • callback:hook回调方法
  • mode:hook模式,FastHookManager.MODE_REWRITE和FastHookManager.MODE_REPLACE
  • jitInline:是否内联,false,禁止内联;true,允许内联

4.4 调用

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
FastHookManager.doHook(className,classLoader, methodName, methodSig, new FastHookCallback() { @Override public void beforeHookedMethod(FastHookParam param) { } @Override public void afterHookedMethod(FastHookParam param) { } },FastHookManager.MODE_REWRITE,false); 复制代码

五、参考

FastHook:https://github.com/turing-technician/FastHook/tree/callback

FastHook系列

  1. FastHook——一种高效稳定、简洁易用的Android Hook框架
  2. FastHook——远超其他同类框架的优异稳定性
  3. FastHook——如何使用FastHook免root hook微信
  4. FastHook——实现.dynsym段和.symtab段符号查询

最后

以上就是追寻发带最近收集整理的关于FastHook——巧妙利用动态代理实现非侵入式AOP一、概述二、Hook方法三、创建任意方法的代理方法四、使用五、参考FastHook系列的全部内容,更多相关FastHook——巧妙利用动态代理实现非侵入式AOP一、概述二、Hook方法三、创建任意方法内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(83)

评论列表共有 0 条评论

立即
投稿
返回
顶部