如果启动一个未注册的Activity

简述

要启动未注册的Activity主要是要逃避AMS的检测,思路是,检测前要启动的Activity换成注册的,检测通过了,再在启动前换回来。这里主要是两个点。检测前,hookAMS。检测后hookHandler。hook点有很多尽量找静态变量单例public

hookAMS

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

image.png

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 替换

image.png

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检测之前我改下

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYISD5XV' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片