https://blog.csdn.net/gdutxiaoxu/article/details/81459910

https://www.jianshu.com/p/e8f0be9e1c15?utm_source=desktop(启动未在清单里注册的activity的方案)

思路:

1.hook startActivity方法

通过分析activity的启动源码,可以知道启动activity有两种方式。

所以对应的hook也有两种方式

1)hook activity

hook activity 的 mInstrumentation

  • 第一步:拿到当前 activity 的 mInstrumentation
  • 第二步:创建代理对象
  • 第三步:将我们的代理替换原 activity 的 mInstrumentation

2)hook context

  • 第一步:拿到ActivityThread(通过其静态方法currentActivityThread),再拿到 mInstrumentation
  • 第二步:创建代理对象
  • 第三步:将我们的代理替换原 activity 的 mInstrumentation

3)hook AMS

上面 hook activity 的两种方法其实都有一定缺陷,比如,第一种方法,只能 hook 住通过 Activity startActivity 的 activity。第二种方法,只能 hook 住通过 getApplicationContext().startActivity 启动的 activity。但是二者都是通过Instrumentation来启动activity的,所以分析 Instrumentation可追溯到IActivityManager,发现正好是个静态变量,就是ActivityManagerService的实例。

  • 第一步, API29以后,hook android.app.ActivityTaskManager.IActivityTaskManagerSingleton;API 26 以后,hook android.app.ActivityManager.IActivityManagerSingleton; API 25 以前,hook android.app.ActivityManagerNative.gDefault
  • 第二步,获取我们的代理对象,这里因为是接口,所以我们使用动态代理的方式
  • 第三步:设置为我们的代理对象
 /**
     * 这里对AMS进行hook
     * ActivityManager(ActivityManagerNative)里的IActivityManager是一个单例,用我们的代理对象替换它!
     *
     * @param context
     */
    public static void hookAMS(Context context) {
        try {
            final Class<?> ActivityManagerClz;
            final String getServiceMethodStr;
            final String IActivityManagerSingletonFieldStr;
            if (ifSdkOverIncluding29()) {//29的ams获取方式是通过ActivityTaskManager.getService()
                ActivityManagerClz = Class.forName("android.app.ActivityTaskManager");
                getServiceMethodStr = "getService";
                IActivityManagerSingletonFieldStr = "IActivityTaskManagerSingleton";
            } else if (ifSdkOverIncluding26()) {//26,27,28的ams获取方式是通过ActivityManager.getService()
                ActivityManagerClz = Class.forName("android.app.ActivityManager");
                getServiceMethodStr = "getService";
                IActivityManagerSingletonFieldStr = "IActivityManagerSingleton";
            } else {//25往下,是ActivityManagerNative.getDefault()
                ActivityManagerClz = Class.forName("android.app.ActivityManagerNative");
                getServiceMethodStr = "getDefault";
                IActivityManagerSingletonFieldStr = "gDefault";
            }

            //这个就是ActivityManager实例
            Object ActivityManagerObj = ReflectUtil.invokeStaticMethod(ActivityManagerClz, getServiceMethodStr);
            //这个就是这个就是ActivityManager实例中的IActivityManager单例对象
            Object IActivityManagerSingleton = ReflectUtil.staticFieldValue(ActivityManagerClz,
                    IActivityManagerSingletonFieldStr);

            // 2.现在创建我们的IActivityManager实例
            // 由于IActivityManager是一个接口,那么其实我们可以使用Proxy类来进行代理对象的创建
            // 结果被摆了一道,IActivityManager这玩意居然还是个AIDL,动态生成的类,编译器还不认识这个类,怎么办?反射咯
            Class<?> IActivityManagerClz;
            if (ifSdkOverIncluding29()) {
                IActivityManagerClz = Class.forName("android.app.IActivityTaskManager");
            } else {
                IActivityManagerClz = Class.forName("android.app.IActivityManager");
            }


            // 构建代理类需要两个东西用于创建伪装的Intent
            String packageName = Util.getPMName(context);
            String clz = Util.getHostClzName(context, packageName);
            Object proxyIActivityManager =
                    Proxy.newProxyInstance(
                            Thread.currentThread().getContextClassLoader(),
                            new Class[]{IActivityManagerClz},
                            new AMSProxyInvocation(ActivityManagerObj, packageName, clz));

            //3.拿到AMS实例,然后用代理的AMS换掉真正的AMS,代理的AMS则是用 假的Intent骗过了 activity manifest检测.
            //偷梁换柱
            Field mInstanceField = ReflectUtil.findSingletonField("mInstance");
            mInstanceField.set(IActivityManagerSingleton, proxyIActivityManager);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 /**
     * 把InvocationHandler的实现类提取出来,因为这里包含了核心技术逻辑,最好独立,方便维护
     */
    private static class AMSProxyInvocation implements InvocationHandler {

        Object amObj;
        String packageName;//这两个String是用来构建Intent的ComponentName的
        String clz;

        public AMSProxyInvocation(Object amObj, String packageName, String clz) {
            this.amObj = amObj;
            this.packageName = packageName;
            this.clz = clz;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Log.e("GlobalActivityHook", "method.getName() = " + method.getName());
            //proxy是创建出来的代理类,method是接口中的方法,args是接口执行时的实参
            if (method.getName().equals("startActivity")) {
                Log.d("GlobalActivityHook", "全局hook 到了 startActivity");

                Intent currentRealIntent = null;//侦测到startActivity动作之后,把intent存到这里
                int intentIndex = -1;
                //遍历参数,找到Intent
                for (int i = 0; i < args.length; i++) {
                    Object temp = args[i];
                    if (temp instanceof Intent) {
                        currentRealIntent = (Intent) temp;//这是原始的Intent,存起来,后面用得着
                        intentIndex = i;
                        break;
                    }
                }

                //构造自己的Intent,这是为了绕过manifest检测
                Intent proxyIntent = new Intent();
                ComponentName componentName = new ComponentName(packageName, clz);//用ComponentName重新创建一个intent
                proxyIntent.setComponent(componentName);
                proxyIntent.putExtra(TextActivity.ORIGINALLY_INTENT, currentRealIntent);//将真正的proxy作为参数,存放到extras中,后面会拿出来还原

                args[intentIndex] = proxyIntent;//替换掉intent
                //哟,已经成功绕过了manifest清单检测. 那么,我不能老让它跳到 伪装的Activity啊,我要给他还原回去,那么,去哪里还原呢?
                //继续看源码。

            }
            return method.invoke(amObj, args);
        }
    }

2.启动没有在应用清单xml文件里注册的activity

1)hook ams startActivity,将intent偷梁换柱。

对于插件里的activity,即使其在插件apk的清单xml里注册过,在宿主apk里启动的时候仍然会报错:

android.content.ActivityNotFoundException: Unable to find explicit activity class {com.renxh.pluginapp/com.renxh.pluginapp.PluginActivity}; have you declared this activity in your AndroidManifest.xml?

解决思路:事先在宿主apk的清单文件里注册一个StubActivity(暗桩,占位,也可以动态的获取宿主apk的第一个activity),然后再通过hook ActivityManager的startActivity的方法,创建新的intent指向StubActivity,并绑定有intent(插件intent)信息。

2)hook H Handler,还原intent

