当前位置:   article > 正文

启动没有在 AndroidManifest 中注册的 Activity_have you declared this activity in your androidman

have you declared this activity in your androidmanifest.xml, or does your in

一、报错

启动没有在 AndroidManifest 中注册的 Activity,会报错:

android.content.ActivityNotFoundException: Unable to find explicit activity class {...}; have you declared this activity in your AndroidManifest.xml?
  • 1

二、思路

Android 使用的是 C/S 架构,我们的 app 是 client 客户端,内核是 Server 服务端。

Activity 是否注册的验证是在服务端进行的,所以我们客户端无法修改判断条件。但我们可以修改客户端的请求,让服务端以为我们要启动的是另外一个已注册的 Acitivity,在客户端得到启动许可后,再去启动真正的目标 Activity。

这一操作要通过 hook 来实现。

在这里插入图片描述

三、启动流程

android 28 启动流程:
在这里插入图片描述

3.1 hook 点的选择

替换:

在 android 28 上,通过 ActivityManager.getService().startActivity() 来向服务端发起请求。所以我们要在这一步之前,将要启动的 Activity 替换为已注册的 Activity。

恢复:

通过 mInstrumentation.newActivity() 来创建 Activity。所以我们要在这一步之前,将要启动的 Activity 替换回未注册的 Activity。

本文选择的替换点是 ActivityManager.getService().startActivity(),即要 hook ActivityManager.getService()

恢复点是:ActivityThread#mH.handleMessage(),即要 hook ActivityThread#mH#mCallback

3.2 版本差异

获取 AMSP:

  • android 26 及以上:
    ActivityManager.getService()

  • android 26 以下:
    ActivityManagerNative.getDefault()

处理启动 Activity 的 message:

  • android 28 及以上
    handleMessage(ActivityThread.H.EXECUTE_TRANSACTION)

  • android 28 以下:
    handleMessage(H.LAUNCH_ACTIVITY)

四、代码

完整代码见 github.com/Gdeeer

新建三个 Activity:

  • CommonActivity 已注册,用于点击跳转 TargetActivity
  • StubActivity 已注册,用于占位
  • TargetActivity 已注册,用于跳转

4.1 Hook

AMSPHookHelper

class AMSPHookHelper {
    static final String EXTRA_TARGET_INTENT = "extra_target_intent";


