27 |
28 | extern "C" {
29 |
30 | void *fake_dlopen(const char *libpath, int flags);
31 | void *fake_dlsym(void *handle, const char *name);
32 |
33 | };
34 | #endif //DEXPOSED_DLFCN_H
35 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/DeviceCheck.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2005-2008, The Android Open Source Project
3 | * Modified work Copyright (c) 2013, rovo89 and Tungstwenty
4 | * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.taobao.android.dexposed;
20 |
21 | import android.annotation.SuppressLint;
22 | import android.content.Context;
23 | import android.util.Log;
24 |
25 | import java.io.BufferedReader;
26 | import java.io.InputStreamReader;
27 | import java.lang.reflect.InvocationTargetException;
28 | import java.lang.reflect.Method;
29 |
30 | public class DeviceCheck {
31 |
32 | private static final String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib";
33 | private static final String LIB_DALVIK = "libdvm.so";
34 | private static final String LIB_ART = "libart.so";
35 | private static final String LIB_ART_D = "libartd.so";
36 |
37 | private static boolean isCheckedDeviceSupport = false;
38 | private static boolean isDeviceSupportable = false;
39 |
40 | private static boolean isDalvikMode() {
41 | String vmMode = getCurrentRuntimeValue();
42 | if("Dalvik".equals(vmMode)){
43 | return true;
44 | }
45 | return false;
46 | }
47 |
48 | private static String getCurrentRuntimeValue() {
49 | try {
50 | Class> systemProperties = Class
51 | .forName("android.os.SystemProperties");
52 | try {
53 | Method get = systemProperties.getMethod("get", String.class,
54 | String.class);
55 | if (get == null) {
56 | return "WTF?!";
57 | }
58 | try {
59 | final String value = (String) get.invoke(systemProperties,
60 | SELECT_RUNTIME_PROPERTY,
61 | /* Assuming default is */"Dalvik");
62 | if (LIB_DALVIK.equals(value)) {
63 | return "Dalvik";
64 | } else if (LIB_ART.equals(value)) {
65 | return "ART";
66 | } else if (LIB_ART_D.equals(value)) {
67 | return "ART debug build";
68 | }
69 |
70 | return value;
71 | } catch (IllegalAccessException e) {
72 | return "IllegalAccessException";
73 | } catch (IllegalArgumentException e) {
74 | return "IllegalArgumentException";
75 | } catch (InvocationTargetException e) {
76 | return "InvocationTargetException";
77 | }
78 | } catch (NoSuchMethodException e) {
79 | return "SystemProperties.get(String key, String def) method is not found";
80 | }
81 | } catch (ClassNotFoundException e) {
82 | return "SystemProperties class is not found";
83 | }
84 | }
85 |
86 | private static boolean isSupportSDKVersion() {
87 | if (android.os.Build.VERSION.SDK_INT >= 14 && android.os.Build.VERSION.SDK_INT < 20) {
88 | return true;
89 | } else if(android.os.Build.VERSION.SDK_INT == 10 || android.os.Build.VERSION.SDK_INT == 9){
90 | return true;
91 | }
92 | return false;
93 | }
94 |
95 | private static boolean isX86CPU() {
96 | Process process = null;
97 | String abi = null;
98 | InputStreamReader ir = null;
99 | BufferedReader input = null;
100 | try {
101 | process = Runtime.getRuntime().exec("getprop ro.product.cpu.abi");
102 | ir = new InputStreamReader(process.getInputStream());
103 | input = new BufferedReader(ir);
104 | abi = input.readLine();
105 | if (abi.contains("x86")) {
106 | return true;
107 | }
108 | } catch (Exception e) {
109 | } finally {
110 | if (input != null) {
111 | try {
112 | input.close();
113 | } catch (Exception e) {
114 | }
115 | }
116 |
117 | if (ir != null) {
118 | try {
119 | ir.close();
120 | } catch (Exception e) {
121 | }
122 | }
123 |
124 | if (process != null) {
125 | try {
126 | process.destroy();
127 | } catch (Exception e) {
128 | }
129 | }
130 | }
131 |
132 | return false;
133 | }
134 |
135 | public static synchronized boolean isDeviceSupport(Context context) {
136 | // return memory checked value.
137 | try {
138 | if (isCheckedDeviceSupport)
139 | return isDeviceSupportable;
140 |
141 | if (!isX86CPU() && !isYunOS()) {
142 | isDeviceSupportable = true;
143 | } else {
144 | isDeviceSupportable = false;
145 | }
146 | } finally {
147 | Log.d("hotpatch", "device support is " + isDeviceSupportable + "checked" + isCheckedDeviceSupport);
148 | isCheckedDeviceSupport = true;
149 | }
150 | return isDeviceSupportable;
151 | }
152 |
153 | @SuppressLint("DefaultLocale")
154 | public static boolean isYunOS() {
155 | String s1 = null;
156 | String s2 = null;
157 | try {
158 | Method m = Class.forName("android.os.SystemProperties").getMethod(
159 | "get", String.class);
160 | s1 = (String) m.invoke(null, "ro.yunos.version");
161 | s2 = (String) m.invoke(null, "java.vm.name");
162 | } catch (NoSuchMethodException a) {
163 | } catch (ClassNotFoundException b) {
164 | } catch (IllegalAccessException c) {
165 | } catch (InvocationTargetException d) {
166 | }
167 | if ((s2 != null && s2.toLowerCase().contains("lemur")) || (s1 != null && s1.trim().length() > 0)) {
168 | return true;
169 | } else {
170 | return false;
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/XC_MethodHook.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2005-2008, The Android Open Source Project
3 | * Modified work Copyright (c) 2013, rovo89 and Tungstwenty
4 | * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.taobao.android.dexposed;
20 |
21 | import java.lang.reflect.Member;
22 |
23 | import com.taobao.android.dexposed.callbacks.IXUnhook;
24 | import com.taobao.android.dexposed.callbacks.XCallback;
25 |
26 | public abstract class XC_MethodHook extends XCallback {
27 | public XC_MethodHook() {
28 | super();
29 | }
30 | public XC_MethodHook(int priority) {
31 | super(priority);
32 | }
33 |
34 | /**
35 | * Called before the invocation of the method.
36 | * Can use {@link MethodHookParam#setResult(Object)} and {@link MethodHookParam#setThrowable(Throwable)}
37 | * to prevent the original method from being called.
38 | */
39 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {}
40 |
41 | /**
42 | * Called after the invocation of the method.
43 | *
Can use {@link MethodHookParam#setResult(Object)} and {@link MethodHookParam#setThrowable(Throwable)}
44 | * to modify the return value of the original method.
45 | */
46 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {}
47 |
48 |
49 | public static class MethodHookParam extends Param {
50 | /** Description of the hooked method */
51 | public Member method;
52 | /** The this
reference for an instance method, or null for static methods */
53 | public Object thisObject;
54 | /** Arguments to the method call */
55 | public Object[] args;
56 |
57 | private Object result = null;
58 | private Throwable throwable = null;
59 | /* package */ boolean returnEarly = false;
60 |
61 | /** Returns the result of the method call */
62 | public Object getResult() {
63 | return result;
64 | }
65 |
66 | /**
67 | * Modify the result of the method call. In a "before-method-call"
68 | * hook, prevents the call to the original method.
69 | * You still need to "return" from the hook handler if required.
70 | */
71 | public void setResult(Object result) {
72 | this.result = result;
73 | this.throwable = null;
74 | this.returnEarly = true;
75 | }
76 |
77 | /** Returns the Throwable
thrown by the method, or null */
78 | public Throwable getThrowable() {
79 | return throwable;
80 | }
81 |
82 | /** Returns true if an exception was thrown by the method */
83 | public boolean hasThrowable() {
84 | return throwable != null;
85 | }
86 |
87 | /**
88 | * Modify the exception thrown of the method call. In a "before-method-call"
89 | * hook, prevents the call to the original method.
90 | * You still need to "return" from the hook handler if required.
91 | */
92 | public void setThrowable(Throwable throwable) {
93 | this.throwable = throwable;
94 | this.result = null;
95 | this.returnEarly = true;
96 | }
97 |
98 | /** Returns the result of the method call, or throws the Throwable caused by it */
99 | public Object getResultOrThrowable() throws Throwable {
100 | if (throwable != null)
101 | throw throwable;
102 | return result;
103 | }
104 | }
105 |
106 | public class Unhook implements IXUnhook {
107 | private final Member hookMethod;
108 |
109 | public Unhook(Member hookMethod) {
110 | this.hookMethod = hookMethod;
111 | }
112 |
113 | public Member getHookedMethod() {
114 | return hookMethod;
115 | }
116 |
117 | public XC_MethodHook getCallback() {
118 | return XC_MethodHook.this;
119 | }
120 |
121 | @Override
122 | public void unhook() {
123 | DexposedBridge.unhookMethod(hookMethod, XC_MethodHook.this);
124 | }
125 |
126 | }
127 |
128 | /**
129 | *
130 | * Note: This class used for distinguish the Method cann't unhook.
131 | *
132 | */
133 | public abstract class XC_MethodKeepHook extends XC_MethodHook {
134 |
135 | public XC_MethodKeepHook() {
136 | super();
137 | }
138 | public XC_MethodKeepHook(int priority) {
139 | super(priority);
140 | }
141 |
142 | }
143 |
144 |
145 | }
146 |
147 |
148 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/XC_MethodReplacement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2005-2008, The Android Open Source Project
3 | * Modified work Copyright (c) 2013, rovo89 and Tungstwenty
4 | * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.taobao.android.dexposed;
20 |
21 |
22 | public abstract class XC_MethodReplacement extends XC_MethodHook {
23 | public XC_MethodReplacement() {
24 | super();
25 | }
26 | public XC_MethodReplacement(int priority) {
27 | super(priority);
28 | }
29 |
30 | @Override
31 | protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
32 | try {
33 | Object result = replaceHookedMethod(param);
34 | param.setResult(result);
35 | } catch (Throwable t) {
36 | param.setThrowable(t);
37 | }
38 | }
39 |
40 | protected final void afterHookedMethod(MethodHookParam param) throws Throwable {}
41 |
42 | /**
43 | * Shortcut for replacing a method completely. Whatever is returned/thrown here is taken
44 | * instead of the result of the original method (which will not be called).
45 | */
46 | protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;
47 |
48 | public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST*2) {
49 | @Override
50 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
51 | return null;
52 | };
53 | };
54 |
55 | /**
56 | * Creates a callback which always returns a specific value
57 | */
58 | public static XC_MethodReplacement returnConstant(final Object result) {
59 | return returnConstant(PRIORITY_DEFAULT, result);
60 | }
61 |
62 | /**
63 | * @see #returnConstant(Object)
64 | */
65 | public static XC_MethodReplacement returnConstant(int priority, final Object result) {
66 | return new XC_MethodReplacement(priority) {
67 | @Override
68 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
69 | return result;
70 | }
71 | };
72 | }
73 |
74 |
75 | /**
76 | *
77 | * Note: This class used for distinguish the Method cann't unhook.
78 | *
79 | */
80 | public abstract class XC_MethodKeepReplacement extends XC_MethodReplacement {
81 | public XC_MethodKeepReplacement() {
82 | super();
83 | }
84 | public XC_MethodKeepReplacement(int priority) {
85 | super(priority);
86 | }
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/callbacks/IXUnhook.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2005-2008, The Android Open Source Project
3 | * Modified work Copyright (c) 2013, rovo89 and Tungstwenty
4 | * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.taobao.android.dexposed.callbacks;
20 |
21 | public interface IXUnhook {
22 | void unhook();
23 | }
24 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/callbacks/XCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2005-2008, The Android Open Source Project
3 | * Modified work Copyright (c) 2013, rovo89 and Tungstwenty
4 | * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.taobao.android.dexposed.callbacks;
20 |
21 | import java.io.Serializable;
22 |
23 | import com.taobao.android.dexposed.DexposedBridge;
24 | import com.taobao.android.dexposed.DexposedBridge.CopyOnWriteSortedSet;
25 |
26 | import android.os.Bundle;
27 |
28 | public abstract class XCallback implements Comparable {
29 | public final int priority;
30 | public XCallback() {
31 | this.priority = PRIORITY_DEFAULT;
32 | }
33 | public XCallback(int priority) {
34 | this.priority = priority;
35 | }
36 |
37 | public static class Param {
38 | public final Object[] callbacks;
39 | private Bundle extra;
40 |
41 | protected Param() {
42 | callbacks = null;
43 | }
44 |
45 | protected Param(CopyOnWriteSortedSet extends XCallback> callbacks) {
46 | this.callbacks = callbacks.getSnapshot();
47 | }
48 |
49 | /**
50 | * This can be used to store anything for the scope of the callback.
51 | * Use this instead of instance variables.
52 | * @see #getObjectExtra
53 | * @see #setObjectExtra
54 | */
55 | public synchronized Bundle getExtra() {
56 | if (extra == null)
57 | extra = new Bundle();
58 | return extra;
59 | }
60 |
61 | /** @see #setObjectExtra */
62 | public Object getObjectExtra(String key) {
63 | Serializable o = getExtra().getSerializable(key);
64 | if (o instanceof SerializeWrapper)
65 | return ((SerializeWrapper) o).object;
66 | return null;
67 | }
68 |
69 | /** Provides a wrapper to store Object
s in extra
. */
70 | public void setObjectExtra(String key, Object o) {
71 | getExtra().putSerializable(key, new SerializeWrapper(o));
72 | }
73 |
74 | private static class SerializeWrapper implements Serializable {
75 | private static final long serialVersionUID = 1L;
76 | private Object object;
77 | public SerializeWrapper(Object o) {
78 | object = o;
79 | }
80 | }
81 | }
82 |
83 | public static final void callAll(Param param) {
84 | if (param.callbacks == null)
85 | throw new IllegalStateException("This object was not created for use with callAll");
86 |
87 | for (int i = 0; i < param.callbacks.length; i++) {
88 | try {
89 | ((XCallback) param.callbacks[i]).call(param);
90 | } catch (Throwable t) { DexposedBridge.log(t); }
91 | }
92 | }
93 |
94 | protected void call(Param param) throws Throwable {};
95 |
96 | @Override
97 | public int compareTo(XCallback other) {
98 | if (this == other)
99 | return 0;
100 |
101 | // order descending by priority
102 | if (other.priority != this.priority)
103 | return other.priority - this.priority;
104 | // then randomly
105 | else if (System.identityHashCode(this) < System.identityHashCode(other))
106 | return -1;
107 | else
108 | return 1;
109 | }
110 |
111 | public static final int PRIORITY_DEFAULT = 50;
112 | /** Call this handler last */
113 | public static final int PRIORITY_LOWEST = -10000;
114 | /** Call this handler first */
115 | public static final int PRIORITY_HIGHEST = 10000;
116 | }
117 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Debug.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2014-2015, Marvin Wißfeld
3 | * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.taobao.android.dexposed.utility;
19 |
20 | import android.util.Log;
21 |
22 | import java.io.BufferedReader;
23 | import java.io.FileReader;
24 | import java.io.IOException;
25 | import java.lang.reflect.Method;
26 |
27 | import me.weishu.epic.BuildConfig;
28 | import me.weishu.epic.art.method.ArtMethod;
29 |
30 | public final class Debug {
31 | private static final String TAG = "Dexposed";
32 |
33 | public static final boolean DEBUG = BuildConfig.DEBUG;
34 |
35 | private static final String RELASE_WRAN_STRING = "none in release mode.";
36 | private Debug() {
37 | }
38 |
39 | public static String addrHex(long i) {
40 | if (!DEBUG) {
41 | return RELASE_WRAN_STRING;
42 | }
43 |
44 | if (Runtime.is64Bit()) {
45 | return longHex(i);
46 | } else {
47 | return intHex((int) i);
48 | }
49 | }
50 |
51 | public static String longHex(long i) {
52 | return String.format("0x%016X", i);
53 | }
54 |
55 | public static String intHex(int i) {
56 | return String.format("0x%08X", i);
57 | }
58 |
59 | public static String byteHex(byte b) {
60 | return String.format("%02X", b);
61 | }
62 |
63 | public static String dump(byte[] b, long start) {
64 | StringBuffer sb = new StringBuffer();
65 | for (int i = 0; i < b.length; i++) {
66 | if (i % 8 == 0) {
67 | sb.append(addrHex(start + i)).append(":");
68 | }
69 | sb.append(byteHex(b[i])).append(" ");
70 | if (i % 8 == 7) {
71 | sb.append("\n");
72 | }
73 | }
74 | return sb.toString();
75 | }
76 | public static String hexdump(byte[] bytes, long start) {
77 | if (!DEBUG) {
78 | return RELASE_WRAN_STRING;
79 | }
80 | StringBuffer sb = new StringBuffer();
81 | for (int i = 0; i < bytes.length; i++) {
82 | if (i % 8 == 0) {
83 | sb.append(addrHex(start + i)).append(":");
84 | }
85 | sb.append(byteHex(bytes[i])).append(" ");
86 | if (i % 8 == 7) {
87 | sb.append("\n");
88 | }
89 | }
90 | return sb.toString();
91 | }
92 |
93 | public static String methodDescription(Method method) {
94 | return method.getDeclaringClass().getName() + "->" + method.getName() + " @" +
95 | addrHex(ArtMethod.of(method).getEntryPointFromQuickCompiledCode()) +
96 | " +" + addrHex(ArtMethod.of(method).getAddress());
97 | }
98 |
99 | public static void dumpMaps() {
100 | BufferedReader br = null;
101 | try {
102 | br = new BufferedReader(new FileReader("/proc/self/maps"));
103 | String line;
104 | while ((line = br.readLine()) != null) {
105 | Log.i(TAG, line);
106 | }
107 | } catch (IOException e) {
108 | Log.e(TAG, "dumpMaps error");
109 | } finally {
110 | if (br != null) {
111 | try {
112 | br.close();
113 | } catch (IOException e) {
114 | // ignore.
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Logger.java:
--------------------------------------------------------------------------------
1 | package com.taobao.android.dexposed.utility;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by weishu on 17/11/10.
7 | */
8 | public class Logger {
9 |
10 | private static final boolean DEBUG = Debug.DEBUG;
11 |
12 | public static final String preFix = "epic.";
13 |
14 | public static void i(String tag, String msg) {
15 | if (DEBUG) {
16 | Log.i(preFix + tag, msg);
17 | }
18 | }
19 |
20 | public static void d(String tagSuffix, String msg) {
21 | if (DEBUG) {
22 | Log.d(preFix + tagSuffix, msg);
23 | }
24 | }
25 |
26 | public static void w(String tag, String msg) {
27 | Log.w(preFix + tag, msg);
28 | }
29 |
30 | public static void e(String tag, String msg) {
31 | if (DEBUG) {
32 | Log.e(preFix + tag, msg);
33 | }
34 | }
35 |
36 | public static void e(String tag, String msg, Throwable e) {
37 | if (DEBUG) {
38 | Log.e(preFix + tag, msg, e);
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/NeverCalled.java:
--------------------------------------------------------------------------------
1 | package com.taobao.android.dexposed.utility;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * This Class is used for get the art_quick_to_interpreter_bridge address
7 | * Do not call this forever!!!
8 | */
9 | public class NeverCalled {
10 | private void fake(int a) {
11 | Log.i(getClass().getSimpleName(), a + "Do not inline me!!");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/NougatPolicy.java:
--------------------------------------------------------------------------------
1 | package com.taobao.android.dexposed.utility;
2 |
3 | import android.content.Context;
4 | import android.os.SystemClock;
5 | import android.util.Log;
6 |
7 | import java.lang.reflect.Method;
8 |
9 | public class NougatPolicy {
10 |
11 | private static class TraceLogger {
12 | static void i(String tag, String msg) {
13 | Log.i(tag, msg);
14 | }
15 | static void e(String tag, String msg) {
16 | Log.i(tag, msg);
17 | }
18 | static void e(String tag, String msg, Throwable e) {
19 | Log.i(tag, msg, e);
20 | }
21 | }
22 |
23 | private static final String TAG = "NougatPolicy";
24 |
25 | public static boolean fullCompile(Context context) {
26 | try {
27 | long t1 = SystemClock.elapsedRealtime();
28 | Object pm = getPackageManagerBinderProxy();
29 | if (pm == null) {
30 | TraceLogger.e(TAG, "can not found package service");
31 | return false;
32 | }
33 | /*
34 | @Override
35 | public boolean performDexOptMode(String packageName,
36 | boolean checkProfiles, String targetCompilerFilter, boolean force) {
37 | int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
38 | targetCompilerFilter, force);
39 | return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
40 | */
41 |
42 | final Method performDexOptMode = pm.getClass().getDeclaredMethod("performDexOptMode",
43 | String.class, boolean.class, String.class, boolean.class);
44 | boolean ret = (boolean) performDexOptMode.invoke(pm, context.getPackageName(), false, "speed", true);
45 | long cost = SystemClock.elapsedRealtime() - t1;
46 | Log.i(TAG, "full Compile cost: " + cost + " result:" + ret);
47 | return ret;
48 | } catch (Throwable e) {
49 | TraceLogger.e(TAG, "fullCompile failed:", e);
50 | return false;
51 | }
52 | }
53 |
54 | public static boolean clearCompileData(Context context) {
55 | boolean ret;
56 | try {
57 | Object pm = getPackageManagerBinderProxy();
58 | final Method performDexOpt = pm.getClass().getDeclaredMethod("performDexOpt", String.class,
59 | boolean.class, int.class, boolean.class);
60 | ret = (Boolean) performDexOpt.invoke(pm, context.getPackageName(), false, 2 /*install*/, true);
61 | } catch (Throwable e) {
62 | TraceLogger.e(TAG, "clear compile data failed", e);
63 | ret = false;
64 | }
65 | return ret;
66 | }
67 |
68 | private static Object getPackageManagerBinderProxy() throws Exception {
69 | Class> activityThread = Class.forName("android.app.ActivityThread");
70 | final Method getPackageManager = activityThread.getDeclaredMethod("getPackageManager");
71 | return getPackageManager.invoke(null);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Platform.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2016, Lody
3 | * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.taobao.android.dexposed.utility;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | public abstract class Platform {
24 |
25 | /*package*/ static Platform PLATFORM_INTERNAL;
26 |
27 | static {
28 | if (Runtime.is64Bit()) {
29 | PLATFORM_INTERNAL = new Platform64Bit();
30 | }else {
31 | PLATFORM_INTERNAL = new Platform32Bit();
32 | }
33 | }
34 |
35 | public static Platform getPlatform() {
36 | return PLATFORM_INTERNAL;
37 | }
38 |
39 | /**
40 | * Convert a byte array to int,
41 | * Use this function to get address from memory.
42 | *
43 | * @param data byte array
44 | * @return long
45 | */
46 | public abstract int orderByteToInt(byte[] data);
47 |
48 | /**
49 | * Convert a byte array to long,
50 | * Use this function to get address from memory.
51 | *
52 | * @param data byte array
53 | * @return long
54 | */
55 | public abstract long orderByteToLong(byte[] data);
56 |
57 | public abstract byte[] orderLongToByte(long serial, int length);
58 |
59 | public abstract byte[] orderIntToByte(int serial);
60 |
61 | public abstract int getIntSize();
62 |
63 |
64 | static class Platform32Bit extends Platform {
65 |
66 | @Override
67 | public int orderByteToInt(byte[] data) {
68 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
69 | }
70 |
71 | @Override
72 | public long orderByteToLong(byte[] data) {
73 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;
74 | }
75 |
76 | @Override
77 | public byte[] orderLongToByte(long serial, int length) {
78 | return ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN).putInt((int) serial).array();
79 | }
80 |
81 | @Override
82 | public byte[] orderIntToByte(int serial) {
83 | return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(serial).array();
84 | }
85 |
86 | @Override
87 | public int getIntSize() {
88 | return 4;
89 | }
90 |
91 |
92 | }
93 |
94 | static class Platform64Bit extends Platform {
95 |
96 | @Override
97 | public int orderByteToInt(byte[] data) {
98 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
99 | }
100 |
101 | @Override
102 | public long orderByteToLong(byte[] data) {
103 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getLong();
104 | }
105 |
106 | @Override
107 | public byte[] orderLongToByte(long serial, int length) {
108 | return ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN).putLong(serial).array();
109 | }
110 |
111 | @Override
112 | public byte[] orderIntToByte(int serial) {
113 | return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(serial).array();
114 | }
115 |
116 | @Override
117 | public int getIntSize() {
118 | return 8;
119 | }
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Runtime.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2016, Lody
3 | * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.taobao.android.dexposed.utility;
19 |
20 | import android.util.Log;
21 |
22 | import java.lang.reflect.Method;
23 |
24 | import me.weishu.epic.art.method.ArtMethod;
25 |
26 | public class Runtime {
27 |
28 | private static final String TAG = "Runtime";
29 |
30 | private volatile static Boolean isThumb = null;
31 |
32 | private volatile static boolean g64 = false;
33 | private volatile static boolean isArt = true;
34 |
35 | static {
36 | try {
37 | g64 = (boolean) Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("is64Bit").invoke(Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("getRuntime").invoke(null));
38 | } catch (Exception e) {
39 | Log.e(TAG, "get is64Bit failed, default not 64bit!", e);
40 | g64 = false;
41 | }
42 | isArt = System.getProperty("java.vm.version").startsWith("2");
43 | Log.i(TAG, "is64Bit: " + g64 + ", isArt: " + isArt);
44 | }
45 |
46 | public static boolean is64Bit() {
47 | return g64;
48 | }
49 |
50 | public static boolean isArt() {
51 | return isArt;
52 | }
53 |
54 | public static boolean isThumb2() {
55 | if (isThumb != null) {
56 | return isThumb;
57 | }
58 |
59 | try {
60 | Method method = String.class.getDeclaredMethod("hashCode");
61 | ArtMethod artMethodStruct = ArtMethod.of(method);
62 | long entryPointFromQuickCompiledCode = artMethodStruct.getEntryPointFromQuickCompiledCode();
63 | Logger.w("Runtime", "isThumb2, entry: " + Long.toHexString(entryPointFromQuickCompiledCode));
64 | isThumb = ((entryPointFromQuickCompiledCode & 1) == 1);
65 | return isThumb;
66 | } catch (Throwable e) {
67 | Logger.w("Runtime", "isThumb2, error: " + e);
68 | return true; // Default Thumb2.
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Unsafe.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2015 Marvin Wißfeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.taobao.android.dexposed.utility;
18 |
19 | import android.util.Log;
20 |
21 | import java.lang.reflect.Field;
22 |
23 | public final class Unsafe {
24 | private static final String TAG = "Unsafe";
25 |
26 | private static Object unsafe;
27 | private static Class unsafeClass;
28 |
29 | static {
30 | try {
31 | unsafeClass = Class.forName("sun.misc.Unsafe");
32 | Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
33 | theUnsafe.setAccessible(true);
34 | unsafe = theUnsafe.get(null);
35 | } catch (Exception e) {
36 | try {
37 | final Field theUnsafe = unsafeClass.getDeclaredField("THE_ONE");
38 | theUnsafe.setAccessible(true);
39 | unsafe = theUnsafe.get(null);
40 | } catch (Exception e2) {
41 | Log.w(TAG, "Unsafe not found o.O");
42 | }
43 | }
44 | }
45 |
46 | private Unsafe() {
47 | }
48 |
49 | @SuppressWarnings("unchecked")
50 | public static int arrayBaseOffset(Class cls) {
51 | try {
52 | return (int) unsafeClass.getDeclaredMethod("arrayBaseOffset", Class.class).invoke(unsafe, cls);
53 | } catch (Exception e) {
54 | Log.w(TAG, e);
55 | return 0;
56 | }
57 | }
58 |
59 | @SuppressWarnings("unchecked")
60 | public static int arrayIndexScale(Class cls) {
61 | try {
62 | return (int) unsafeClass.getDeclaredMethod("arrayIndexScale", Class.class).invoke(unsafe, cls);
63 | } catch (Exception e) {
64 | Log.w(TAG, e);
65 | return 0;
66 | }
67 | }
68 |
69 | @SuppressWarnings("unchecked")
70 | public static long objectFieldOffset(Field field) {
71 | try {
72 | return (long) unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class).invoke(unsafe, field);
73 | } catch (Exception e) {
74 | Log.w(TAG, e);
75 | return 0;
76 | }
77 | }
78 |
79 | @SuppressWarnings("unchecked")
80 | public static int getInt(Object array, long offset) {
81 | try {
82 | return (int) unsafeClass.getDeclaredMethod("getInt", Object.class, long.class).invoke(unsafe, array, offset);
83 | } catch (Exception e) {
84 | Log.w(TAG, e);
85 | return 0;
86 | }
87 | }
88 |
89 | @SuppressWarnings("unchecked")
90 | public static long getLong(Object array, long offset) {
91 | try {
92 | return (long) unsafeClass.getDeclaredMethod("getLong", Object.class, long.class).invoke(unsafe, array, offset);
93 | } catch (Exception e) {
94 | Log.w(TAG, e);
95 | return 0;
96 | }
97 | }
98 |
99 | @SuppressWarnings("unchecked")
100 | public static void putLong(Object array, long offset, long value) {
101 | try {
102 | unsafeClass.getDeclaredMethod("putLongVolatile", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);
103 | } catch (Exception e) {
104 | try {
105 | unsafeClass.getDeclaredMethod("putLong", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);
106 | } catch (Exception e1) {
107 | Log.w(TAG, e);
108 | }
109 | }
110 | }
111 |
112 | @SuppressWarnings("unchecked")
113 | public static void putInt(Object array, long offset, int value) {
114 | try {
115 | unsafeClass.getDeclaredMethod("putIntVolatile", Object.class, long.class, int.class).invoke(unsafe, array, offset, value);
116 | } catch (Exception e) {
117 | try {
118 | unsafeClass.getDeclaredMethod("putIntVolatile", Object.class, long.class, int.class).invoke(unsafe, array, offset, value);
119 | } catch (Exception e1) {
120 | Log.w(TAG, e);
121 | }
122 | }
123 | }
124 |
125 | public static long getObjectAddress(Object obj) {
126 | try {
127 | Object[] array = new Object[]{obj};
128 | if (arrayIndexScale(Object[].class) == 8) {
129 | return getLong(array, arrayBaseOffset(Object[].class));
130 | } else {
131 | return 0xffffffffL & getInt(array, arrayBaseOffset(Object[].class));
132 | }
133 | } catch (Exception e) {
134 | Log.w(TAG, e);
135 | return -1;
136 | }
137 | }
138 |
139 | /**
140 | * get Object from address, refer: http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
141 | * @param address the address of a object.
142 | * @return
143 | */
144 | public static Object getObject(long address) {
145 | Object[] array = new Object[]{null};
146 | long baseOffset = arrayBaseOffset(Object[].class);
147 | if (Runtime.is64Bit()) {
148 | putLong(array, baseOffset, address);
149 | } else {
150 | putInt(array, baseOffset, (int) address);
151 | }
152 | return array[0];
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/Epic.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art;
18 |
19 | import android.os.Build;
20 |
21 | import com.taobao.android.dexposed.utility.Debug;
22 | import com.taobao.android.dexposed.utility.Logger;
23 | import com.taobao.android.dexposed.utility.Runtime;
24 |
25 | import java.lang.reflect.Constructor;
26 | import java.lang.reflect.Method;
27 | import java.lang.reflect.Modifier;
28 | import java.nio.ByteBuffer;
29 | import java.nio.ByteOrder;
30 | import java.util.HashMap;
31 | import java.util.Map;
32 | import java.util.concurrent.ConcurrentHashMap;
33 |
34 | import me.weishu.epic.art.arch.Arm64_2;
35 | import me.weishu.epic.art.arch.ShellCode;
36 | import me.weishu.epic.art.arch.Thumb2;
37 | import me.weishu.epic.art.method.ArtMethod;
38 |
39 | /**
40 | * Hook Center.
41 | */
42 | public final class Epic {
43 |
44 | private static final String TAG = "Epic";
45 |
46 | private static final Map backupMethodsMapping = new ConcurrentHashMap<>();
47 |
48 | private static final Map originSigs = new ConcurrentHashMap<>();
49 |
50 | private static final Map scripts = new HashMap<>();
51 | private static ShellCode ShellCode;
52 |
53 | static {
54 | boolean isArm = true; // TODO: 17/11/21 TODO
55 | int apiLevel = Build.VERSION.SDK_INT;
56 | if (isArm) {
57 | if (Runtime.is64Bit()) {
58 | switch (apiLevel) {
59 | case Build.VERSION_CODES.M:
60 | ShellCode = new Arm64_2();
61 | break;
62 | case Build.VERSION_CODES.N:
63 | case Build.VERSION_CODES.N_MR1:
64 | case Build.VERSION_CODES.O:
65 | case Build.VERSION_CODES.O_MR1:
66 | ShellCode = new Arm64_2();
67 | break;
68 | case Build.VERSION_CODES.P:
69 | ShellCode = new Arm64_2();
70 | break;
71 | }
72 | } else if (Runtime.isThumb2()) {
73 | ShellCode = new Thumb2();
74 | } else {
75 | // todo ARM32
76 | Logger.w(TAG, "ARM32, not support now.");
77 | }
78 | }
79 | if (ShellCode == null) {
80 | throw new RuntimeException("Do not support this ARCH now!! API LEVEL:" + apiLevel);
81 | }
82 | Logger.i(TAG, "Using: " + ShellCode.getName());
83 | }
84 |
85 | public static boolean hookMethod(Constructor origin) {
86 | return hookMethod(ArtMethod.of(origin));
87 | }
88 |
89 | public static boolean hookMethod(Method origin) {
90 | ArtMethod artOrigin = ArtMethod.of(origin);
91 | return hookMethod(artOrigin);
92 | }
93 |
94 | private static boolean hookMethod(ArtMethod artOrigin) {
95 |
96 | MethodInfo methodInfo = new MethodInfo();
97 | methodInfo.isStatic = Modifier.isStatic(artOrigin.getModifiers());
98 | final Class>[] parameterTypes = artOrigin.getParameterTypes();
99 | if (parameterTypes != null) {
100 | methodInfo.paramNumber = parameterTypes.length;
101 | methodInfo.paramTypes = parameterTypes;
102 | } else {
103 | methodInfo.paramNumber = 0;
104 | methodInfo.paramTypes = new Class>[0];
105 | }
106 | methodInfo.returnType = artOrigin.getReturnType();
107 | methodInfo.method = artOrigin;
108 | originSigs.put(artOrigin.getAddress(), methodInfo);
109 |
110 | if (!artOrigin.isAccessible()) {
111 | artOrigin.setAccessible(true);
112 | }
113 |
114 | artOrigin.ensureResolved();
115 |
116 | long originEntry = artOrigin.getEntryPointFromQuickCompiledCode();
117 | if (originEntry == ArtMethod.getQuickToInterpreterBridge()) {
118 | Logger.i(TAG, "this method is not compiled, compile it now. current entry: 0x" + Long.toHexString(originEntry));
119 | boolean ret = artOrigin.compile();
120 | if (ret) {
121 | originEntry = artOrigin.getEntryPointFromQuickCompiledCode();
122 | Logger.i(TAG, "compile method success, new entry: 0x" + Long.toHexString(originEntry));
123 | } else {
124 | Logger.e(TAG, "compile method failed...");
125 | return false;
126 | // return hookInterpreterBridge(artOrigin);
127 | }
128 | }
129 |
130 | ArtMethod backupMethod = artOrigin.backup();
131 |
132 | Logger.i(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress()));
133 | Logger.i(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));
134 |
135 | ArtMethod backupList = getBackMethod(artOrigin);
136 | if (backupList == null) {
137 | setBackMethod(artOrigin, backupMethod);
138 | }
139 |
140 | final long key = originEntry;
141 | final EntryLock lock = EntryLock.obtain(originEntry);
142 | //noinspection SynchronizationOnLocalVariableOrMethodParameter
143 | synchronized (lock) {
144 | if (!scripts.containsKey(key)) {
145 | scripts.put(key, new Trampoline(ShellCode, originEntry));
146 | }
147 | Trampoline trampoline = scripts.get(key);
148 | boolean ret = trampoline.install(artOrigin);
149 | // Logger.d(TAG, "hook Method result:" + ret);
150 | return ret;
151 | }
152 | }
153 |
154 | /*
155 | private static boolean hookInterpreterBridge(ArtMethod artOrigin) {
156 |
157 | String identifier = artOrigin.getIdentifier();
158 | ArtMethod backupMethod = artOrigin.backup();
159 |
160 | Logger.d(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress()));
161 | Logger.d(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));
162 |
163 | List backupList = backupMethodsMapping.get(identifier);
164 | if (backupList == null) {
165 | backupList = new LinkedList();
166 | backupMethodsMapping.put(identifier, backupList);
167 | }
168 | backupList.add(backupMethod);
169 |
170 | long originalEntryPoint = ShellCode.toMem(artOrigin.getEntryPointFromQuickCompiledCode());
171 | Logger.d(TAG, "originEntry Point(bridge):" + Debug.addrHex(originalEntryPoint));
172 |
173 | originalEntryPoint += 16;
174 | Logger.d(TAG, "originEntry Point(offset8):" + Debug.addrHex(originalEntryPoint));
175 |
176 | if (!scripts.containsKey(originalEntryPoint)) {
177 | scripts.put(originalEntryPoint, new Trampoline(ShellCode, artOrigin));
178 | }
179 | Trampoline trampoline = scripts.get(originalEntryPoint);
180 |
181 | boolean ret = trampoline.install();
182 | Logger.i(TAG, "hook Method result:" + ret);
183 | return ret;
184 |
185 | }*/
186 |
187 | public synchronized static ArtMethod getBackMethod(ArtMethod origin) {
188 | String identifier = origin.getIdentifier();
189 | return backupMethodsMapping.get(identifier);
190 | }
191 |
192 | public static synchronized void setBackMethod(ArtMethod origin, ArtMethod backup) {
193 | String identifier = origin.getIdentifier();
194 | backupMethodsMapping.put(identifier, backup);
195 | }
196 |
197 | public static MethodInfo getMethodInfo(long address) {
198 | return originSigs.get(address);
199 | }
200 |
201 | public static int getQuickCompiledCodeSize(ArtMethod method) {
202 |
203 | long entryPoint = ShellCode.toMem(method.getEntryPointFromQuickCompiledCode());
204 | long sizeInfo1 = entryPoint - 4;
205 | byte[] bytes = EpicNative.get(sizeInfo1, 4);
206 | int size = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
207 | Logger.d(TAG, "getQuickCompiledCodeSize: " + size);
208 | return size;
209 | }
210 |
211 |
212 | public static class MethodInfo {
213 | public boolean isStatic;
214 | public int paramNumber;
215 | public Class>[] paramTypes;
216 | public Class> returnType;
217 | public ArtMethod method;
218 |
219 | @Override
220 | public String toString() {
221 | return method.toGenericString();
222 | }
223 | }
224 |
225 | private static class EntryLock {
226 | static Map sLockPool = new HashMap<>();
227 |
228 | static synchronized EntryLock obtain(long entry) {
229 | if (sLockPool.containsKey(entry)) {
230 | return sLockPool.get(entry);
231 | } else {
232 | EntryLock entryLock = new EntryLock();
233 | sLockPool.put(entry, entryLock);
234 | return entryLock;
235 | }
236 | }
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/EpicNative.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art;
18 |
19 | import android.util.Log;
20 |
21 | import com.taobao.android.dexposed.DeviceCheck;
22 | import com.taobao.android.dexposed.XposedHelpers;
23 | import com.taobao.android.dexposed.utility.Debug;
24 | import com.taobao.android.dexposed.utility.Logger;
25 | import com.taobao.android.dexposed.utility.Unsafe;
26 |
27 | import java.lang.reflect.Member;
28 |
29 | import static com.taobao.android.dexposed.utility.Debug.addrHex;
30 |
31 |
32 | public final class EpicNative {
33 |
34 | private static final String TAG = "EpicNative";
35 | private static volatile boolean useUnsafe = false;
36 | static {
37 | try {
38 | System.loadLibrary("epic");
39 | useUnsafe = DeviceCheck.isYunOS() || !isGetObjectAvailable();
40 | Log.i(TAG, "use unsafe ? " + useUnsafe);
41 | } catch (Throwable e) {
42 | Log.e(TAG, "init EpicNative error", e);
43 | }
44 | }
45 |
46 | public static native long mmap(int length);
47 |
48 | public static native boolean munmap(long address, int length);
49 |
50 | public static native void memcpy(long src, long dest, int length);
51 |
52 | public static native void memput(byte[] bytes, long dest);
53 |
54 | public static native byte[] memget(long src, int length);
55 |
56 | public static native boolean munprotect(long addr, long len);
57 |
58 | public static native long getMethodAddress(Member method);
59 |
60 | public static native boolean cacheflush(long addr, long len);
61 |
62 | public static native long malloc(int sizeOfPtr);
63 |
64 | public static native Object getObjectNative(long self, long address);
65 |
66 | private static native boolean isGetObjectAvailable();
67 |
68 | public static Object getObject(long self, long address) {
69 | if (useUnsafe) {
70 | return Unsafe.getObject(address);
71 | } else {
72 | return getObjectNative(self, address);
73 | }
74 | }
75 |
76 | public static native boolean compileMethod(Member method, long self);
77 |
78 | /**
79 | * suspend all running thread momently
80 | * @return a handle to resume all thread, used by {@link #resumeAll(long)}
81 | */
82 | public static native long suspendAll();
83 |
84 | /**
85 | * resume all thread which are suspend by {@link #suspendAll()}
86 | * only work abobe Android N
87 | * @param cookie the cookie return by {@link #suspendAll()}
88 | */
89 | public static native void resumeAll(long cookie);
90 |
91 | /**
92 | * stop jit compiler in runtime.
93 | * Warning: Just for experiment Do not call this now!!!
94 | * @return cookie use by {@link #startJit(long)}
95 | */
96 | public static native long stopJit();
97 |
98 | /**
99 | * start jit compiler stop by {@link #stopJit()}
100 | * Warning: Just for experiment Do not call this now!!!
101 | * @param cookie the cookie return by {@link #stopJit()}
102 | */
103 | public static native void startJit(long cookie);
104 |
105 | // FIXME: 17/12/29 reimplement it with pure native code.
106 | static native boolean activateNative(long jumpToAddress, long pc, long sizeOfTargetJump, long sizeOfBridgeJump, byte[] code);
107 |
108 | /**
109 | * Disable the moving gc of runtime.
110 | * Warning: Just for experiment Do not call this now!!!
111 | * @param api the api level
112 | */
113 | public static native void disableMovingGc(int api);
114 |
115 |
116 | private EpicNative() {
117 | }
118 |
119 | public static boolean compileMethod(Member method) {
120 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer");
121 | return compileMethod(method, nativePeer);
122 | }
123 |
124 | public static Object getObject(long address) {
125 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer");
126 | return getObject(nativePeer, address);
127 | }
128 |
129 | public static long map(int length) {
130 | long m = mmap(length);
131 | Logger.i(TAG, "Mapped memory of size " + length + " at " + addrHex(m));
132 | return m;
133 | }
134 |
135 | public static boolean unmap(long address, int length) {
136 | Logger.d(TAG, "Removing mapped memory of size " + length + " at " + addrHex(address));
137 | return munmap(address, length);
138 | }
139 |
140 | public static void put(byte[] bytes, long dest) {
141 | if (Debug.DEBUG) {
142 | Logger.d(TAG, "Writing memory to: " + addrHex(dest));
143 | Logger.d(TAG, Debug.hexdump(bytes, dest));
144 | }
145 | memput(bytes, dest);
146 | }
147 |
148 | public static byte[] get(long src, int length) {
149 | Logger.d(TAG, "Reading " + length + " bytes from: " + addrHex(src));
150 | byte[] bytes = memget(src, length);
151 | Logger.d(TAG, Debug.hexdump(bytes, src));
152 | return bytes;
153 | }
154 |
155 | public static boolean unprotect(long addr, long len) {
156 | Logger.d(TAG, "Disabling mprotect from " + addrHex(addr));
157 | return munprotect(addr, len);
158 | }
159 |
160 | public static void copy(long src, long dst, int length) {
161 | Logger.d(TAG, "Copy " + length + " bytes form " + addrHex(src) + " to " + addrHex(dst));
162 | memcpy(src, dst, length);
163 | }
164 |
165 | }
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/Trampoline.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art;
18 |
19 | import com.taobao.android.dexposed.utility.Debug;
20 | import com.taobao.android.dexposed.utility.Logger;
21 | import com.taobao.android.dexposed.utility.Runtime;
22 |
23 | import java.lang.reflect.Method;
24 | import java.util.HashSet;
25 | import java.util.Set;
26 |
27 | import me.weishu.epic.art.arch.ShellCode;
28 | import me.weishu.epic.art.entry.Entry;
29 | import me.weishu.epic.art.entry.Entry64_2;
30 | import me.weishu.epic.art.method.ArtMethod;
31 |
32 | class Trampoline {
33 | private static final String TAG = "Trampoline";
34 |
35 | private final ShellCode shellCode;
36 | private final long jumpToAddress;
37 | private final byte[] originalCode;
38 | private int trampolineSize;
39 | private long trampolineAddress;
40 | private boolean active;
41 |
42 | // private ArtMethod artOrigin;
43 | private Set segments = new HashSet<>();
44 |
45 | Trampoline(ShellCode shellCode, long entryPoint) {
46 | this.shellCode = shellCode;
47 | this.jumpToAddress = shellCode.toMem(entryPoint);
48 | this.originalCode = EpicNative.get(jumpToAddress, shellCode.sizeOfDirectJump());
49 | }
50 |
51 | public boolean install(ArtMethod originMethod){
52 | boolean modified = segments.add(originMethod);
53 | if (!modified) {
54 | // Already hooked, ignore
55 | Logger.d(TAG, originMethod + " is already hooked, return.");
56 | return true;
57 | }
58 |
59 | byte[] page = create();
60 | EpicNative.put(page, getTrampolineAddress());
61 |
62 | int quickCompiledCodeSize = Epic.getQuickCompiledCodeSize(originMethod);
63 | int sizeOfDirectJump = shellCode.sizeOfDirectJump();
64 | if (quickCompiledCodeSize < sizeOfDirectJump) {
65 | Logger.w(TAG, originMethod.toGenericString() + " quickCompiledCodeSize: " + quickCompiledCodeSize);
66 | originMethod.setEntryPointFromQuickCompiledCode(getTrampolinePc());
67 | return true;
68 | }
69 | // 这里是绝对不能改EntryPoint的,碰到GC就挂(GC暂停线程的时候,遍历所有线程堆栈,如果被hook的方法在堆栈上,那就GG)
70 | // source.setEntryPointFromQuickCompiledCode(script.getTrampolinePc());
71 | return activate();
72 | }
73 |
74 | private long getTrampolineAddress() {
75 | if (getSize() != trampolineSize) {
76 | alloc();
77 | }
78 | return trampolineAddress;
79 | }
80 |
81 | private long getTrampolinePc() {
82 | return shellCode.toPC(getTrampolineAddress());
83 | }
84 |
85 | private void alloc() {
86 | if (trampolineAddress != 0) {
87 | free();
88 | }
89 | trampolineSize = getSize();
90 | trampolineAddress = EpicNative.map(trampolineSize);
91 | Logger.d(TAG, "Trampoline alloc:" + trampolineSize + ", addr: 0x" + Long.toHexString(trampolineAddress));
92 | }
93 |
94 | private void free() {
95 | if (trampolineAddress != 0) {
96 | EpicNative.unmap(trampolineAddress, trampolineSize);
97 | trampolineAddress = 0;
98 | trampolineSize = 0;
99 | }
100 |
101 | if (active) {
102 | EpicNative.put(originalCode, jumpToAddress);
103 | }
104 | }
105 |
106 | private int getSize() {
107 | int count = 0;
108 | count += shellCode.sizeOfBridgeJump() * segments.size();
109 | count += shellCode.sizeOfCallOrigin();
110 | return count;
111 | }
112 |
113 | private byte[] create() {
114 | Logger.d(TAG, "create trampoline." + segments);
115 | byte[] mainPage = new byte[getSize()];
116 |
117 | int offset = 0;
118 | for (ArtMethod method : segments) {
119 | byte[] bridgeJump = createTrampoline(method);
120 | int length = bridgeJump.length;
121 | System.arraycopy(bridgeJump, 0, mainPage, offset, length);
122 | offset += length;
123 | }
124 |
125 | byte[] callOriginal = shellCode.createCallOrigin(jumpToAddress, originalCode);
126 | System.arraycopy(callOriginal, 0, mainPage, offset, callOriginal.length);
127 |
128 | return mainPage;
129 | }
130 |
131 | private boolean activate() {
132 | long pc = getTrampolinePc();
133 | Logger.d(TAG, "Writing direct jump entry " + Debug.addrHex(pc) + " to origin entry: 0x" + Debug.addrHex(jumpToAddress));
134 | synchronized (Trampoline.class) {
135 | return EpicNative.activateNative(jumpToAddress, pc, shellCode.sizeOfDirectJump(),
136 | shellCode.sizeOfBridgeJump(), shellCode.createDirectJump(pc));
137 | }
138 | }
139 |
140 | @Override
141 | protected void finalize() throws Throwable {
142 | free();
143 | super.finalize();
144 | }
145 |
146 | private byte[] createTrampoline(ArtMethod source){
147 | final Epic.MethodInfo methodInfo = Epic.getMethodInfo(source.getAddress());
148 | final Class> returnType = methodInfo.returnType;
149 |
150 | // Method bridgeMethod = Runtime.is64Bit() ? (Build.VERSION.SDK_INT == 23 ? Entry64_2.getBridgeMethod(methodInfo) : Entry64.getBridgeMethod(returnType))
151 | // : Entry.getBridgeMethod(returnType);
152 | Method bridgeMethod = Runtime.is64Bit() ? Entry64_2.getBridgeMethod(methodInfo)
153 | : Entry.getBridgeMethod(returnType);
154 |
155 | final ArtMethod target = ArtMethod.of(bridgeMethod);
156 | long targetAddress = target.getAddress();
157 | long targetEntry = target.getEntryPointFromQuickCompiledCode();
158 | long sourceAddress = source.getAddress();
159 | long structAddress = EpicNative.malloc(4);
160 |
161 | Logger.d(TAG, "targetAddress:"+ Debug.longHex(targetAddress));
162 | Logger.d(TAG, "sourceAddress:"+ Debug.longHex(sourceAddress));
163 | Logger.d(TAG, "targetEntry:"+ Debug.longHex(targetEntry));
164 | Logger.d(TAG, "structAddress:"+ Debug.longHex(structAddress));
165 |
166 | return shellCode.createBridgeJump(targetAddress, targetEntry, sourceAddress, structAddress);
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/Arm64.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteOrder;
20 |
21 | public class Arm64 extends ShellCode {
22 |
23 | @Override
24 | public int sizeOfDirectJump() {
25 | return 4 * 4;
26 | }
27 |
28 | @Override
29 | public byte[] createDirectJump(long targetAddress) {
30 | byte[] instructions = new byte[]{
31 | 0x50, 0x00, 0x00, 0x58, // ldr x9, _targetAddress
32 | 0x00, 0x02, 0x1F, (byte) 0xD6, // br x9
33 | 0x00, 0x00, 0x00, 0x00, // targetAddress
34 | 0x00, 0x00, 0x00, 0x00 // targetAddress
35 | };
36 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
37 | return instructions;
38 | }
39 |
40 | @Override
41 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
42 |
43 | byte[] instructions = new byte[]{
44 | 0x1f, 0x20, 0x03, (byte) 0xd5, // nop
45 | 0x69, 0x02, 0x00, 0x58, // ldr x9, source_method
46 | 0x1f, 0x00, 0x09, (byte) 0xeb, // cmp x0, x9
47 | (byte) 0xa1, 0x02, 0x00, 0x54, // bne 5f
48 | (byte) 0x80, 0x01, 0x00, 0x58, // ldr x0, target_method
49 |
50 | 0x29, 0x02, 0x00, 0x58, // ldr x9, struct
51 | (byte) 0xea, 0x03, 0x00, (byte) 0x91, // mov x10, sp
52 |
53 | 0x2a, 0x01, 0x00, (byte) 0xf9, // str x10, [x9, #0]
54 | 0x22, 0x05, 0x00, (byte) 0xf9, // str x2, [x9, #8]
55 |
56 | 0x23, 0x09, 0x00, (byte) 0xf9, // str x3, [x9, #16]
57 | (byte) 0xe3, 0x03, 0x09, (byte) 0xaa, // mov x3, x9
58 | 0x22, 0x01, 0x00, 0x58, // ldr x2, source_method
59 | 0x22, 0x0d, 0x00, (byte) 0xf9, // str x2, [x9, #24]
60 | (byte) 0xe2, 0x03, 0x13, (byte) 0xaa, // mov x2, x19
61 | (byte) 0x89, 0x00, 0x00, 0x58, // ldr x9, target_method_entry
62 | 0x20, 0x01, 0x1f, (byte) 0xd6, // br x9
63 |
64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_address
65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_entry
66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source_method
67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // struct
68 |
69 | };
70 |
71 |
72 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
73 | instructions.length - 32);
74 | writeLong(targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,
75 | instructions.length - 24);
76 | writeLong(srcAddress,
77 | ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 16);
78 | writeLong(structAddress, ByteOrder.LITTLE_ENDIAN, instructions,
79 | instructions.length - 8);
80 |
81 | return instructions;
82 | }
83 |
84 | @Override
85 | public int sizeOfBridgeJump() {
86 | return 24 * 4;
87 | }
88 |
89 |
90 | @Override
91 | public long toPC(long code) {
92 | return code;
93 | }
94 |
95 | @Override
96 | public long toMem(long pc) {
97 | return pc;
98 | }
99 |
100 | @Override
101 | public String getName() {
102 | return "64-bit ARM";
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/Arm64_2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteOrder;
20 |
21 | public class Arm64_2 extends ShellCode {
22 |
23 | @Override
24 | public int sizeOfDirectJump() {
25 | return 4 * 4;
26 | }
27 |
28 | @Override
29 | public byte[] createDirectJump(long targetAddress) {
30 | byte[] instructions = new byte[]{
31 | 0x50, 0x00, 0x00, 0x58, // ldr x16, _targetAddress
32 | 0x00, 0x02, 0x1F, (byte) 0xD6, // br x16
33 | 0x00, 0x00, 0x00, 0x00, // targetAddress
34 | 0x00, 0x00, 0x00, 0x00 // targetAddress
35 | };
36 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
37 | return instructions;
38 | }
39 |
40 | @Override
41 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
42 |
43 | byte[] instructions = new byte[] {
44 |
45 | /*
46 | ldr x17, 3f
47 | cmp x0, x17
48 | bne 5f
49 | ldr x0, 1f
50 | ldr x17, 4f
51 | mov x16, sp
52 | str x16, [x17, #0]
53 | str x2, [x17, #8]
54 | ldr x16, 3f
55 | str x16, [x17, #16]
56 | mov x2, x17
57 | ldr x17, 2f
58 | br x17
59 |
60 | 1:
61 | .quad 0x0
62 | 2:
63 | .quad 0x0
64 | 3:
65 | .quad 0x0
66 | 4:
67 | .quad 0x0
68 |
69 | 5:
70 | mov x0, x17
71 |
72 | */
73 | 0x1f, 0x20, 0x03, (byte) 0xd5, // nop
74 | 0x31, 0x02, 0x00, 0x58,
75 | 0x1f, 0x00, 0x11, (byte)0xeb,
76 | (byte)0x61, 0x02, 0x00, 0x54,
77 | (byte)0x40, 0x01, 0x00, 0x58,
78 | (byte)0xf1, 0x01, 0x00, 0x58,
79 | (byte)0xf0, 0x03, 0x00, (byte)0x91,
80 | (byte)0x30, 0x02, 0x00, (byte)0xf9,
81 | 0x22, 0x06, 0x00, (byte)0xf9,
82 | 0x30, 0x01, 0x00, (byte)0x58,
83 | (byte)0x30, 0x0a, 0x00, (byte)0xf9,
84 | (byte)0xe2, 0x03, 0x11, (byte)0xaa,
85 | (byte)0x91, 0x00, 0x00, 0x58,
86 | (byte)0x20, 0x02, 0x1f, (byte)0xd6,
87 |
88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_address
89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_entry
90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source_method
91 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // struct
92 | };
93 |
94 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
95 | instructions.length - 32);
96 | writeLong(targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,
97 | instructions.length - 24);
98 | writeLong(srcAddress,
99 | ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 16);
100 | writeLong(structAddress, ByteOrder.LITTLE_ENDIAN, instructions,
101 | instructions.length - 8);
102 |
103 | return instructions;
104 | }
105 |
106 | @Override
107 | public int sizeOfBridgeJump() {
108 | return 22 * 4;
109 | }
110 |
111 |
112 | @Override
113 | public long toPC(long code) {
114 | return code;
115 | }
116 |
117 | @Override
118 | public long toMem(long pc) {
119 | return pc;
120 | }
121 |
122 | @Override
123 | public String getName() {
124 | return "64-bit ARM(Android M)";
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/ShellCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteBuffer;
20 | import java.nio.ByteOrder;
21 |
22 | public abstract class ShellCode {
23 |
24 | public abstract byte[] createDirectJump(long targetAddress);
25 |
26 | public abstract int sizeOfDirectJump();
27 |
28 | public abstract long toPC(long code);
29 |
30 | public abstract long toMem(long pc);
31 |
32 | public byte[] createCallOrigin(long originalAddress, byte[] originalPrologue) {
33 | byte[] callOriginal = new byte[sizeOfCallOrigin()];
34 | System.arraycopy(originalPrologue, 0, callOriginal, 0, sizeOfDirectJump());
35 | byte[] directJump = createDirectJump(toPC(originalAddress + sizeOfDirectJump()));
36 | System.arraycopy(directJump, 0, callOriginal, sizeOfDirectJump(), sizeOfDirectJump());
37 | return callOriginal;
38 | }
39 |
40 | public int sizeOfCallOrigin() {
41 | return sizeOfDirectJump() * 2;
42 | }
43 |
44 | public abstract int sizeOfBridgeJump();
45 |
46 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
47 | throw new RuntimeException("not impled");
48 | }
49 |
50 | static void writeInt(int i, ByteOrder order, byte[] target, int pos) {
51 | System.arraycopy(ByteBuffer.allocate(4).order(order).putInt(i).array(), 0, target, pos, 4);
52 | }
53 |
54 | static void writeLong(long i, ByteOrder order, byte[] target, int pos) {
55 | System.arraycopy(ByteBuffer.allocate(8).order(order).putLong(i).array(), 0, target, pos, 8);
56 | }
57 |
58 | public abstract String getName();
59 | }
60 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/Thumb2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteOrder;
20 |
21 |
22 | public class Thumb2 extends ShellCode {
23 |
24 | @Override
25 | public int sizeOfDirectJump() {
26 | return 8;
27 | }
28 |
29 | @Override
30 | public byte[] createDirectJump(long targetAddress) {
31 | byte[] instructions = new byte[] {
32 | (byte) 0xdf, (byte) 0xf8, 0x00, (byte) 0xf0, // ldr pc, [pc]
33 | 0, 0, 0, 0
34 | };
35 | writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
36 | instructions.length - 4);
37 | return instructions;
38 | }
39 |
40 | @Override
41 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
42 | // 有问题,参数丢失。
43 | // byte[] instructions = new byte[] {
44 | // (byte) 0xdf, (byte) 0xf8, 0x18, (byte) 0xc0, // ldr ip, [pc, #24], ip = source_method_address
45 | //
46 | // (byte) 0x01, (byte) 0x91, // str r1, [sp, #4]
47 | // (byte) 0x02, (byte) 0x92, // str r2, [sp, #8]
48 | //
49 | // (byte) 0x03, (byte) 0x93, // str r3, [sp, #12]
50 | // (byte) 0x62, (byte) 0x46, // mov r2, ip
51 | //
52 | // 0x01, 0x48, // ldr r0, [pc, #4]
53 | // 0x6b, 0x46, // mov r3, sp
54 | //
55 | // (byte) 0xdf, (byte) 0xf8, 0x04, (byte) 0xf0, // ldr pc, [pc, #4]
56 | //
57 | // 0x0, 0x0, 0x0, 0x0, // target_method_pos_x
58 | // 0x0, 0x0, 0x0, 0x0, // target_method_entry_point
59 | // 0x0, 0x0, 0x0, 0x0, // src_method_pos_x
60 | // };
61 | // writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
62 | // instructions.length - 12);
63 | // writeInt((int) targetEntry,
64 | // ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
65 | // writeInt((int) srcAddress, ByteOrder.LITTLE_ENDIAN, instructions,
66 | // instructions.length - 4);
67 |
68 | byte[] instructions = new byte[]{
69 |
70 | (byte) 0xdf, (byte) 0xf8, (byte) 0x30, (byte) 0xc0, // ldr ip, [pc, #48] ip = source method address
71 |
72 | (byte) 0x60, (byte) 0x45, // cmp r0, ip if r0 != ip
73 | (byte) 0x40, (byte) 0xf0, (byte) 0x19, (byte) 0x80, // bne.w 1f jump label 1:
74 | (byte) 0x08, (byte) 0x48, // ldr r0, [pc, #28] r0 = target_method_address
75 | (byte) 0xdf, (byte) 0xf8, (byte) 0x28, (byte) 0xc0, // ldr ip, [pc, #38] ip = struct address
76 | (byte) 0xcc, (byte) 0xf8, (byte) 0x00, (byte) 0xd0, // str sp, [ip, #0]
77 | (byte) 0xcc, (byte) 0xf8, (byte) 0x04, (byte) 0x20, // str r2, [ip, #4]
78 | (byte) 0xcc, (byte) 0xf8, (byte) 0x08, (byte) 0x30, // str r3, [ip, #8]
79 |
80 | (byte) 0x63, (byte) 0x46, // mov r3, ip
81 | (byte) 0x05, (byte) 0x4a, // ldr r2, [pc, #16] r2 = source_method_address
82 | (byte) 0xcc, (byte) 0xf8, (byte) 0x0c, (byte) 0x20, // str r2, [ip, #12]
83 | (byte) 0x4a, (byte) 0x46, // move r2, r9
84 | (byte) 0x4a, (byte) 0x46, // move r2, r9
85 |
86 | (byte) 0xdf, (byte) 0xf8, (byte) 0x04, (byte) 0xf0, // ldr pc, [pc, #4]
87 |
88 | 0x0, 0x0, 0x0, 0x0, // target_method_pos_x
89 | 0x0, 0x0, 0x0, 0x0, // target_method_entry_point
90 | 0x0, 0x0, 0x0, 0x0, // src_method_address
91 | 0x0, 0x0, 0x0, 0x0, // struct address (sp, r1, r2)
92 | // 1:
93 | };
94 |
95 | writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
96 | instructions.length - 16);
97 | writeInt((int) targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,
98 | instructions.length - 12);
99 | writeInt((int) srcAddress,
100 | ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
101 | writeInt((int) structAddress, ByteOrder.LITTLE_ENDIAN, instructions,
102 | instructions.length - 4);
103 |
104 | return instructions;
105 | }
106 |
107 | @Override
108 | public int sizeOfBridgeJump() {
109 | return 15 * 4;
110 | }
111 |
112 | @Override
113 | public long toPC(long code) {
114 | return toMem(code) + 1;
115 | }
116 |
117 | @Override
118 | public long toMem(long pc) {
119 | return pc & ~0x1;
120 | }
121 |
122 | @Override
123 | public String getName() {
124 | return "Thumb2";
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/entry/Entry64.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.entry;
18 |
19 | import com.taobao.android.dexposed.DexposedBridge;
20 | import com.taobao.android.dexposed.XposedHelpers;
21 | import com.taobao.android.dexposed.utility.Logger;
22 |
23 | import java.lang.reflect.Method;
24 | import java.nio.ByteBuffer;
25 | import java.nio.ByteOrder;
26 | import java.util.Arrays;
27 | import java.util.HashMap;
28 | import java.util.Map;
29 |
30 | import me.weishu.epic.art.Epic;
31 | import me.weishu.epic.art.EpicNative;
32 |
33 | @SuppressWarnings({"unused", "ConstantConditions"})
34 | public class Entry64 {
35 |
36 | private final static String TAG = "Entry64";
37 |
38 | //region ---------------callback---------------
39 | private static int onHookInt(Object artmethod, Object receiver, Object[] args) {
40 | return (Integer) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
41 | }
42 |
43 | private static long onHookLong(Object artmethod, Object receiver, Object[] args) {
44 | return (Long) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
45 | }
46 |
47 | private static double onHookDouble(Object artmethod, Object receiver, Object[] args) {
48 | return (Double) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
49 | }
50 |
51 | private static char onHookChar(Object artmethod, Object receiver, Object[] args) {
52 | return (Character) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
53 | }
54 |
55 | private static short onHookShort(Object artmethod, Object receiver, Object[] args) {
56 | return (Short) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
57 | }
58 |
59 | private static float onHookFloat(Object artmethod, Object receiver, Object[] args) {
60 | return (Float) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
61 | }
62 |
63 | private static Object onHookObject(Object artmethod, Object receiver, Object[] args) {
64 | return DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
65 | }
66 |
67 | private static void onHookVoid(Object artmethod, Object receiver, Object[] args) {
68 | DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
69 | }
70 |
71 | private static boolean onHookBoolean(Object artmethod, Object receiver, Object[] args) {
72 | return (Boolean) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
73 | }
74 |
75 | private static byte onHookByte(Object artmethod, Object receiver, Object[] args) {
76 | return (Byte) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);
77 | }
78 | //endregion
79 |
80 | //region ---------------bridge---------------
81 | private static void voidBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
82 | referenceBridge(r1, self, struct, x4, x5, x6, x7);
83 | }
84 |
85 | private static boolean booleanBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
86 | return (Boolean) referenceBridge(r1, self, struct, x4, x5, x6, x7);
87 | }
88 |
89 | private static byte byteBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
90 | return (Byte) referenceBridge(r1, self, struct, x4, x5, x6, x7);
91 | }
92 |
93 | private static short shortBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
94 | return (Short) referenceBridge(r1, self, struct, x4, x5, x6, x7);
95 | }
96 |
97 | private static char charBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
98 | return (Character) referenceBridge(r1, self, struct, x4, x5, x6, x7);
99 | }
100 |
101 | private static int intBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
102 | return (Integer) referenceBridge(r1, self, struct, x4, x5, x6, x7);
103 | }
104 |
105 | private static long longBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
106 | return (Long) referenceBridge(r1, self, struct, x4, x5, x6, x7);
107 | }
108 |
109 | private static float floatBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
110 | return (Float) referenceBridge(r1, self, struct, x4, x5, x6, x7);
111 | }
112 |
113 | private static double doubleBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {
114 | return (Double) referenceBridge(r1, self, struct, x4, x5, x6, x7);
115 | }
116 | //endregion
117 |
118 | private static Object referenceBridge(long x1, long self, long struct, long x4, long x5, long x6, long x7) {
119 | Logger.i(TAG, "enter bridge function.");
120 |
121 | // struct {
122 | // void* sp;
123 | // void* r2;
124 | // void* r3;
125 | // void* sourceMethod
126 | // }
127 | // sp + 16 = r4
128 |
129 | Logger.d(TAG, "self:" + Long.toHexString(self));
130 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer");
131 | Logger.d(TAG, "java thread native peer:" + Long.toHexString(nativePeer));
132 |
133 | Logger.d(TAG, "struct:" + Long.toHexString(struct));
134 |
135 | final long sp = ByteBuffer.wrap(EpicNative.get(struct, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong();
136 |
137 | Logger.d(TAG, "stack:" + sp);
138 |
139 | final byte[] rr1 = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(x1).array();
140 | final byte[] r2 = EpicNative.get(struct + 8, 8);
141 |
142 | final byte[] r3 = EpicNative.get(struct + 16, 8);
143 |
144 | final long sourceMethod = ByteBuffer.wrap(EpicNative.get(struct + 24, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong();
145 | Logger.d(TAG, "sourceMethod:" + Long.toHexString(sourceMethod));
146 |
147 | Epic.MethodInfo originMethodInfo = Epic.getMethodInfo(sourceMethod);
148 | Logger.d(TAG, "originMethodInfo :" + originMethodInfo);
149 |
150 | boolean isStatic = originMethodInfo.isStatic;
151 |
152 | int numberOfArgs = originMethodInfo.paramNumber;
153 | Class>[] typeOfArgs = originMethodInfo.paramTypes;
154 | Object[] arguments = new Object[numberOfArgs];
155 |
156 | Object receiver;
157 |
158 | self = nativePeer;
159 |
160 | if (isStatic) {
161 | receiver = null;
162 | do {
163 | if (numberOfArgs == 0) break;
164 | arguments[0] = wrapArgument(typeOfArgs[0], self, rr1);
165 | if (numberOfArgs == 1) break;
166 | arguments[1] = wrapArgument(typeOfArgs[1], self, r2);
167 | if (numberOfArgs == 2) break;
168 | arguments[2] = wrapArgument(typeOfArgs[2], self, r3);
169 | if (numberOfArgs == 3) break;
170 | arguments[3] = wrapArgument(typeOfArgs[3], self, x4);
171 | if (numberOfArgs == 4) break;
172 | arguments[4] = wrapArgument(typeOfArgs[4], self, x5);
173 | if (numberOfArgs == 5) break;
174 | arguments[5] = wrapArgument(typeOfArgs[5], self, x6);
175 | if (numberOfArgs == 6) break;
176 | arguments[6] = wrapArgument(typeOfArgs[6], self, x7);
177 | if (numberOfArgs == 7) break;
178 |
179 | for (int i = 7; i < numberOfArgs; i++) {
180 | byte[] argsInStack = EpicNative.get(sp + i * 8 + 8, 8);
181 | arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack);
182 | }
183 | } while (false);
184 |
185 | } else {
186 |
187 | receiver = EpicNative.getObject(self, x1);
188 | Logger.i(TAG, "this :" + receiver);
189 |
190 | do {
191 | if (numberOfArgs == 0) break;
192 | arguments[0] = wrapArgument(typeOfArgs[0], self, r2);
193 | if (numberOfArgs == 1) break;
194 | arguments[1] = wrapArgument(typeOfArgs[1], self, r3);
195 | if (numberOfArgs == 2) break;
196 | arguments[2] = wrapArgument(typeOfArgs[2], self, x4);
197 | if (numberOfArgs == 3) break;
198 | arguments[3] = wrapArgument(typeOfArgs[3], self, x5);
199 | if (numberOfArgs == 4) break;
200 | arguments[4] = wrapArgument(typeOfArgs[4], self, x6);
201 | if (numberOfArgs == 5) break;
202 | arguments[5] = wrapArgument(typeOfArgs[5], self, x7);
203 | if (numberOfArgs == 6) break;
204 |
205 | for (int i = 6; i < numberOfArgs; i++) {
206 | byte[] argsInStack = EpicNative.get(sp + i * 8 + 16, 8);
207 | arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack);
208 | }
209 | } while (false);
210 | }
211 |
212 | Logger.i(TAG, "arguments:" + Arrays.toString(arguments));
213 |
214 | Class> returnType = originMethodInfo.returnType;
215 | Object artMethod = originMethodInfo.method;
216 |
217 | Logger.d(TAG, "leave bridge function");
218 |
219 | if (returnType == void.class) {
220 | onHookVoid(artMethod, receiver, arguments);
221 | return 0;
222 | } else if (returnType == char.class) {
223 | return onHookChar(artMethod, receiver, arguments);
224 | } else if (returnType == byte.class) {
225 | return onHookByte(artMethod, receiver, arguments);
226 | } else if (returnType == short.class) {
227 | return onHookShort(artMethod, receiver, arguments);
228 | } else if (returnType == int.class) {
229 | return onHookInt(artMethod, receiver, arguments);
230 | } else if (returnType == long.class) {
231 | return onHookLong(artMethod, receiver, arguments);
232 | } else if (returnType == float.class) {
233 | return onHookFloat(artMethod, receiver, arguments);
234 | } else if (returnType == double.class) {
235 | return onHookDouble(artMethod, receiver, arguments);
236 | } else if (returnType == boolean.class) {
237 | return onHookBoolean(artMethod, receiver, arguments);
238 | } else {
239 | return onHookObject(artMethod, receiver, arguments);
240 | }
241 | }
242 |
243 | private static Object wrapArgument(Class> type, long self, long value) {
244 | return wrapArgument(type, self, ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array());
245 | }
246 |
247 | private static Object wrapArgument(Class> type, long self, byte[] value) {
248 | final ByteBuffer byteBuffer = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);
249 | if (type.isPrimitive()) {
250 | if (type == int.class) {
251 | return byteBuffer.getInt();
252 | } else if (type == long.class) {
253 | return byteBuffer.getLong();
254 | } else if (type == float.class) {
255 | return byteBuffer.getFloat();
256 | } else if (type == short.class) {
257 | return byteBuffer.getShort();
258 | } else if (type == byte.class) {
259 | return byteBuffer.get();
260 | } else if (type == char.class) {
261 | return byteBuffer.getChar();
262 | } else if (type == double.class) {
263 | return byteBuffer.getDouble();
264 | } else if (type == boolean.class) {
265 | return byteBuffer.getInt() == 0;
266 | } else {
267 | throw new RuntimeException("unknown type:" + type);
268 | }
269 | } else {
270 | long address = byteBuffer.getLong();
271 | Object object = EpicNative.getObject(self, address);
272 | //Logger.i(TAG, "wrapArgument, address: 0x" + Long.toHexString(address) + ", value:" + object);
273 | return object;
274 | }
275 | }
276 |
277 | private static Map, String> bridgeMethodMap = new HashMap<>();
278 | static {
279 | Class>[] primitiveTypes = new Class[]{boolean.class, byte.class, char.class, short.class,
280 | int.class, long.class, float.class, double.class};
281 | for (Class> primitiveType : primitiveTypes) {
282 | bridgeMethodMap.put(primitiveType, primitiveType.getName() + "Bridge");
283 | }
284 | bridgeMethodMap.put(void.class, "voidBridge");
285 | bridgeMethodMap.put(Object.class, "referenceBridge");
286 | }
287 |
288 | public static Method getBridgeMethod(Class> returnType) {
289 | try {
290 | final String bridgeMethod = bridgeMethodMap.get(returnType.isPrimitive() ? returnType : Object.class);
291 | Logger.d(TAG, "bridge method:" + bridgeMethod + ", map:" + bridgeMethodMap);
292 | Method method = Entry64.class.getDeclaredMethod(bridgeMethod, long.class, long.class,
293 | long.class, long.class, long.class, long.class, long.class);
294 | method.setAccessible(true);
295 | return method;
296 | } catch (Throwable e) {
297 | throw new RuntimeException("error", e);
298 | }
299 | }
300 | }
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/method/Offset.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.method;
18 |
19 | import android.os.Build;
20 |
21 | import com.taobao.android.dexposed.utility.Runtime;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.nio.ByteOrder;
25 |
26 | import me.weishu.epic.art.EpicNative;
27 |
28 | /**
29 | * The Offset of field in an ArtMethod
30 | */
31 | class Offset {
32 |
33 | /**
34 | * the offset of the entry point
35 | */
36 | static Offset ART_QUICK_CODE_OFFSET;
37 |
38 | /**
39 | * the offset of the access flag
40 | */
41 | static Offset ART_ACCESS_FLAG_OFFSET;
42 |
43 | /**
44 | * the offset of a jni entry point
45 | */
46 | static Offset ART_JNI_ENTRY_OFFSET;
47 |
48 | static {
49 | initFields();
50 | }
51 |
52 | private enum BitWidth {
53 | DWORD(4),
54 | QWORD(8);
55 |
56 | BitWidth(int width) {
57 | this.width = width;
58 | }
59 |
60 | int width;
61 | }
62 |
63 | private long offset;
64 | private BitWidth length;
65 |
66 | public long getOffset() {
67 | return offset;
68 | }
69 |
70 | public void setOffset(long offset) {
71 | this.offset = offset;
72 | }
73 |
74 | public BitWidth getLength() {
75 | return length;
76 | }
77 |
78 | public void setLength(BitWidth length) {
79 | this.length = length;
80 | }
81 |
82 | public static long read(long base, Offset offset) {
83 | long address = base + offset.offset;
84 | byte[] bytes = EpicNative.get(address, offset.length.width);
85 | if (offset.length == BitWidth.DWORD) {
86 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;
87 | } else {
88 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();
89 | }
90 | }
91 |
92 | public static void write(long base, Offset offset, long value) {
93 | long address = base + offset.offset;
94 | byte[] bytes;
95 | if (offset.length == BitWidth.DWORD) {
96 | if (value > 0xFFFFFFFFL) {
97 | throw new IllegalStateException("overflow may occur");
98 | } else {
99 | bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array();
100 | }
101 | } else {
102 | bytes = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array();
103 | }
104 | EpicNative.put(bytes, address);
105 | }
106 |
107 | private static void initFields() {
108 | ART_QUICK_CODE_OFFSET = new Offset();
109 | ART_ACCESS_FLAG_OFFSET = new Offset();
110 | ART_JNI_ENTRY_OFFSET = new Offset();
111 |
112 | ART_ACCESS_FLAG_OFFSET.setLength(Offset.BitWidth.DWORD);
113 |
114 | final int apiLevel = Build.VERSION.SDK_INT;
115 | if (apiLevel > 28) {
116 | throw new RuntimeException("API LEVEL: " + apiLevel + " is not supported now : (");
117 | }
118 |
119 | if (Runtime.is64Bit()) {
120 | ART_QUICK_CODE_OFFSET.setLength(Offset.BitWidth.QWORD);
121 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD);
122 | switch (apiLevel) {
123 | case Build.VERSION_CODES.P:
124 | ART_QUICK_CODE_OFFSET.setOffset(32);
125 | ART_JNI_ENTRY_OFFSET.setOffset(24);
126 | ART_ACCESS_FLAG_OFFSET.setOffset(4);
127 | break;
128 | case Build.VERSION_CODES.O_MR1:
129 | case Build.VERSION_CODES.O:
130 | ART_QUICK_CODE_OFFSET.setOffset(40);
131 | ART_JNI_ENTRY_OFFSET.setOffset(32);
132 | ART_ACCESS_FLAG_OFFSET.setOffset(4);
133 | break;
134 | case Build.VERSION_CODES.N_MR1:
135 | case Build.VERSION_CODES.N:
136 | ART_QUICK_CODE_OFFSET.setOffset(48);
137 | ART_JNI_ENTRY_OFFSET.setOffset(40);
138 | ART_ACCESS_FLAG_OFFSET.setOffset(4);
139 | break;
140 | case Build.VERSION_CODES.M:
141 | ART_QUICK_CODE_OFFSET.setOffset(48);
142 | ART_JNI_ENTRY_OFFSET.setOffset(40);
143 | ART_ACCESS_FLAG_OFFSET.setOffset(12);
144 | break;
145 | case Build.VERSION_CODES.LOLLIPOP_MR1:
146 | ART_QUICK_CODE_OFFSET.setOffset(52);
147 | ART_JNI_ENTRY_OFFSET.setOffset(44);
148 | ART_ACCESS_FLAG_OFFSET.setOffset(20);
149 | break;
150 | case Build.VERSION_CODES.LOLLIPOP:
151 | ART_QUICK_CODE_OFFSET.setOffset(40);
152 | ART_QUICK_CODE_OFFSET.setLength(BitWidth.QWORD);
153 | ART_JNI_ENTRY_OFFSET.setOffset(32);
154 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD);
155 | ART_ACCESS_FLAG_OFFSET.setOffset(56);
156 | break;
157 | case Build.VERSION_CODES.KITKAT:
158 | ART_QUICK_CODE_OFFSET.setOffset(32);
159 | ART_ACCESS_FLAG_OFFSET.setOffset(28);
160 | break;
161 | default:
162 | throw new RuntimeException("API LEVEL: " + apiLevel + " is not supported now : (");
163 | }
164 | } else {
165 | ART_QUICK_CODE_OFFSET.setLength(Offset.BitWidth.DWORD);
166 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.DWORD);
167 | switch (apiLevel) {
168 | case Build.VERSION_CODES.P:
169 | ART_QUICK_CODE_OFFSET.setOffset(24);
170 | ART_JNI_ENTRY_OFFSET.setOffset(20);
171 | ART_ACCESS_FLAG_OFFSET.setOffset(4);
172 | break;
173 | case Build.VERSION_CODES.O_MR1:
174 | case Build.VERSION_CODES.O:
175 | ART_QUICK_CODE_OFFSET.setOffset(28);
176 | ART_JNI_ENTRY_OFFSET.setOffset(24);
177 | ART_ACCESS_FLAG_OFFSET.setOffset(4);
178 | break;
179 | case Build.VERSION_CODES.N_MR1:
180 | case Build.VERSION_CODES.N:
181 | ART_QUICK_CODE_OFFSET.setOffset(32);
182 | ART_JNI_ENTRY_OFFSET.setOffset(28);
183 | ART_ACCESS_FLAG_OFFSET.setOffset(4);
184 | break;
185 | case Build.VERSION_CODES.M:
186 | ART_QUICK_CODE_OFFSET.setOffset(36);
187 | ART_JNI_ENTRY_OFFSET.setOffset(32);
188 | ART_ACCESS_FLAG_OFFSET.setOffset(12);
189 | break;
190 | case Build.VERSION_CODES.LOLLIPOP_MR1:
191 | ART_QUICK_CODE_OFFSET.setOffset(44);
192 | ART_JNI_ENTRY_OFFSET.setOffset(40);
193 | ART_ACCESS_FLAG_OFFSET.setOffset(20);
194 | break;
195 | case Build.VERSION_CODES.LOLLIPOP:
196 | ART_QUICK_CODE_OFFSET.setOffset(40);
197 | ART_QUICK_CODE_OFFSET.setLength(BitWidth.QWORD);
198 | ART_JNI_ENTRY_OFFSET.setOffset(32);
199 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD);
200 | ART_ACCESS_FLAG_OFFSET.setOffset(56);
201 | break;
202 | case Build.VERSION_CODES.KITKAT:
203 | ART_QUICK_CODE_OFFSET.setOffset(32);
204 | ART_ACCESS_FLAG_OFFSET.setOffset(28);
205 | break;
206 | default:
207 | throw new RuntimeException("API LEVEL: " + apiLevel + " is not supported now : (");
208 | }
209 | }
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/library/src/main/jniLibs/armeabi/libdexposed.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/library/src/main/jniLibs/armeabi/libdexposed.so
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | epic
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':jhook', ':library'
2 |
--------------------------------------------------------------------------------