红橙Darren视频笔记 启动不在清单文件注册的activity 安卓8有效

参考链接

https://www.jianshu.com/p/aa03c4458b9a
https://www.jianshu.com/p/af148ab5ddf7

前提:

了解InvocationHandler的使用以及Activity的启动流程
另外还需要对反射比较了解
如果不是很了解可以参考如下两篇文章
https://blog.csdn.net/u011109881/article/details/117263548
https://blog.csdn.net/u011109881/article/details/117716367

思路:

activity的启动流程中有两个重要的部分 一个是检查startActivity中传递的intent的有效性,其中会检测activity是否在清单文件中注册。
第二个是launchActivity 通过ActivityThread内部的handler处理各种msg 其中有一项是起到启动activity的作用的
我们的思路是
1.在第一步中ActivityManager.startActivity中检测有效性的时候,偷偷将原来的intent替换掉,传递给系统一个空壳activity的intent 该activity什么都没有 仅仅是在清单文件注册过,我们将真正想要启动的activity的intent附加在空壳intent中
2.在ActivityThread中,将系统的Handler多进行一层包装 拦截在系统的handler之前处理launchActivity的事件 将之前偷换的intent再换回去,最终达到启动不在清单文件注册的activity的目的

coding部分:

创建三个activity 其中有两个是在清单文件注册过的,我们想打开的是那个没有注册的activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void jump(View view) {
        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        startActivity(intent);
    }
}


/**
 * Created by hjcai on 2021/6/9.
 * 临时Activity 已经在清单文件注册
 * 用于糊弄intent的检测流程
 */
public class TempActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_temp);
    }
}

/**
 * Created by hjcai on 2021/6/9.
 * <p>
 * 真正想要启动的Activity
 */
public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }
}

//清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hookstartactivity">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:name=".MainApplication"
        android:theme="@style/Theme.LearnEassyJoke">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".TempActivity" />
        <!--        <activity android:name=".SecondActivity"/>-->
    </application>

</manifest>

