/
ArtEngine.java
134 lines (112 loc) · 4.09 KB
/
ArtEngine.java
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package org.apolo;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.os.Build;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.HashMap;
public class ArtEngine {
private static final String TAG = "ApoloEngine";
@IntDef({
MODE_SIMPLE,
MODE_TRAMPOLINE,
MODE_INTERPRET
})
public @interface MODE {}
public static final int MODE_SIMPLE = 0x1;
public static final int MODE_TRAMPOLINE = 0x1 << 1;
public static final int MODE_INTERPRET = 0x1 << 2;
private static volatile int sInterpretMode = 0;
private static volatile boolean sInterpretLogOn = false;
private static volatile String sInterpretFilter = null;
private static volatile boolean sAlreadyHooked = false;
public static void preLoad() {
System.loadLibrary("apolo");
Log.d(TAG, "preLoad success!");
}
public static void setHookMode(@MODE int mode) {
sInterpretMode = mode;
}
public static void enableInterpretLog() {
sInterpretLogOn = true;
}
public static void setInterpretFilterRegex(String regex) {
sInterpretFilter = regex;
}
public static void addHooker(Class<?> cls) {
try {
ArtHookInternal.addHooker(cls.getClassLoader(), cls, false);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static void addHook(Member member, Method proxyMethod) {
ArtHookInternal.addHook(member, proxyMethod);
}
public static void addHookers(ClassLoader loader, Class<?>... classes) {
try {
for (Class<?> cls : classes) {
ArtHookInternal.addHooker(loader, cls, false);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static boolean contains(Member hookMethod) {
return ArtHookInternal.contains(hookMethod);
}
public static boolean startHook() {
if (sAlreadyHooked) {
Log.e(TAG, "already hooked! only can be called once!");
return false;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
Log.e(TAG, "hooked failed! platform not support!");
return false;
}
sAlreadyHooked = true;
return nativeStartHook(ArtHookInternal.methods, sInterpretMode, sInterpretLogOn, sInterpretFilter);
}
/**
* @param instance If origin method is static, you can pass null
* @param args params must match it's signature
* @param <T> Type of return
* @return Cast return to T
*/
public static <T> T callOrigin(Object instance/*Nullable*/, Object... args) {
return (T) nativeCallOrigin(instance, args);
}
/**
* Note: Only the current thread is affected!
* <p>
* If you want to disable hook-status in the current thread, pass true.
* HookTransition(true);
* <p>
* enable hook-status in the current thread, Pass false
* HookTransition(false);
*
* @param originMode Represents whether the original function is executed when method enter
* true: call originMethod
* false: call proxyMethod
*/
public static native void hookTransition(boolean originMode);
private static native Object nativeCallOrigin(Object instance/*Nullable*/, Object[] args);
/**
* @param proxyMethods <origin-Method, proxy-Method>
* @param mode {@link MODE}
* @param interpretLogOn
* @param filterRegex
* @return If hook success, will return true
*/
private static native boolean nativeStartHook(HashMap<Member, Member> proxyMethods, @MODE int mode, boolean interpretLogOn, String filterRegex);
private static native void reserve0();
private static native void reserve1();
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
@interface IntDef {
int[] value() default {};
}
}