美文网首页收藏
插件化(三)-启动Activity

插件化(三)-启动Activity

作者: 涛涛123759 | 来源:发表于2022-02-17 11:44 被阅读0次

    Android知识总结

    一、启动插件的四大组件

    1.1、宿主启动插件的Activity

    Activity 是需要在清单文件中注册的,显然,插件的 Activity 没有在宿主的清单文件中注册,那我们如何来启动它呢?

    这里我们就需要使用 Hook 技术,来绕开系统的检测。可能有些同学不知道 Hook 是什么,所以我们先简单的介绍下 Hook 技术。

    正常情况下对象A调用对象B,对象B处理后将数据返回给对象A,如下图:

    而加入Hook后的流程就变成了下图形式:

    Hook可以是一个方法或者一个对象,它就像一个钩子一样挂在对象B上面,当对象A调用对象B之前,Hook可以做一些处理,起到“欺上瞒下”的作用。而对象B就是我们常说的Hook点。为了保证Hook的稳定性,Hook点一般选择容易找到并且不易变化的对象,如静态变量和单例。

    那么思路就来了,首先我们在宿主里面创建一个 ProxyActivity 继承自 Activity,并且在清单中注册。当启动插件Activity 的时候,在系统检测前,找到一个Hook点,然后通过 Hook 将插件 Activity 替换成 ProxyActivity,等到检测完了后,再找一个Hook点,使用 Hook 将它们换回来,这样就实现了插件 Activity 的启动。思路是不是非常的简单。

    HOOK 启动插件 Activity

    通过 动态代理反射 实现 Hook。

    查找Hook点的原则:

    • 尽量静态变量或者单例对象。
    • 尽量 Hook public 的对象和方法。

    1.2、Activity启动流程

    首先我们来看一张 Activity 启动流程的简单示意图,如下:


    Activity启动流程

    通过这张图我们可以确定 Hook 点的大致位置。

    • 1、在进入 AMS 之前,找到一个 Hook 点,用来将插件 Activity 替换为 ProxyActivity。
    • 2、从 AMS 出来后,再找一个 Hook 点,用来将 ProxyActivity 替换为插件 Activity。

    1.3、Activity启动流程

    图1 图2 图3 API 28时序图

    详情见:
    Launch 启动 Activity
    Activity 启动流程(一)
    Activity 启动流程(二)

    二、HOOK 点的选择

    我们在项目中一般通过 startActivity(new Intent(this,PluginActivity.class)); 启动 PluginActivity,如果我想换成启动 ProxyActivity,调用方法 startActivity(new Intent(this,ProxyActivity.class)); 这样就可以了。是不是已经知道答案了!!!没错,我们只要找到能够修改 Intent 的地方,就可以作为 Hook 点,从这儿也可以看出 Hook 点并不是唯一的。

    • 一般我们选择 Intent 作为 HOOK 点。

    2.1、进入 AMS 之前HOOK 点

    • API30 源码
    // android/app/Instrumentation.java
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        // 这儿就是我们的 Hook 点,替换传入 startActivity 方法中的 intent 参数
        try {
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
    
    • API 28 源码
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
    

    既然 Hook 点找到了,那我们怎么修改呢?
    答案是动态代理,所以我们要生成一个代理对象,显然,我们要代理的是 ActivityTaskManager.getService() 返回的对象。

    • API 30 源码
    // android/app/ActivityTaskManager.java
    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }
    
    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };
    
    • API 28 源码
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }
    
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
    

    桶过上面的代码,我们知道 IActivityManager 是调用的 Singleton 里面的 get 方法,所以下面我们再看下Singleton 是怎么样的。

    // android/util/Singleton
    public abstract class Singleton<T> {
    
        @UnsupportedAppUsage
        public Singleton() {
        }
    
        @UnsupportedAppUsage
        private T mInstance;
    
        protected abstract T create();
    
        @UnsupportedAppUsage
        public final T get() {
            synchronized (this) {
                if (mInstance == null) {
                    mInstance = create();
                }
                return mInstance;
            }
        }
    }
    

    可以看出,IActivityManagerSingleton.get() 返回的实际上就是 mInstance 对象。所以接下来我们要替换的就是这个对象。

    2.2、从 AMS 出来后HOOK点

    替换回去会调用 Handler 的 handleMessage方法,所以下面我们看下 Handler 的源码。

    public void handleMessage(Message msg) {
    }
    
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    当 mCallback != null 时,首先会执行mCallback.handleMessage(msg),再执行 handleMessage(msg),所以我们可以将 mCallback 作为 Hook 点,创建它。ok,现在问题就只剩一个了,就是找到含有 intent 的对象,没办法,只能接着看源码。

    • API 26 源码
    // android/app/ActivityThread.java
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            } break;
        }
    }
    
    static final class ActivityClientRecord {
        Intent intent;
    }
    

    可以看到,在 ActivityClientRecord 类中,刚好就有个 intent,而且这个类的对象,我们也可以获取到,就是msg.obj。

    • API 30源码
    // android/app/ActivityThread.H.java
    case EXECUTE_TRANSACTION:
        final ClientTransaction transaction = (ClientTransaction) msg.obj;
        mTransactionExecutor.execute(transaction);
        if (isSystem()) {
            transaction.recycle();
        }
        break;
    
    //android/app/servertransaction/ClientTransaction .java
    public class ClientTransaction implements Parcelable, ObjectPoolItem {
    
        /** A list of individual callbacks to a client. */
        @UnsupportedAppUsage
        private List<ClientTransactionItem> mActivityCallbacks;
    }
    
    //android/app/servertransaction/TransactionExecutor.java
    public class TransactionExecutor {
        public void execute(ClientTransaction transaction) {
            executeCallbacks(transaction);
            executeLifecycleState(transaction);
        }
    
        public void executeCallbacks(ClientTransaction transaction) {
            final int size = callbacks.size();
            for (int i = 0; i < size; ++i) {
                final ClientTransactionItem item = callbacks.get(i);
                if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
                final int postExecutionState = item.getPostExecutionState();
                final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                        item.getPostExecutionState());
                if (closestPreExecutionState != UNDEFINED) {
                    cycleToPath(r, closestPreExecutionState, transaction);
                }
                //HOOK 点
                item.execute(mTransactionHandler, token, mPendingActions);
                item.postExecute(mTransactionHandler, token, mPendingActions);
            }
        }
    }
    
    //android/app/servertransaction/LaunchActivityItem.java
    public class LaunchActivityItem extends ClientTransactionItem {
    
        @UnsupportedAppUsage
        private Intent mIntent;
    
        @Override
        public void execute(ClientTransactionHandler client, IBinder token,
                PendingTransactionActions pendingActions) {
            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            //HOOK 点
            ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                    mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                    mPendingResults, mPendingNewIntents, mIsForward,
                    mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
            client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
        }
    }
    

    LaunchActivityItem中我们就找到Intent了,可以在此利用 HOOK 技术进行替换。

    创建 mCallback 替换系统的Intent,运用拿到 msg 从而可以拿到 msg.obj; 然后在替换 intent
    --> ClientTransaction == msg.obj  
    --> private List<ClientTransactionItem> mActivityCallbacks; 
    --> ClientTransactionItem的子类
    --> private Intent mIntent; 
    --> LaunchActivityItem 对象 
    --> private List<ClientTransactionItem> mActivityCallbacks; 
    --> ClientTransaction == msg.obj 
    

    三、源码

    • 核心代码
    public class HookUtil {
    
        private static final String TARGET_INTENT = "target_intent";
    
        public static void hookAMSAidl(){
            if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
                hookIActivityTaskManager();
            }else{
                hookIActivityManager();
            }
        }
    
        public static void hookIActivityTaskManager(){
            try{
                Field singletonField = null;
                Class<?> actvityManager = Class.forName("android.app.ActivityTaskManager");
                singletonField = actvityManager.getDeclaredField("IActivityTaskManagerSingleton");
                singletonField.setAccessible(true);
                Object singleton = singletonField.get(null);
                //拿IActivityManager对象
                Class<?> singletonClass = Class.forName("android.util.Singleton");
                Field mInstanceField = singletonClass.getDeclaredField("mInstance");
                mInstanceField.setAccessible(true);
                //原始的IActivityTaskManager
                final Object IActivityTaskManager = mInstanceField.get(singleton);
    
                Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
                        , new Class[]{Class.forName("android.app.IActivityTaskManager")}
                        , new InvocationHandler() {
                            @Override
                            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
    //                            Log.i(TAG, "invoke: " + method.getName());
    
                                //偷梁换柱
                                //真正要启动的activity目标
                                Intent raw = null;
                                int index = -1;
                                if ("startActivity".equals(method.getName())) {
                                    for (int i = 0; i < args.length; i++) {
                                        if(args[i] instanceof  Intent){
                                            raw = (Intent)args[i];
                                            index = i;
                                        }
                                    }
                                    //代替的Intent
                                    Intent newIntent = new Intent();
                                    newIntent.setComponent(new ComponentName("com.example.designdemo", StubActivity.class.getName()));
                                    newIntent.putExtra(TARGET_INTENT,raw);
                                    args[index] = newIntent;
                                }
    
                                return method.invoke(IActivityTaskManager, args);
                            }
                        });
    
                //            7. IActivityManagerProxy 融入到framework
                mInstanceField.set(singleton, proxy);
    
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        public static void hookIActivityManager() {
            try {
                // 获取 singleton 对象
                Field singletonField = null;
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // 小于8.0
                    Class<?> clazz = Class.forName("android.app.ActivityManagerNative");
                    singletonField = clazz.getDeclaredField("gDefault");
                } else {
                    Class<?> clazz = Class.forName("android.app.ActivityManager");
                    singletonField = clazz.getDeclaredField("IActivityManagerSingleton");
                }
    
                singletonField.setAccessible(true);
                Object singleton = singletonField.get(null);
    
                // 获取 系统的 IActivityManager 对象
                Class<?> singletonClass = Class.forName("android.util.Singleton");
                Field mInstanceField = singletonClass.getDeclaredField("mInstance");
                mInstanceField.setAccessible(true);
                final Object mInstance = mInstanceField.get(singleton);
    
                Class<?> iActivityManagerClass = Class.forName("android.app.IActivityManager");
    
                // 创建动态代理对象
                Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                        new Class[]{iActivityManagerClass}, new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                // do something
                                // Intent的修改 -- 过滤
                                /**
                                 * IActivityManager类的方法
                                 * startActivity(whoThread, who.getBasePackageName(), intent,
                                 *                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                                 *                         token, target != null ? target.mEmbeddedID : null,
                                 *                         requestCode, 0, null, options)
                                 */
                                // 过滤
                                if ("startActivity".equals(method.getName())) {
                                    int index = -1;
    
                                    for (int i = 0; i < args.length; i++) {
                                        if (args[i] instanceof Intent) {
                                            index = i;
                                            break;
                                        }
                                    }
                                    // 启动插件的
                                    Intent intent = (Intent) args[index];
    
                                    Intent proxyIntent = new Intent();
                                    proxyIntent.setComponent(new ComponentName("com.example.designdemo", StubActivity.class.getName()));
    
                                    proxyIntent.putExtra(TARGET_INTENT, intent);
    
                                    args[index] = proxyIntent;
                                }
    
                                // args  method需要的参数  --- 不改变原有的执行流程
                                // mInstance 系统的 IActivityManager 对象
                                return method.invoke(mInstance, args);
                            }
                        });
    
                // ActivityManager.getService() 替换成 proxyInstance
                mInstanceField.set(singleton, proxyInstance);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
        public static void hookHandler() {
            try {
                // 获取 ActivityThread 类的 Class 对象
                Class<?> clazz = Class.forName("android.app.ActivityThread");
    
                // 获取 ActivityThread 对象
                Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
                activityThreadField.setAccessible(true);
                Object activityThread = activityThreadField.get(null);
    
                // 获取 mH 对象
                Field mHField = clazz.getDeclaredField("mH");
                mHField.setAccessible(true);
                final Handler mH = (Handler) mHField.get(activityThread);
    
                Field mCallbackField = Handler.class.getDeclaredField("mCallback");
                mCallbackField.setAccessible(true);
    
                // 创建的 callback
                Handler.Callback callback = new Handler.Callback() {
    
                    @Override
                    public boolean handleMessage(@NonNull Message msg) {
                        // 通过msg  可以拿到 Intent,可以换回执行插件的Intent
    
                        // 找到 Intent的方便替换的地方  --- 在这个类里面 ActivityClientRecord --- Intent intent 非静态
                        // msg.obj == ActivityClientRecord
                        switch (msg.what) {
                            case 100: //API 26 以下
                                try {
                                    Field intentField = msg.obj.getClass().getDeclaredField("intent");
                                    intentField.setAccessible(true);
                                    // 启动代理Intent
                                    Intent proxyIntent = (Intent) intentField.get(msg.obj);
                                    // 启动插件的 Intent
                                    Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
                                    if (intent != null) {
                                        intentField.set(msg.obj, intent);
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                break;
                            case 159: //API 30
                                try {
                                    // 获取 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++) {
                                        if (mActivityCallbacks.get(i).getClass().getName()
                                                .equals("android.app.servertransaction.LaunchActivityItem")) {
                                            Object launchActivityItem = mActivityCallbacks.get(i);
    
                                            // 获取启动代理的 Intent
                                            Field mIntentField = launchActivityItem.getClass()
                                                    .getDeclaredField("mIntent");
                                            mIntentField.setAccessible(true);
                                            Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);
    
                                            // 目标 intent 替换 proxyIntent
                                            Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
                                            if (intent != null) {
                                                mIntentField.set(launchActivityItem, intent);
                                            }
                                        }
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                break;
                        }
                        // 必须 return false
                        return false;
                    }
                };
    
                // 替换系统的 callBack
                mCallbackField.set(mH, callback);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 初始化工具类
    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            LoadUtil.loadClass(this);
    
            HookUtil.hookAMS();
            HookUtil.hookHandler();
        }
    }
    
    • 启动Activity
    Intent intent = new Intent();
    intent.setComponent(new ComponentName("com.enjoy.plugin",
            "com.enjoy.plugin.MainActivity"));
    startActivity(intent);
    

    相关文章

      网友评论

        本文标题:插件化(三)-启动Activity

        本文链接:https://www.haomeiwen.com/subject/nhudlrtx.html