() {
68 | @Override
69 | protected IBinder queryBaseBinder() {
70 | return ServiceManager.getService(Context.ACTIVITY_SERVICE);
71 | }
72 |
73 | @Override
74 | protected IActivityManager createInterface(IBinder baseBinder) {
75 | return getHookObject().getProxyObject();
76 | }
77 | };
78 | hookAMBinder.injectService(Context.ACTIVITY_SERVICE);
79 | }
80 |
81 | @Override
82 | public boolean isEnvBad() {
83 | return getAMN() != getHookObject().getProxyObject();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Project/commonhook/src/main/java/dodola/common/hook/base/PatchObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
3 | */
4 | package dodola.common.hook.base;
5 |
6 | import android.os.Build;
7 |
8 | import java.lang.reflect.Constructor;
9 |
10 | /**
11 | * @author Lody
12 | *
13 | *
14 | * 所有注入器的基类,使用Patch注解来添加Hook.
15 | * @see Patch
16 | */
17 | public abstract class PatchObject> implements Injectable {
18 |
19 | protected H hookObject;
20 |
21 | protected T baseObject;
22 |
23 | public PatchObject() {
24 | this.hookObject = initHookObject();
25 | applyHooks();
26 | afterHookApply(hookObject);
27 | }
28 |
29 | public PatchObject(T baseObject) {
30 | this.baseObject = baseObject;
31 | this.hookObject = initHookObject();
32 | applyHooks();
33 | afterHookApply(hookObject);
34 | }
35 |
36 | /**
37 | * 初始化Hook对象
38 | */
39 | protected abstract H initHookObject();
40 |
41 | /**
42 | * 打入Hooks的Impl
43 | */
44 | protected void applyHooks() {
45 |
46 | if (hookObject != null) {
47 | Class extends PatchObject> clazz = getClass();
48 | Patch patch = clazz.getAnnotation(Patch.class);
49 | int version = Build.VERSION.SDK_INT;
50 | if (patch != null) {
51 | Class extends Hook>[] hookTypes = patch.value();
52 | for (Class extends Hook> hookType : hookTypes) {
53 | ApiLimit apiLimit = hookType.getAnnotation(ApiLimit.class);
54 | boolean needToAddHook = true;
55 | if (apiLimit != null) {
56 | int apiStart = apiLimit.start();
57 | int apiEnd = apiLimit.end();
58 | boolean highThanStart = apiStart == -1 || version > apiStart;
59 | boolean lowThanEnd = apiEnd == -1 || version < apiEnd;
60 | if (!highThanStart || !lowThanEnd) {
61 | needToAddHook = false;
62 | }
63 | }
64 | if (needToAddHook) {
65 | addHook(hookType);
66 | }
67 | }
68 |
69 | }
70 | }
71 | }
72 |
73 | protected void addHook(Class extends Hook> hookType) {
74 | try {
75 | Constructor> constructor = hookType.getDeclaredConstructors()[0];
76 | if (!constructor.isAccessible()) {
77 | constructor.setAccessible(true);
78 | }
79 | Hook hook = (Hook) constructor.newInstance(this);
80 | hookObject.addHook(hook);
81 | } catch (Throwable e) {
82 | throw new RuntimeException("Unable to instance Hook : " + hookType + " :" + e.getMessage());
83 | }
84 | }
85 |
86 | /**
87 | * Hook添加完成后回调
88 | *
89 | * @param hookObject HookObject
90 | */
91 | protected void afterHookApply(H hookObject) {
92 | }
93 |
94 | /**
95 | * 注入的Impl
96 | *
97 | * @throws Throwable
98 | */
99 | @Override
100 | public abstract void inject() throws Throwable;
101 |
102 | public H getHookObject() {
103 | return hookObject;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Project/demo1/src/main/java/dodola/deepin/hook/HCallbackHook.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
3 | */
4 | package dodola.deepin.hook;
5 |
6 | import android.app.ActivityThread;
7 | import android.content.Intent;
8 | import android.os.Handler;
9 | import android.os.Message;
10 |
11 | import java.lang.reflect.Field;
12 |
13 | import dodola.common.hook.base.Injectable;
14 | import dodola.common.utils.ActivityRecordCompat;
15 | import dodola.common.utils.ExtraConstants;
16 | import dodola.deepin.demo1.DodoApplication;
17 |
18 | public class HCallbackHook implements Handler.Callback, Injectable {
19 |
20 | public static final int LAUNCH_ACTIVITY = 100;
21 |
22 | private static final String TAG = HCallbackHook.class.getSimpleName();
23 | private static final HCallbackHook sCallback = new HCallbackHook();
24 | private static Field f_h;
25 | private static Field f_handleCallback;
26 |
27 | static {
28 | try {
29 | f_h = ActivityThread.class.getDeclaredField("mH");
30 | f_handleCallback = Handler.class.getDeclaredField("mCallback");
31 | f_h.setAccessible(true);
32 | f_handleCallback.setAccessible(true);
33 | } catch (NoSuchFieldException e) {
34 | // Ignore
35 | }
36 | }
37 |
38 | /**
39 | * 其它插件化可能也会注入Activity$H, 这里要保留其它插件化的Callback引用,我们的Callback完事后再调用它的。
40 | */
41 | private Handler.Callback otherCallback;
42 |
43 | private HCallbackHook() {
44 | }
45 |
46 | public static HCallbackHook getDefault() {
47 | return sCallback;
48 | }
49 |
50 | public static Handler getH() {
51 | try {
52 | return (Handler) f_h.get(DodoApplication.mainThread);
53 | } catch (Throwable e) {
54 | e.printStackTrace();
55 | }
56 | return null;
57 | }
58 |
59 | private static Handler.Callback getHCallback() {
60 | try {
61 | Handler handler = getH();
62 |
63 | return (Handler.Callback) f_handleCallback.get(handler);
64 | } catch (Throwable e) {
65 | e.printStackTrace();
66 | }
67 | return null;
68 | }
69 |
70 | @Override
71 | public boolean handleMessage(Message msg) {
72 | switch (msg.what) {
73 | case LAUNCH_ACTIVITY: {
74 | if (!handleLaunchActivity(msg)) {
75 | return true;
76 | }
77 | break;
78 | }
79 | }
80 | if (true) {
81 | return false;
82 | }
83 | // 向下调用兼容其它的插件化
84 | return otherCallback != null && otherCallback.handleMessage(msg);
85 | }
86 |
87 | private boolean handleLaunchActivity(Message msg) {
88 | Object r = msg.obj;
89 | //Callback回来我们需要替换成我们需要启动的,因为没有注册的Activity获取不到ActivityInfo在前面验证会直接报错,
90 | //所以我们用桩的name让系统获取到一个可用的ActivityInfo,跳过验证,最终会回到这里,这里一定要处理否则掉起的就是桩的Activity了
91 |
92 | Intent stubIntent = ActivityRecordCompat.getIntent(r);
93 |
94 | // TargetIntent
95 | Intent targetIntent = stubIntent.getParcelableExtra(ExtraConstants.EXTRA_TARGET_INTENT);
96 | stubIntent.setComponent(targetIntent.getComponent());//换回去,你懂的
97 |
98 | return true;
99 | }
100 |
101 | @Override
102 | public void inject() throws Throwable {
103 | otherCallback = getHCallback();
104 | f_handleCallback.set(getH(), this);
105 | }
106 |
107 | @Override
108 | public boolean isEnvBad() {
109 | Handler.Callback callback = getHCallback();
110 | boolean envBad = callback != this;
111 | if (envBad) {
112 | }
113 | return envBad;
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/Project/demo1/src/main/java/dodola/deepin/demo1/DodoApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
3 | */
4 | package dodola.deepin.demo1;
5 |
6 | import android.app.ActivityThread;
7 | import android.app.Application;
8 | import android.content.Context;
9 | import android.content.pm.ActivityInfo;
10 | import android.content.pm.ComponentInfo;
11 | import android.content.pm.PackageInfo;
12 | import android.content.pm.PackageManager;
13 | import android.os.Bundle;
14 | import android.text.TextUtils;
15 |
16 | import dodola.deepin.patch.PatchManager;
17 |
18 | /**
19 | * Created by sunpengfei on 16/7/12.
20 | */
21 |
22 | public class DodoApplication extends Application {
23 | public static PackageManager mOriginPackageManager;
24 | public static ActivityInfo info;
25 | public static ActivityThread mainThread;
26 | private static ProcessType processType;
27 | private String processName;
28 | private String mainProcessName;
29 |
30 | public static boolean isAppClient() {
31 | return processType == ProcessType.VAppClient;
32 | }
33 |
34 | @Override
35 | protected void attachBaseContext(Context base) {
36 | super.attachBaseContext(base);
37 | //从PackageManager里拿取全部的Activity桩
38 | initStub();
39 | mainThread = ActivityThread.currentActivityThread();
40 | // 主进程名
41 | mainProcessName = base.getApplicationInfo().processName;
42 | // 当前进程名
43 | processName = mainThread.getProcessName();
44 | if (processName.equals(mainProcessName)) {
45 | processType = ProcessType.Main;
46 | } else if (isAppProcess(processName)) {
47 | processType = ProcessType.VAppClient;
48 | } else {
49 | processType = ProcessType.CHILD;
50 | }
51 | mOriginPackageManager = this.getPackageManager();
52 | //此处注入补丁
53 | try {
54 | PatchManager.getInstance().injectAll();
55 | PatchManager.getInstance().checkEnv();
56 | } catch (Throwable throwable) {
57 | throwable.printStackTrace();
58 | }
59 |
60 | }
61 |
62 | private boolean isAppProcess(String processName) {
63 | return "dodola.deepin.demo1:v1".equals(processName);
64 | }
65 |
66 | private void initStub() {
67 | PackageManager pm = this.getPackageManager();
68 | PackageInfo packageInfo = null;
69 | try {
70 | packageInfo = pm.getPackageInfo(this.getPackageName(),
71 | PackageManager.GET_ACTIVITIES | PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);
72 | } catch (PackageManager.NameNotFoundException e) {
73 | e.printStackTrace();
74 | }
75 |
76 | if (packageInfo == null) {
77 | throw new RuntimeException("Unable to found PackageInfo : " + this.getPackageName());
78 | }
79 |
80 | ActivityInfo[] activityInfos = packageInfo.activities;
81 | for (ActivityInfo activityInfo : activityInfos) {
82 | if (isStubComponent(activityInfo)) {
83 | info = activityInfo;//只写一个桩
84 | break;
85 | }
86 | }
87 | }
88 |
89 | private boolean isStubComponent(ComponentInfo componentInfo) {
90 | Bundle metaData = componentInfo.metaData;
91 | return metaData != null
92 | && TextUtils.equals(metaData.getString("dodometa"), "dododola");
93 | }
94 |
95 | @Override
96 | public void onCreate() {
97 | super.onCreate();
98 | }
99 |
100 | public static Object mainThread() {
101 | return null;
102 | }
103 |
104 |
105 | /**
106 | * 进程类型
107 | */
108 | enum ProcessType {
109 | /**
110 | * 服务端进程
111 | */
112 | Server,
113 | /**
114 | * 插件客户端进程
115 | */
116 | VAppClient,
117 | /**
118 | * 主进程
119 | */
120 | Main,
121 | /**
122 | * 子进程
123 | */
124 | CHILD
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/Project/commonhook/src/main/java/dodola/common/hook/base/HookObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
3 | */
4 | package dodola.common.hook.base;
5 |
6 | import android.text.TextUtils;
7 |
8 | import java.lang.reflect.InvocationHandler;
9 | import java.lang.reflect.InvocationTargetException;
10 | import java.lang.reflect.Method;
11 | import java.lang.reflect.Proxy;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | /**
16 | * @author Lody
17 | *
18 | *
19 | * 对一个要Hook的对象进行包装.
20 | */
21 | @SuppressWarnings("unchecked")
22 | public class HookObject implements IHookObject {
23 |
24 | private static final String TAG = HookObject.class.getSimpleName();
25 | private T mBaseObject;
26 | private T mProxyObject;
27 | /**
28 | * 内部维护的Hook集合
29 | */
30 | private Map internalHookMapping = new HashMap();
31 |
32 | public HookObject(T baseObject, Class>... proxyInterfaces) {
33 | this(baseObject == null ? null : baseObject.getClass().getClassLoader(), baseObject, proxyInterfaces);
34 | }
35 |
36 | public HookObject(ClassLoader cl, T baseObject, Class>... proxyInterfaces) {
37 | this.mBaseObject = baseObject;
38 | if (mBaseObject != null) {
39 | if (proxyInterfaces == null) {
40 | proxyInterfaces = baseObject.getClass().getInterfaces();
41 | }
42 | mProxyObject = (T) Proxy.newProxyInstance(cl, proxyInterfaces, new HookHandler());
43 | }
44 | }
45 |
46 | public HookObject(T baseObject) {
47 | this(baseObject, (Class>[]) null);
48 | }
49 |
50 | /**
51 | * 添加一个Hook
52 | *
53 | * @param hook 要添加的Hook
54 | */
55 | @Override
56 | public void addHook(Hook hook) {
57 | if (hook != null && !TextUtils.isEmpty(hook.getName())) {
58 | if (internalHookMapping.containsKey(hook.getName())) {
59 | }
60 | internalHookMapping.put(hook.getName(), hook);
61 | }
62 | }
63 |
64 | /**
65 | * 移除一个Hook
66 | *
67 | * @param hookName 要移除的Hook名
68 | * @return 移除的Hook
69 | */
70 | @Override
71 | public Hook removeHook(String hookName) {
72 | return internalHookMapping.remove(hookName);
73 | }
74 |
75 | /**
76 | * 移除一个Hook
77 | *
78 | * @param hook 要移除的Hook
79 | */
80 | @Override
81 | public void removeHook(Hook hook) {
82 | if (hook != null) {
83 | removeHook(hook.getName());
84 | }
85 | }
86 |
87 | /**
88 | * 移除全部Hook
89 | */
90 | @Override
91 | public void removeAllHook() {
92 | internalHookMapping.clear();
93 | }
94 |
95 | /**
96 | * 取得指定名称的Hook
97 | *
98 | * @param name Hook名
99 | * @param Hook类型
100 | * @return 指定名称的Hook
101 | */
102 | @SuppressWarnings("unchecked")
103 | @Override
104 | public H getHook(String name) {
105 | return (H) internalHookMapping.get(name);
106 | }
107 |
108 | /**
109 | * @return 包装后的代理对象
110 | */
111 | @Override
112 | public T getProxyObject() {
113 | return mProxyObject;
114 | }
115 |
116 | /**
117 | * @return 原对象
118 | */
119 | @Override
120 | public T getBaseObject() {
121 | return mBaseObject;
122 | }
123 |
124 | /**
125 | * @return Hook数量
126 | */
127 | @Override
128 | public int getHookCount() {
129 | return internalHookMapping.size();
130 | }
131 |
132 | private class HookHandler implements InvocationHandler {
133 | @Override
134 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
135 | Hook hook = getHook(method.getName());
136 | try {
137 | if (hook != null && hook.isEnable()) {
138 | return hook.onHook(mBaseObject, method, args);
139 | }
140 | return method.invoke(mBaseObject, args);
141 | } catch (Throwable e) {
142 | if (e instanceof InvocationTargetException) {
143 | throw e.getCause();
144 | } else {
145 | throw e;
146 | }
147 | }
148 | }
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/Project/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/Project/commonhook/src/main/java/dodola/common/hook/base/HookBinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
3 | */
4 | package dodola.common.hook.base;
5 |
6 | import android.annotation.TargetApi;
7 | import android.os.Build;
8 | import android.os.IBinder;
9 | import android.os.IInterface;
10 | import android.os.Parcel;
11 | import android.os.RemoteException;
12 | import android.os.ServiceManager;
13 | import android.text.TextUtils;
14 |
15 | import java.io.FileDescriptor;
16 | import java.lang.reflect.Field;
17 | import java.lang.reflect.InvocationHandler;
18 | import java.lang.reflect.InvocationTargetException;
19 | import java.lang.reflect.Method;
20 | import java.lang.reflect.Proxy;
21 | import java.util.HashMap;
22 | import java.util.Map;
23 |
24 | /**
25 | * @author Lody
26 | *
27 | *
28 | * 掌握ServiceManager中的实权.代理所有接口.
29 | */
30 | @SuppressWarnings("unchecked")
31 | public abstract class HookBinder implements IHookObject, IBinder {
32 |
33 | private static final String TAG = HookBinder.class.getSimpleName();
34 | private static Map sCache;
35 |
36 | static {
37 | try {
38 | Class.forName(ServiceManager.class.getName());
39 | Field f_sCache = ServiceManager.class.getDeclaredField("sCache");
40 | f_sCache.setAccessible(true);
41 | sCache = (Map) f_sCache.get(null);
42 | } catch (Throwable e) {
43 | // 不考虑
44 | }
45 | }
46 |
47 | private IBinder baseBinder;
48 | private Interface mBaseObject;
49 | private Interface mProxyObject;
50 | /**
51 | * 内部维护的Hook集合
52 | */
53 | private Map internalHookMapping = new HashMap();
54 |
55 | public HookBinder() {
56 | bind();
57 | }
58 |
59 | public static Map getsCache() {
60 | return sCache;
61 | }
62 |
63 | protected abstract IBinder queryBaseBinder();
64 |
65 | protected abstract Interface createInterface(IBinder baseBinder);
66 |
67 | public final void bind() {
68 | this.baseBinder = queryBaseBinder();
69 | if (baseBinder != null) {
70 | this.mBaseObject = createInterface(baseBinder);
71 | mProxyObject = (Interface) Proxy.newProxyInstance(mBaseObject.getClass().getClassLoader(),
72 | mBaseObject.getClass().getInterfaces(), new HookHandler());
73 |
74 | }
75 | }
76 |
77 | @Override
78 | public String getInterfaceDescriptor() throws RemoteException {
79 | return baseBinder.getInterfaceDescriptor();
80 | }
81 |
82 | @Override
83 | public boolean pingBinder() {
84 | return baseBinder.pingBinder();
85 | }
86 |
87 | @Override
88 | public boolean isBinderAlive() {
89 | return baseBinder.isBinderAlive();
90 | }
91 |
92 | @Override
93 | public IInterface queryLocalInterface(String descriptor) {
94 | return getProxyObject();
95 | }
96 |
97 | @Override
98 | public void dump(FileDescriptor fd, String[] args) throws RemoteException {
99 | baseBinder.dump(fd, args);
100 | }
101 |
102 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
103 | @Override
104 | public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
105 | baseBinder.dumpAsync(fd, args);
106 | }
107 |
108 | @Override
109 | public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
110 | return baseBinder.transact(code, data, reply, flags);
111 | }
112 |
113 | @Override
114 | public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
115 | baseBinder.linkToDeath(recipient, flags);
116 | }
117 |
118 | @Override
119 | public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
120 | return baseBinder.unlinkToDeath(recipient, flags);
121 | }
122 |
123 | public IBinder getBaseBinder() {
124 | return baseBinder;
125 | }
126 |
127 | public void injectService(String name) throws Throwable {
128 | if (sCache != null) {
129 | sCache.remove(name);
130 | sCache.put(name, this);
131 | } else {
132 | // 不会发生
133 | throw new IllegalStateException("ServiceManager is invisible.");
134 | }
135 | }
136 |
137 | public void restoreService(String name) {
138 | if (sCache != null) {
139 | if (sCache.remove(name) != null) {
140 | sCache.put(name, baseBinder);
141 | }
142 | }
143 | }
144 |
145 | /**
146 | * 添加一个Hook
147 | *
148 | * @param hook 要添加的Hook
149 | */
150 | @Override
151 | public void addHook(Hook hook) {
152 | if (hook != null && !TextUtils.isEmpty(hook.getName())) {
153 | if (internalHookMapping.containsKey(hook.getName())) {
154 | }
155 | internalHookMapping.put(hook.getName(), hook);
156 | }
157 | }
158 |
159 | /**
160 | * 移除一个Hook
161 | *
162 | * @param hookName 要移除的Hook名
163 | * @return 移除的Hook
164 | */
165 | @Override
166 | public Hook removeHook(String hookName) {
167 | return internalHookMapping.remove(hookName);
168 | }
169 |
170 | /**
171 | * 移除一个Hook
172 | *
173 | * @param hook 要移除的Hook
174 | */
175 | @Override
176 | public void removeHook(Hook hook) {
177 | if (hook != null) {
178 | removeHook(hook.getName());
179 | }
180 | }
181 |
182 | /**
183 | * 移除全部Hook
184 | */
185 | @Override
186 | public void removeAllHook() {
187 | internalHookMapping.clear();
188 | }
189 |
190 | /**
191 | * 取得指定名称的Hook
192 | *
193 | * @param name Hook名
194 | * @param Hook类型
195 | * @return 指定名称的Hook
196 | */
197 | @Override
198 | public H getHook(String name) {
199 | return (H) internalHookMapping.get(name);
200 | }
201 |
202 | /**
203 | * @return 包装后的代理对象
204 | */
205 | @Override
206 | public Interface getProxyObject() {
207 | return mProxyObject;
208 | }
209 |
210 | /**
211 | * @return 原对象
212 | */
213 | @Override
214 | public Interface getBaseObject() {
215 | return mBaseObject;
216 | }
217 |
218 | /**
219 | * @return Hook数量
220 | */
221 | @Override
222 | public int getHookCount() {
223 | return internalHookMapping.size();
224 | }
225 |
226 | private class HookHandler implements InvocationHandler {
227 |
228 | @Override
229 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
230 | Hook hook = getHook(method.getName());
231 | try {
232 | if (hook != null && hook.isEnable()) {
233 | return hook.onHook(mBaseObject, method, args);
234 | }
235 | return method.invoke(mBaseObject, args);
236 | } catch (Throwable e) {
237 | if (e instanceof InvocationTargetException) {
238 | throw e.getCause();
239 | } else {
240 | throw e;
241 | }
242 | }
243 | }
244 | }
245 | }
246 |
--------------------------------------------------------------------------------