    static void hookAMSP() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            hookAMSPBefore26();
        } else {
            hookAMSPSince26();
        }
    }


    /**
     * android 26 以下版本 AMSP 的 hook
     */
    private static void hookAMSPBefore26() {
        try {
            Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
            Object gDefault = FieldUtils.readStaticField(classActivityManagerNative, "gDefault");
            Object mInstance = FieldUtils.readField(gDefault, "mInstance");
            Class classIActivityManager = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{classIActivityManager},
                new MockAMSP(mInstance)
            );

            FieldUtils.writeField(gDefault, "mInstance", proxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * android 26 及以上版本 AMSP 的 hook
     */
    private static void hookAMSPSince26() {
        try {
            Object IActivityManagerSingleton = FieldUtils.readStaticField(ActivityManager.class, "IActivityManagerSingleton");
            Object mInstance = FieldUtils.readField(IActivityManagerSingleton, "mInstance");
            Class classIActivityManager = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{classIActivityManager},
                new MockAMSP(mInstance)
            );

            FieldUtils.writeField(IActivityManagerSingleton, "mInstance", proxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void hookActivityThread() {
        try {
            Class classActivityThread = Class.forName("android.app.ActivityThread");
            Object currentActivityThread = FieldUtils.readStaticField(classActivityThread, "sCurrentActivityThread");
            Handler mH = (Handler) FieldUtils.readField(currentActivityThread, "mH");
            FieldUtils.writeField(mH, "mCallback", new MockHCallback(mH));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

StubActivityApplication

public class StubActivityApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        // 解除 android P 上的私有 api 限制,见http://weishu.me/2018/06/07/free-reflection-above-android-p/
        Reflection.unseal(base);

        // hook
        AMSPHookHelper.hookAMSP();
        AMSPHookHelper.hookActivityThread();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.2 替换点

MockAMSP:

public class MockAMSP implements InvocationHandler {
    private static final String START_ACTIVITY = "startActivity";

    private Object mBase;

    MockAMSP(Object base) {
        mBase = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (START_ACTIVITY.equals(method.getName())) {
            // 找到旧的 intent
            Intent raw;
            int index = 0;
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Intent) {
                    index = i;
                    break;
                }
            }
            raw = (Intent) args[index];

            // 创建新的 intent
            Intent newIntent = new Intent();
            String stubPackage = "com.gdeer.gdtesthub";
            ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
            newIntent.setComponent(componentName);
            newIntent.putExtra(AMSPHookHelper.EXTRA_TARGET_INTENT, raw);

            // 替换旧的 intent 为新的 intent
            args[index] = newIntent;

            // 调用 "startActivity" 方法
            return method.invoke(mBase, args);
        }
        return method.invoke(mBase, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

4.3 恢复点

MockHCallback:

public class MockHCallback implements Handler.Callback {
    /**
     * android 28 以下,ActivityThread$H.LAUNCH_ACTIVITY = 100
     */
    private static final int LAUNCH_ACTIVITY = 100;

    /**
     * android 28 上,ActivityThread$H.EXECUTE_TRANSACTION = 159
     */
    private static final int EXECUTE_TRANSACTION = 159;

    private Handler mBase;

    MockHCallback(Handler base) {
        mBase = base;
    }

    @Override
    public boolean handleMessage(Message msg) {
        handleLaunchActivity(msg);
        mBase.handleMessage(msg);
        return true;
    }

    private void handleLaunchActivity(Message msg) {
        Log.d(TAG, "handleLaunchActivity");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            if (msg.what == EXECUTE_TRANSACTION) {
                handleLaunchActivitySince28(msg);
            }
        } else {
            if (msg.what == LAUNCH_ACTIVITY) {
                handleLaunchActivityBefore28(msg);
            }
        }
    }

    private void handleLaunchActivityBefore28(Message msg) {
        try {
            Object obj = msg.obj;
            if (obj != null) {
                Intent raw = (Intent) FieldUtils.readField(obj, "intent");
                Intent target = raw.getParcelableExtra(AMSPHookHelper.EXTRA_TARGET_INTENT);
                if (target != null) {
                    raw.setComponent(target.getComponent());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleLaunchActivitySince28(Message msg) {
        try {
            Object mActivityCallbacks = FieldUtils.readField(msg.obj, "mActivityCallbacks");
            if (mActivityCallbacks != null) {
                List<?> list = (List<?>) mActivityCallbacks;
                if (list.size() > 0) {
                    Object listItem = list.get(0);
                    Class classLaunchActivityItem = Class.forName("android.app.servertransaction.LaunchActivityItem");
                    if (listItem.getClass() == classLaunchActivityItem) {
                        Intent raw = (Intent) FieldUtils.readField(listItem, "mIntent");
                        Intent target = raw.getParcelableExtra(AMSPHookHelper.EXTRA_TARGET_INTENT);
                        raw.setComponent(target.getComponent());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

补充:FiledUtil 工具类

public class FieldUtils {

    private static Map<String, Field> sFieldCache = new HashMap<String, Field>();

    private static String getKey(Class<?> cls, String fieldName) {
        StringBuilder sb = new StringBuilder();
        sb.append(cls.toString()).append("#").append(fieldName);
        return sb.toString();
    }

    private static Field getField(Class<?> cls, String fieldName, final boolean forceAccess) throws ReflectIllegalArgumentsException {
        Validate.assertTrue(cls != null, "The class must not be null");
        Validate.assertTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");

        String key = getKey(cls, fieldName);
        Field cachedField;
        synchronized (sFieldCache) {
            cachedField = sFieldCache.get(key);
        }
        if (cachedField != null) {
            if (forceAccess && !cachedField.isAccessible()) {
                cachedField.setAccessible(true);
            }
            return cachedField;
        }

        // check up the superclass hierarchy
        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                final Field field = acls.getDeclaredField(fieldName);
                // getDeclaredField checks for non-public scopes as well
                // and it returns accurate results
                if (!Modifier.isPublic(field.getModifiers())) {
                    if (forceAccess) {
                        field.setAccessible(true);
                    } else {
                        continue;
                    }
                }
                synchronized (sFieldCache) {
                    sFieldCache.put(key, field);
                }
                return field;
            } catch (final NoSuchFieldException ex) { // NOPMD
                // ignore
            }
        }
        // check the public interface case. This must be manually searched for
        // incase there is a public supersuperclass field hidden by a private/package
        // superclass field.
        Field match = null;
        for (final Class<?> class1 : ReflectUtils.getAllInterfaces(cls)) {
            try {
                final Field test = class1.getField(fieldName);
                Validate.assertTrue(match == null, "Reference to field %s is ambiguous relative to %s"
                        + "; a matching field exists on two or more implemented interfaces.",
                        fieldName, cls);
                match = test;
            } catch (final NoSuchFieldException ex) { // NOPMD
                // ignore
            }
        }
        synchronized (sFieldCache) {
            sFieldCache.put(key, match);
        }
        return match;
    }

    public static Object readField(final Field field, final Object target, final boolean
            forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(field != null, "The field must not be null");
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            MemberUtils.setAccessibleWorkaround(field);
        }
        return field.get(target);
    }


    public static void writeField(final Field field, final Object target, final Object value,
                                  final boolean forceAccess)
            throws IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(field != null, "The field must not be null");
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            MemberUtils.setAccessibleWorkaround(field);
        }
        field.set(target, value);
    }


    public static Object readField(final Field field, final Object target) throws
            IllegalAccessException, ReflectIllegalArgumentsException {
        return readField(field, target, true);
    }

    public static Field getField(final Class<?> cls, final String fieldName) throws ReflectIllegalArgumentsException {
        return getField(cls, fieldName, true);
    }


    public static Object readField(final Object target, final String fieldName) throws
            IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(target != null, "target object must not be null");
        final Class<?> cls = target.getClass();
        final Field field = getField(cls, fieldName, true);
        Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
        // already forced access above, don't repeat it here:
        return readField(field, target, false);
    }

    public static Object readField(final Object target, final String fieldName, final boolean
            forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(target != null, "target object must not be null");
        final Class<?> cls = target.getClass();
        final Field field = getField(cls, fieldName, forceAccess);
        Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
        // already forced access above, don't repeat it here:
        return readField(field, target, forceAccess);
    }


    public static void writeField(final Object target, final String fieldName, final Object
            value) throws IllegalAccessException, ReflectIllegalArgumentsException {
        writeField(target, fieldName, value, true);
    }

    public static void writeField(final Object target, final String fieldName, final Object
            value, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(target != null, "target object must not be null");
        final Class<?> cls = target.getClass();
        final Field field = getField(cls, fieldName, true);
        Validate.assertTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(),
                fieldName);
        // already forced access above, don't repeat it here:
        writeField(field, target, value, forceAccess);
    }

    public static void writeField(final Field field, final Object target, final Object value)
            throws IllegalAccessException, ReflectIllegalArgumentsException {
        writeField(field, target, value, true);
    }

    public static Object readStaticField(final Field field, final boolean forceAccess) throws
            IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(field != null, "The field must not be null");
        Validate.assertTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static",
                field.getName());
        return readField(field, (Object) null, forceAccess);
    }

    public static Object readStaticField(final Class<?> cls, final String fieldName) throws
            IllegalAccessException, ReflectIllegalArgumentsException {
        final Field field = getField(cls, fieldName, true);
        Validate.assertTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
        // already forced access above, don't repeat it here:
        return readStaticField(field, true);
    }

    public static void writeStaticField(final Field field, final Object value, final boolean
            forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(field != null, "The field must not be null");
        Validate.assertTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static",
                field.getDeclaringClass().getName(),
                field.getName());
        writeField(field, (Object) null, value, forceAccess);
    }


    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object
            value) throws IllegalAccessException, ReflectIllegalArgumentsException {
        final Field field = getField(cls, fieldName, true);
        Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
        // already forced access above, don't repeat it here:
        writeStaticField(field, value, true);
    }

    public static Field getDeclaredField(final Class<?> cls, final String fieldName, final
    boolean forceAccess) throws ReflectIllegalArgumentsException {
        Validate.assertTrue(cls != null, "The class must not be null");
        Validate.assertTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");
        try {
            // only consider the specified class by using getDeclaredField()
            final Field field = cls.getDeclaredField(fieldName);
            if (!MemberUtils.isAccessible(field)) {
                if (forceAccess) {
                    field.setAccessible(true);
                } else {
                    return null;
                }
            }
            return field;
        } catch (final NoSuchFieldException e) { // NOPMD
            // ignore
        }
        return null;
    }

    public static void writeDeclaredField(final Object target, final String fieldName, final
    Object value) throws IllegalAccessException, ReflectIllegalArgumentsException {
        Validate.assertTrue(target != null, "target object must not be null");
        final Class<?> cls = target.getClass();
        final Field field = getDeclaredField(cls, fieldName, true);
        Validate.assertTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(),
                fieldName);
        // already forced access above, don't repeat it here:
        writeField(field, target, value, false);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/670621
推荐阅读
相关标签
  

闽ICP备14008679号