插件化(二)插件化Activity的啟動

大話插件化系列目錄
插件化(一) 插件化思想與類加載
插件化(二) 插件化Activity的啟動
插件化(三) 插件資源加載

啟動Activity

我們啟動Activity的方式

正常一個進程app
startActivity(new Intent(MainActivity.this,ProxyAcitvity.class));

啟動其他進程
       Intent intent = new Intent();
       intent.setComponent(new ComponentName("top.zcwfeng.plugin",
               "top.zcwfeng.plugin.MainActivity"));

但是我們啟動的時候都需要進行注冊。在AMS(ActivityManagerService)中進行注冊。
APP----AMS----APP

所以我們用到的技術局勢Hook: 通過 反射 和 動態代理實現。

如何查找Hook點和思路

偷梁換柱.png

Activity------>AMS

查找源碼的Hook點

Activity#startActivity 方法查看

Activity-AMS.png

Activity#startActivityForResult 三個參數方法

instrumentation 這個位置是個hook點,但是我們繼續看看有沒有更輕松

 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

繼續進入Instrumentation#execStartActivity

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        result = am.onStartActivity(intent);
                    }
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        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;
    }

找到 靜態方法,并且有Intent相關的

int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);

這里讓我們容易想到,可以用動態代理去修改Intent。反射對于靜態方法比較容易。所以這里是突破口。

接口+實現,比較容易想到
source 26——8.0源碼中

 /**
     * @hide
     */
    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 對象

ActivityManager.getService()---靜態方法,后去返回---IActivityManager
IActivityManger 對象---IActivityManagerSingleton get 獲取---Singleton<T>#private T mInstance
我們的proxyInstance 替換系統的 mInstance 對象
mInstance 非靜態的 -- Singleton的對象 ---- IActivityManagerSingleton 是靜態的

 public static void hookAMS() {
        try {

            // 獲取 Singleton 對象
            Class<?> clazz = Class.forName("android.app.ActivityManager");
            Field 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);
            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 {
                            //TODO Intent 的修改

//TODO Intent 的修改---進行過濾
                            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.setClassName("top.zcwfeng.zcwplugin",
                                        "top.zcwfeng.zcwplugin.ProxyActivity");
                                intent.putExtra(TARGET_INTENT, intent);
                                args[index] = proxyIntent;
                            }


                            //iActivityManager 對象
                            return method.invoke(mInstance,args);
                        }
                    });
            // TODO: 替換
            //proxyInstance.startActivity()
            // ActivityManager.getService() 替換成ProxyActity Instance
            mInstanceField.set(singleton, proxyInstance);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

Activity 啟動

Activity 啟動.png

AMS----->ApplicationThread Hook Handler

ActivityThread------>Activity

ActvityThread流程.png

ActivityThread#scheduleLaunchActivity----->sendMessage--->H

所以我們要Hook Handler

        public static final int LAUNCH_ACTIVITY         = 100;

我們的目標還是找Intent 先關方便替換的地方

繼續搜索
ActvityThread#handleLaunchActivity-->performLaunchActivity
-----> r.intent.getComponent();

找到 Intent的方便替換的地方 --- 在這個類 ActivityClientRecord 里面 --- Intent 是非靜態 -- 獲取 ActivityClientRecord 對象(關鍵點)

final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
看到這個,所以 r ---- 相當與msg.obj

源碼中Handler 對象H 是 final H mH = new H();

因此我們需要到Handler#handlerMessage-------->
Handler 源碼

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

調用handlerMessage 之前,我們會執行mCallback
所以我們可以借助mCallback
mH 創建沒有構造賦值過,所以 系統的mCallback == null

思路:

創建 mCallback 替換系統的 -- 從而拿到 msg --- msg.obj -- intent

想要拿到Handler mCallBack 因為不是靜態,我們就要拿到Handler,handler就是H類,mH 不是靜態,我們就需要ActivityThread 找到
private static volatile ActivityThread sCurrentActivityThread;

靜態的反射比較方便拿到。
hookHandler

public static void hookHandler() {

        // 拿到handler對象 mh,非靜態,拿到ActivityThread是靜態的

        try {
            Class<?> clazz = Class.forName("android.app.ActiityThread");

            //獲取 ActivityThread 對象
            Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
            activityThreadField.setAccessible(true);
            Object activityThread = activityThreadField.get(null);

            //獲取 mh 對象
            Field mHField = clazz.getDeclaredField("mH");
            mHField.setAccessible(true);
            Handler mH = (Handler) mHField.get(activityThread);

            Field callbackField = Handler.class.getDeclaredField("mCallback");
            callbackField.setAccessible(true);


            // 創建callback
            Handler.Callback callback = new Handler.Callback() {
                @Override
                public boolean handleMessage(@NonNull Message msg) {
                    // 通過msg 拿到 Intent 換回 插件執行的Intent


                    switch (msg.what) {
                        case 100:
                            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;
                    }


                    // 必須 return false 保證向下執行handlerMessage
                    return false;
                }
            };

            // 替換系統callback
            callbackField.set(mH, callback);


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

適配(上面用的源代碼是8.0 上的源碼,所以我們需要適配)

適配點:

1.hookAMS

Instrumentation.java ActivityManager.java

AMS.png
 // 獲取 Singleton 對象
            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");
            }
  1. hookHandler
截屏2020-11-29 23.30.12.png

API 26 H 類 從100 開始
API 28 H 類 從110 開始 生命周期從100~109 合并成了一個類 159

API28 startActivit.png

ClientTransaction == msg.obj ---- private List<ClientTransactionItem> mActivityCallbacks;private List<ClientTransactionItem> mActivityCallbacks; -- -- ClientTransactionItem的子類

private Intent mIntent; -- LaunchActivityItem 對象 -- private List<ClientTransactionItem> mActivityCallbacks;
-- ClientTransaction == msg.obj

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();
                            }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容