简述

要发动未注册的Activity首要是要躲避AMS的检测,思路是,检测前要发动的Activity换成注册的,检测经过了,再在发动前换回来。这儿首要是两个点。检测前,hookAMS。检测后hookHandler。hook点有许多尽量找静态变量单例public

hookAMS

1、android 11举例,发动acitivty是在ATMS中(11之前是AMS,这个自己能够去适配)

如果启动一个未注册的Activity

2、拿到ATMS的署理。

3、然后ATMS整个动态署理在startActivity之前将Intent 偷梁换柱

4、换成现已注册的Activity之后记住原目标Acitivty存起来,在骗完AMS之后换回来


public static void hookAMS() {
    // 10之前
    try {
        Class<?> clazz = Class.forName("android.app.ActivityTaskManager");
        Field singletonField = clazz.getDeclaredField("IActivityTaskManagerSingleton");
        singletonField.setAccessible(true);
        Object singleton = singletonField.get(null);
        Class<?> singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceField = singletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        Method getMethod = singletonClass.getMethod("get");
        Object mInstance = getMethod.invoke(singleton);
        Class IActivityTaskManagerClass = Class.forName("android.app.IActivityTaskManager");
        Object mInstanceProxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{IActivityTaskManagerClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if ("startActivity".equals(method.getName())) {
                            int index = -1;
                            // 获取 Intent 参数在 args 数组中的index值
                            for (int i = 0; i < args.length; i++) {
                                if (args[i] instanceof Intent) {
                                    index = i;
                                    break;
                                }
                            }
                            // 生成署理proxyIntent -- 孙悟空(署理)的Intent
                            Intent proxyIntent = new Intent();
                            // 这个包名是宿主的
                            proxyIntent.setClassName("com.leo.amsplugin",
                                    ProxyActivity.class.getName());
                            // 原始Intent能丢掉吗?保存原始的Intent目标
                            Intent intent = (Intent) args[index];
                            proxyIntent.putExtra(TARGET_INTENT, intent);
                            // 运用proxyIntent替换数组中的Intent
                            args[index] = proxyIntent;
                        }
                        // 本来流程
                        return method.invoke(mInstance, args);
                    }
                });
        // 用署理的目标替换体系的目标
        mInstanceField.set(singleton, mInstanceProxy);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

hookHandler

hookAMS完成,欺骗了AMS,接下来要把Intent中的原目标扶起回正位, 发动Activity要用handler,咱们从这儿hook吧

1、Activtiy thread 中的handler用来发动activity class H extends Handler

2、handlerMessage中的EXECUTE_TRANSACTION(159)来发动activity

3、 final ClientTransaction transaction = (ClientTransaction) msg.obj;–包含Intent

mTransactionExecutor.execute(transaction);–履行发动

launchActivityItem中有Intent,而ta继承于ClientTransactionItem,而ClientTransaction中包含List<ClientTransactionItem>

4、所以我只需拿到msg就能够拿到Intent。 msg.obj –> ClientTransaction –> List mActivityCallbacks(LaunchActivityItem) –> private Intent mIntent 替换

如果启动一个未注册的Activity

5、handlerMessage(MSG)之前有个callback也能够拿到msg。则会callback是一个接口,如果重写这个接口可就可重新handlerMessage这个办法,然后操作msg。

6、ActivityThread当中,Handler的构建没有传参数。

...//去ActivityThread.java里看
@UnsupportedAppUsage
final H mH = new H();
    ...
class H extends Handler //也没写构造办法
...//去Handler.java里看
@Deprecated
public Handler() {
    this(null, false);
}   

7、实际上callback是看,那么我自己替换体系的call就能够啦

8、那我经过反射拿Handler中的mCallback

 public void hoodHandler() {
    try {
        Class<?> clazz = Class.forName("android.app.ActivityThread");
        Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
        activityThreadField.setAccessible(true);
        Object activityThread = activityThreadField.get(null);
        Field mHField = clazz.getDeclaredField("mH");
        mHField.setAccessible(true);
        final Handler mH = (Handler) mHField.get(activityThread);
        Field mCallbackField = Handler.class.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        mCallbackField.set(mH, new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                switch (msg.what) {
                    case 159:
                        // msg.obj = ClientTransaction
                        try {
                            // 获取 List<ClientTransactionItem> mActivityCallbacks 目标
                            Field mActivityCallbacksField = msg.obj.getClass()
                                    .getDeclaredField("mActivityCallbacks");
                            mActivityCallbacksField.setAccessible(true);
                            List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
                            for (int i = 0; i < mActivityCallbacks.size(); i++) {
                                // 打印 mActivityCallbacks 的所有item:
                                //android.app.servertransaction.WindowVisibilityItem
                                //android.app.servertransaction.LaunchActivityItem
                                // 如果是 LaunchActivityItem,则获取该类中的 mIntent 值,即 proxyIntent
                                if (mActivityCallbacks.get(i).getClass().getName()
                                        .equals("android.app.servertransaction.LaunchActivityItem")) {
                                    Object launchActivityItem = mActivityCallbacks.get(i);
                                    Field mIntentField = launchActivityItem.getClass()
                                            .getDeclaredField("mIntent");
                                    mIntentField.setAccessible(true);
                                    Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);
                                    // 获取发动插件的 Intent,并替换回来
                                    Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
                                    if (intent != null) {
                                        mIntentField.set(launchActivityItem, intent);
                                    }
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        break;
                }
                return false;
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}

总结

一个分为两步

1、hookAMS首要就是躲避ams检测,让ams检测的是一个现已注册了的activity。

2、hookHandler在生成activity之前再把activity换回来。

所以一定要熟悉动态署理,反射和Activity的发动流程。

首要经过hook,核心在于hook点

插桩 1、尽量找 静态变量 单利 2、public

动态署理

AMS检测之前我改下

如果启动一个未注册的Activity