插件化(三)-啟動Activity

Android知識總結(jié)

一、啟動插件的四大組件

1.1、宿主啟動插件的Activity

Activity 是需要在清單文件中注冊的,顯然,插件的 Activity 沒有在宿主的清單文件中注冊,那我們?nèi)绾蝸韱铀兀?/p>

這里我們就需要使用 Hook 技術,來繞開系統(tǒng)的檢測。可能有些同學不知道 Hook 是什么,所以我們先簡單的介紹下 Hook 技術。

正常情況下對象A調(diào)用對象B,對象B處理后將數(shù)據(jù)返回給對象A,如下圖:

而加入Hook后的流程就變成了下圖形式:

Hook可以是一個方法或者一個對象,它就像一個鉤子一樣掛在對象B上面,當對象A調(diào)用對象B之前,Hook可以做一些處理,起到“欺上瞞下”的作用。而對象B就是我們常說的Hook點。為了保證Hook的穩(wěn)定性,Hook點一般選擇容易找到并且不易變化的對象,如靜態(tài)變量和單例。

那么思路就來了,首先我們在宿主里面創(chuàng)建一個 ProxyActivity 繼承自 Activity,并且在清單中注冊。當啟動插件Activity 的時候,在系統(tǒng)檢測前,找到一個Hook點,然后通過 Hook 將插件 Activity 替換成 ProxyActivity,等到檢測完了后,再找一個Hook點,使用 Hook 將它們換回來,這樣就實現(xiàn)了插件 Activity 的啟動。思路是不是非常的簡單。

HOOK 啟動插件 Activity

通過 動態(tài)代理反射 實現(xiàn) Hook。

查找Hook點的原則:

  • 盡量靜態(tài)變量或者單例對象。
  • 盡量 Hook public 的對象和方法。

從 HOOK 一直找到靜態(tài)的變量或者直接拿到類的對象,才說明 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,調(diào)用方法 startActivity(new Intent(this,ProxyActivity.class)); 這樣就可以了。是不是已經(jīng)知道答案了?。。]錯,我們只要找到能夠修改 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 參數(shù)
    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 點找到了,那我們怎么修改呢?
答案是動態(tài)代理,所以我們要生成一個代理對象,顯然,我們要代理的是 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 是調(diào)用的 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點

替換回去會調(diào)用 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 時,首先會執(zhí)行mCallback.handleMessage(msg),再執(zhí)行 handleMessage(msg),所以我們可以將 mCallback 作為 Hook 點,創(chuàng)建它。ok,現(xiàn)在問題就只剩一個了,就是找到含有 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 技術進行替換。

創(chuàng)建 mCallback 替換系統(tǒng)的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);

            // 獲取 系統(tǒng)的 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");

            // 創(chuàng)建動態(tài)代理對象
            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需要的參數(shù)  --- 不改變原有的執(zhí)行流程
                            // mInstance 系統(tǒng)的 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);

            // 創(chuàng)建的 callback
            Handler.Callback callback = new Handler.Callback() {

                @Override
                public boolean handleMessage(@NonNull Message msg) {
                    // 通過msg  可以拿到 Intent,可以換回執(zhí)行插件的Intent
                    // 找到 Intent的方便替換的地方  --- 在這個類里面 ActivityClientRecord --- Intent intent 非靜態(tài)
                    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;
                }
            };

            // 替換系統(tǒng)的 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);
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容