插件化(三)-启动Activity

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 的对象和方法。

从 HOOK 一直找到静态的变量或者直接拿到类的对象,才说明 HOOK 成功。

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 非静态
                    switch (msg.what) {
                        case 100: //API 26 以下
                            try {
                                // msg.obj == ActivityClientRecord
                                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 {
                                // msg.obj == ClientTransaction
                                // 获取 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);

推荐阅读更多精彩内容