通过activity的启动流程:

app 调用 startActivity 方法 -> Instrumentation 类通过 ActivityManagerNative 或者 ActivityManager( API 26以后)将启动请求发送给 AMS -> AMS 进行一系列检查并将此请求通过 Binder 派发给所属 app -> app 通过 Binder 收到这个启动请求 -> ActivityThread 中的实现将收到的请求进行封装后送入 Handler -> 从 Handler 中取出这个消息,开始 app 本地的 Activity 初始化和启动逻辑。

所以可以hook ActivityThread类中的H类mH。

public static void hookActivityThread_mH(Context context) {

        try {
            Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");

            Object sCurrentActivityThread = ReflectUtil.staticFieldValue(activityThreadClazz, "sCurrentActivityThread");

            Handler mH = (Handler) ReflectUtil.fieldValue(sCurrentActivityThread, "mH");

            Field mCallBackField = ReflectUtil.findField(Handler.class, "mCallback");

            Handler.Callback callback;
            if (ifSdkOverIncluding28()) {
                //2.现在,造一个代理
                // 他就是一个简单的Handler子类
                callback = new ProxyHandlerCallback();//不需要重写全部mH,只需要对mH的callback进行重新定义
            } else {
                callback = new ActivityThreadHandlerCallBack(context);
            }

            //3.替换
            //将Handler的mCallback成员,替换成创建出来的代理HandlerCallback
            mCallBackField.set(mH, callback);


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

注意,android28以后的Activity的H Handler,hook方式不一样。!!!!!!

android28以上的H Handler的hook:

/**
     * 注意,这里有个坑
     * android.os.handler 这个类有 3个 callback,按照优先级,依次是 msg的callback,自己成员变量mCallback,自己的成员方法 handleMessage()
     *
     * 其中,msg.callback一般很少用,但是它是最优先的,如果有一个Message.存在callback非空成员,那么它是先执行,后面两个就没戏了。
     * 如果 handler自己的成员变量mCallback,非空,那么 handlerMessage()方法就没戏了
     * 前面两个都执行,那么handlerMessage才会执行,
     * 这个叫责任链模式?根据实际条件决定代码分支。
     */
    private static class ProxyHandlerCallback implements Handler.Callback {

        private int EXECUTE_TRANSACTION = 159;//这个值,是android.app.ActivityThread的内部类H 中定义的常量EXECUTE_TRANSACTION

        @Override
        public boolean handleMessage(Message msg) {
            boolean result = false;//返回值,请看Handler的源码,dispatchMessage就会懂了
            //Handler的dispatchMessage有3个callback优先级,首先是msg自带的callback,其次是Handler的成员mCallback,最后才是Handler类自身的handlerMessage方法,
            //它成员mCallback.handleMessage的返回值为true,则不会继续往下执行 Handler.handlerMessage
            //我们这里只是要hook,插入逻辑,所以必须返回false,让Handler原本的handlerMessage能够执行.
            if (msg.what == EXECUTE_TRANSACTION) {//这是跳转的时候,要对intent进行还原
                try {
                    //先把相关@hide的类都建好
                    Class<?> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction");
                    Class<?> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem");

                    Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");//ClientTransaction的成员
                    mActivityCallbacksField.setAccessible(true);
                    //类型判定,好习惯
                    if (!ClientTransactionClz.isInstance(msg.obj)) {
                        return true;
                    }
                    Object mActivityCallbacksObj = mActivityCallbacksField.get(msg.obj);//根据源码,在这个分支里面,msg.obj就是 ClientTransaction类型,所以,直接用
                    //拿到了ClientTransaction的List<ClientTransactionItem> mActivityCallbacks;
                    List list = (List) mActivityCallbacksObj;

                    if (list.size() == 0) {
                        return false;
                    }
                    Object LaunchActivityItemObj = list.get(0);//所以这里直接就拿到第一个就好了

                    if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) {
                        return true;
                    }
                    //这里必须判定 LaunchActivityItemClz,
                    // 因为 最初的ActivityResultItem传进去之后都被转化成了这LaunchActivityItemClz的实例

                    Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent");
                    mIntentField.setAccessible(true);
                    Intent mIntent = (Intent) mIntentField.get(LaunchActivityItemObj);

                    Bundle extras = mIntent.getExtras();
                    if (extras != null) {
                        Intent oriIntent = (Intent) extras.get(TextActivity.ORIGINALLY_INTENT);
                        //那么现在有了最原始的intent,应该怎么处理呢?
                        Log.d("1", "2");
                        mIntentField.set(LaunchActivityItemObj, oriIntent);
                    }

                    return result;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return result;
        }
    }

android28以下的H Handler的hook:

 public static class ActivityThreadHandlerCallBack implements Handler.Callback {

        private final Context mContext;

        public ActivityThreadHandlerCallBack(Context context) {
            mContext = context;
        }

        @Override
        public boolean handleMessage(Message msg) {
            int LAUNCH_ACTIVITY = 0;
            try {
                Class<?> clazz = Class.forName("android.app.ActivityThread$H");
                LAUNCH_ACTIVITY = (int) ReflectUtil.staticFieldValue(clazz, "LAUNCH_ACTIVITY");
            } catch (Exception e) {
            }
            if (msg.what == LAUNCH_ACTIVITY) {
                handleLaunchActivity(mContext, msg);
            }
            return false;
        }
    }

    private static void handleLaunchActivity(Context context, Message msg) {
        try {
            Object obj = msg.obj;

            Intent proxyIntent = (Intent) ReflectUtil.fieldValue(obj, "intent");
            //拿到之前真实要被启动的Intent 然后把Intent换掉
            Intent originallyIntent = proxyIntent.getParcelableExtra(TextActivity.ORIGINALLY_INTENT);
            if (originallyIntent == null) {
                return;
            }
            proxyIntent.setComponent(originallyIntent.getComponent());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.总结

hook actiivty的启动

  1. hook ams
  2. hook ActivityThread的H Handler
  3. hook的时候,要注意各个android版本的兼容处理。

4.关于插件apk的自定义classloader加载activity的兼容性问题

在下面的文章中,我提到了自定义classloader加载activity的兼容性问题

系统创建插件activity时,用的是应用的classloader去加载,自然就会报找不到,于是hook掉ActivityThread的Instrumentation的newActivity方法,将应用的classloader替换成自定义的classloader:

 @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
       return instrumentation.newActivity(PluginHelper.pluginClassLoader, className, intent);
    }

发现插件Activity的生命周期方法onResume也走了,但是Activity却并没有显示,不知道是为何?求路过的高手解答。//TODO

如果把这个插件的activity替换成app应用内没有注册的activity,这个activity会正常显示。

待续......

分类: hook技术

0 条评论

发表回复

您的电子邮箱地址不会被公开。