插件化(三)-啓動Activity 一、啓動插件的四大組件 二、HOOK 點的選擇 三、源碼

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。

查找Hook點的原則:

  • 儘量靜態變量或者單例對象。
  • 儘量 Hook public 的對象和方法。

1.2、Activity啓動流程

首先我們來看一張 Activity 啓動流程的簡單示意圖,如下:


通過這張圖我們可以確定 Hook 點的大致位置。

  • 1、在進入 AMS 之前,找到一個 Hook 點,用來將插件 Activity 替換爲 ProxyActivity。
  • 2、從 AMS 出來後,再找一個 Hook 點,用來將 ProxyActivity 替換爲插件 Activity。

1.3、Activity啓動流程

詳情見:
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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章