准备工作做得差不多了 下面创建工具类 主要作用有两个
1.拦截startActivity(通过InvocationHandler+反射实现)

    public static void hookStartActivity() throws Exception {

        // 查看API28的API源码 发现启动activity时
        // 1.ActivityManager.getService().startActivity方法内部 进行了intent可用性检查
        // 2.而真正启动activity是在ActivityThread handleMessage(EXECUTE_TRANSACTION)的时候
        // 在第一步中 我们需要先使用临时的空壳activity的intent来通过检测
        // 在第二步中 我们需要取出handleMessage里面我们塞进去的壳activity的intent 替换为我们真正想要启动的activity的intent

        /* 以下仅仅涉及第一步 大致思路
         * 我们需要拦截IActivityManager原先实现者的startActivity方法
         * IActivityManager的原先实现者是android.util.Singleton里面的mInstance对象
         * 因此我们的核心是取得mInstance对象
         * 取得过程:
         * 反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象
         * 再从IActivityManagerSingleton中取得mInstance对象
         */
        try {
            // 1.反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象
            Object defaultSingleton = getIActivityManagerSingleton();
            if (defaultSingleton == null) {
                Log.e(TAG, "hookStartActivity: failed");
                return;
            }
            // 反射android.util.Singleton对象
            Class<?> singletonClazz = Class.forName("android.util.Singleton");
            // 2.从IActivityManagerSingleton中取得mInstance对象
            Field instanceField = singletonClazz.getDeclaredField("mInstance");
            instanceField.setAccessible(true);
            Object activityManagerInstance = instanceField.get(defaultSingleton);// mInstance不是静态变量 需要从具体实例中获取对象 这里是defaultSingleton获取

            // Proxy 的三个参数
            // 1.class loader
            // 2.需要代理的接口类
            // 3.实现InvocationHandler的类 InvocationHandler中包含真正想要执行接口方法的对象
            // ActivityManager.getService().startActivity内部拦截intent 在InvocationHandler中 将其intent替换成我们空壳activity的intent
            Class<?> interfaceActivityManager = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),
                    new Class[]{interfaceActivityManager},
                    new HookInvocationHandler(activityManagerInstance));
            //替换为代理类IActivityManagerProxy
            instanceField.set(defaultSingleton, proxy);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static Object getIActivityManagerSingleton() {
        try {
            // 反射获取ActivityManager实例
            Class<?> activityManagerClazz = Class.forName("android.app.ActivityManager");
            // 拿到其中的静态变量 IActivityManagerSingleton
            Field field = activityManagerClazz.getDeclaredField("IActivityManagerSingleton");
            field.setAccessible(true);
            return field.get(null);// IActivityManagerSingleton是static变量 field.get(null)可以获取IActivityManagerSingleton对象
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }


/**
 * Created by hjcai on 2021/6/10.
 * 
 * 拦截startActivity方法 达到偷梁的目的
 */
public class HookInvocationHandler implements InvocationHandler {
    private static final String TAG = "HookInvocationHandler";
    public static final String REQUEST_TARGET_INTENT_KEY = "REQUEST_TARGET_INTENT_KEY";
    // 代理ActivityManager
    private final Object mActivityManager;

    public HookInvocationHandler(Object activityManagerServiceClass) {
        this.mActivityManager = activityManagerServiceClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e(TAG, method.getName());
        if (method.getName().equals("startActivity")) {
            Log.e(TAG, "invoke: startActivity....");
            // 拦截startActivity
            Intent intent;
            int index = 0;
            // 查找intent在startActivity方法中是第几个参数 由于Android版本更新 参数index可能变化
            for (int i = 0, length = args.length; i < length; i++) {
                if (args[i] instanceof Intent) {
                    index = i;
                    break;
                }
            }
            //获取一开始设置进去的真正想要进入activity的intent
            intent = (Intent) args[index];

            //创建占坑Activity的Intent
            Intent subIntent = new Intent();
            subIntent.setClassName("com.example.hookstartactivity", "com.example.hookstartactivity.TempActivity");
            // 保存插件Activity的Intent
            subIntent.putExtra(REQUEST_TARGET_INTENT_KEY, intent);
            // 借尸还魂中的借尸 由于想要打开的activity没有注册 这里替换为已经注册的TempActivity 真正想要打开的activity的intent放在intent的bundle里面
            args[index] = subIntent;
        }
        return method.invoke(mActivityManager, args);
    }
}

2.拦截launchActivity(通过对ActivityThread中的handler进行二次封装+反射实现)

    public static void hookLaunchActivity() throws Exception {
        try {
            Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
            Field currentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
            currentActivityThreadField.setAccessible(true);
            Object currentActivityThread = currentActivityThreadField.get(null);

            Field handlerField = activityThreadClazz.getDeclaredField("mH");
            handlerField.setAccessible(true);
            Handler mH = (Handler) handlerField.get(currentActivityThread);

            Field callbackField = Handler.class.getDeclaredField("mCallback");
            callbackField.setAccessible(true);
            //Handler的mCallback替换为CallBackProxy
            callbackField.set(mH, new CallBackProxy(mH));
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

/**
 * Created by hjcai on 2021/6/11.
 * 
 * 在launchActivity前拦住 达到换柱的目的
 */
public class CallBackProxy implements Handler.Callback {

    private final Handler mHandler;

    public CallBackProxy(Handler handler) {
        this.mHandler = handler;
    }

    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == 100) {
            Object o = msg.obj;
            try {
                Field field = o.getClass().getDeclaredField("intent");
                field.setAccessible(true);
                //获取占坑Activity的Intent
                Intent intent = (Intent) field.get(o);
                //获取之前保存的插件Activity的Intent
                Intent targetIntent = intent.getParcelableExtra(REQUEST_TARGET_INTENT_KEY);
                //将占坑的Activity替换为插件Activity
                if (targetIntent == null) {
                    return false;
                }
                // 借尸还魂中的还魂
                intent.setComponent(targetIntent.getComponent());
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        mHandler.handleMessage(msg);
        return true;
    }
}

完整工具类代码

/**
 * Created by hjcai on 2021/6/9.
 */
public class HookStartActivityUtil {
    private static final String TAG = "HookStartActivityUtil";
    Context mContext;
    Class<?> mTempClazz;

    public HookStartActivityUtil(Context context, Class<?> tempClazz) {
        mContext = context;
        mTempClazz = tempClazz;
    }

    public static void hookStartActivity() throws Exception {

        // 查看API28的API源码 发现启动activity时
        // 1.ActivityManager.getService().startActivity方法内部 进行了intent可用性检查
        // 2.而真正启动activity是在ActivityThread handleMessage(EXECUTE_TRANSACTION)的时候
        // 在第一步中 我们需要先使用临时的空壳activity的intent来通过检测
        // 在第二步中 我们需要取出handleMessage里面我们塞进去的壳activity的intent 替换为我们真正想要启动的activity的intent

        /* 以下仅仅涉及第一步 大致思路
         * 我们需要拦截IActivityManager原先实现者的startActivity方法
         * IActivityManager的原先实现者是android.util.Singleton里面的mInstance对象
         * 因此我们的核心是取得mInstance对象
         * 取得过程:
         * 反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象
         * 再从IActivityManagerSingleton中取得mInstance对象
         */
        try {
            // 1.反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象
            Object defaultSingleton = getIActivityManagerSingleton();
            if (defaultSingleton == null) {
                Log.e(TAG, "hookStartActivity: failed");
                return;
            }
            // 反射android.util.Singleton对象
            Class<?> singletonClazz = Class.forName("android.util.Singleton");
            // 2.从IActivityManagerSingleton中取得mInstance对象
            Field instanceField = singletonClazz.getDeclaredField("mInstance");
            instanceField.setAccessible(true);
            Object activityManagerInstance = instanceField.get(defaultSingleton);// mInstance不是静态变量 需要从具体实例中获取对象 这里是defaultSingleton获取

            // Proxy 的三个参数
            // 1.class loader
            // 2.需要代理的接口类
            // 3.实现InvocationHandler的类 InvocationHandler中包含真正想要执行接口方法的对象
            // ActivityManager.getService().startActivity内部拦截intent 在InvocationHandler中 将其intent替换成我们空壳activity的intent
            Class<?> interfaceActivityManager = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),
                    new Class[]{interfaceActivityManager},
                    new HookInvocationHandler(activityManagerInstance));
            //替换为代理类IActivityManagerProxy
            instanceField.set(defaultSingleton, proxy);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static Object getIActivityManagerSingleton() {
        try {
            // 反射获取ActivityManager实例
            Class<?> activityManagerClazz = Class.forName("android.app.ActivityManager");
            // 拿到其中的静态变量 IActivityManagerSingleton
            Field field = activityManagerClazz.getDeclaredField("IActivityManagerSingleton");
            field.setAccessible(true);
            return field.get(null);// IActivityManagerSingleton是static变量 field.get(null)可以获取IActivityManagerSingleton对象
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void hookLaunchActivity() throws Exception {
        try {
            Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
            Field currentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
            currentActivityThreadField.setAccessible(true);
            Object currentActivityThread = currentActivityThreadField.get(null);

            Field handlerField = activityThreadClazz.getDeclaredField("mH");
            handlerField.setAccessible(true);
            Handler mH = (Handler) handlerField.get(currentActivityThread);

            Field callbackField = Handler.class.getDeclaredField("mCallback");
            callbackField.setAccessible(true);
            //Handler的mCallback替换为CallBackProxy
            callbackField.set(mH, new CallBackProxy(mH));
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

最后在application中初始化工具类并调用两个hook方法

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        HookStartActivityUtil hookStartActivityUtil =
                new HookStartActivityUtil(this, TempActivity.class);
        try {
            hookStartActivityUtil.hookStartActivity();
            hookStartActivityUtil.hookLaunchActivity();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

最终效果:
在这里插入图片描述
布局文件我就不贴出来了,最后我的代码仅在Android 8.0(API27) 是work的 Android9.0(API28)是不work的
从https://blog.csdn.net/u011109881/article/details/117716367 的分析可知 Android 9.0取消了msg 100 并通过各种TransactionItem来执行activity的生命周期 因此显然我在代码中拦截msg 100 在9.0是无法生效的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值