啓動沒有在 AndroidManifest 中註冊的 Activity

一、報錯

啓動沒有在 AndroidManifest 中註冊的 Activity,會報錯:

android.content.ActivityNotFoundException: Unable to find explicit activity class {...}; have you declared this activity in your AndroidManifest.xml?

二、思路

Android 使用的是 C/S 架構,我們的 app 是 client 客戶端,內核是 Server 服務端。

Activity 是否註冊的驗證是在服務端進行的,所以我們客戶端無法修改判斷條件。但我們可以修改客戶端的請求,讓服務端以爲我們要啓動的是另外一個已註冊的 Acitivity,在客戶端得到啓動許可後,再去啓動真正的目標 Activity。

這一操作要通過 hook 來實現。

在這裏插入圖片描述

三、啓動流程

android 28 啓動流程:
在這裏插入圖片描述

3.1 hook 點的選擇

替換:

在 android 28 上,通過 ActivityManager.getService().startActivity() 來向服務端發起請求。所以我們要在這一步之前,將要啓動的 Activity 替換爲已註冊的 Activity。

恢復:

通過 mInstrumentation.newActivity() 來創建 Activity。所以我們要在這一步之前,將要啓動的 Activity 替換回未註冊的 Activity。

本文選擇的替換點是 ActivityManager.getService().startActivity(),即要 hook ActivityManager.getService()

恢復點是:ActivityThread#mH.handleMessage(),即要 hook ActivityThread#mH#mCallback

3.2 版本差異

獲取 AMSP:

  • android 26 及以上:
    ActivityManager.getService()

  • android 26 以下:
    ActivityManagerNative.getDefault()

處理啓動 Activity 的 message:

  • android 28 及以上
    handleMessage(ActivityThread.H.EXECUTE_TRANSACTION)

  • android 28 以下:
    handleMessage(H.LAUNCH_ACTIVITY)

四、代碼

完整代碼見 github.com/Gdeeer

新建三個 Activity:

  • CommonActivity 已註冊,用於點擊跳轉 TargetActivity
  • StubActivity 已註冊,用於佔位
  • TargetActivity 已註冊,用於跳轉

4.1 Hook

AMSPHookHelper

class AMSPHookHelper {
    static final String EXTRA_TARGET_INTENT = "extra_target_intent";


    static void hookAMSP() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            hookAMSPBefore26();
        } else {
            hookAMSPSince26();
        }
    }


    /**
     * android 26 以下版本 AMSP 的 hook
     */
    private static void hookAMSPBefore26() {
        try {
            Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
            Object gDefault = FieldUtils.readStaticField(classActivityManagerNative, "gDefault");
            Object mInstance = FieldUtils.readField(gDefault, "mInstance");
            Class classIActivityManager = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{classIActivityManager},
                new MockAMSP(mInstance)
            );

            FieldUtils.writeField(gDefault, "mInstance", proxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * android 26 及以上版本 AMSP 的 hook
     */
    private static void hookAMSPSince26() {
        try {
            Object IActivityManagerSingleton = FieldUtils.readStaticField(ActivityManager.class, "IActivityManagerSingleton");
            Object mInstance = FieldUtils.readField(IActivityManagerSingleton, "mInstance");
            Class classIActivityManager = Class.forName("android.app.IActivityManager");
            Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{classIActivityManager},
                new MockAMSP(mInstance)
            );

            FieldUtils.writeField(IActivityManagerSingleton, "mInstance", proxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void hookActivityThread() {
        try {
            Class classActivityThread = Class.forName("android.app.ActivityThread");
            Object currentActivityThread = FieldUtils.readStaticField(classActivityThread, "sCurrentActivityThread");
            Handler mH = (Handler) FieldUtils.readField(currentActivityThread, "mH");
            FieldUtils.writeField(mH, "mCallback", new MockHCallback(mH));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

StubActivityApplication

public class StubActivityApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        // 解除 android P 上的私有 api 限制,見http://weishu.me/2018/06/07/free-reflection-above-android-p/
        Reflection.unseal(base);

        // hook
        AMSPHookHelper.hookAMSP();
        AMSPHookHelper.hookActivityThread();
    }
}

4.2 替換點

MockAMSP:

public class MockAMSP implements InvocationHandler {
    private static final String START_ACTIVITY = "startActivity";

    private Object mBase;

    MockAMSP(Object base) {
        mBase = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (START_ACTIVITY.equals(method.getName())) {
            // 找到舊的 intent
            Intent raw;
            int index = 0;
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Intent) {
                    index = i;
                    break;
                }
            }
            raw = (Intent) args[index];

            // 創建新的 intent
            Intent newIntent = new Intent();
            String stubPackage = "com.gdeer.gdtesthub";
            ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
            newIntent.setComponent(componentName);
            newIntent.putExtra(AMSPHookHelper.EXTRA_TARGET_INTENT, raw);

            // 替換舊的 intent 爲新的 intent
            args[index] = newIntent;

            // 調用 "startActivity" 方法
            return method.invoke(mBase, args);
        }
        return method.invoke(mBase, args);
    }
}

4.3 恢復點

MockHCallback:

public class MockHCallback implements Handler.Callback {
    /**
     * android 28 以下,ActivityThread$H.LAUNCH_ACTIVITY = 100
     */
    private static final int LAUNCH_ACTIVITY = 100;

    /**
     * android 28 上,ActivityThread$H.EXECUTE_TRANSACTION = 159
     */
    private static final int EXECUTE_TRANSACTION = 159;

    private Handler mBase;

    MockHCallback(Handler base) {
        mBase = base;
    }

    @Override
    public boolean handleMessage(Message msg) {
        handleLaunchActivity(msg);
        mBase.handleMessage(msg);
        return true;
    }

    private void handleLaunchActivity(Message msg) {
        Log.d(TAG, "handleLaunchActivity");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            if (msg.what == EXECUTE_TRANSACTION) {
                handleLaunchActivitySince28(msg);
            }
        } else {
            if (msg.what == LAUNCH_ACTIVITY) {
                handleLaunchActivityBefore28(msg);
            }
        }
    }

    private void handleLaunchActivityBefore28(Message msg) {
        try {
            Object obj = msg.obj;
            if (obj != null) {
                Intent raw = (Intent) FieldUtils.readField(obj, "intent");
                Intent target = raw.getParcelableExtra(AMSPHookHelper.EXTRA_TARGET_INTENT);
                if (target != null) {
                    raw.setComponent(target.getComponent());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleLaunchActivitySince28(Message msg) {
        try {
            Object mActivityCallbacks = FieldUtils.readField(msg.obj, "mActivityCallbacks");
            if (mActivityCallbacks != null) {
                List<?> list = (List<?>) mActivityCallbacks;
                if (list.size() > 0) {
                    Object listItem = list.get(0);
                    Class classLaunchActivityItem = Class.forName("android.app.servertransaction.LaunchActivityItem");
                    if (listItem.getClass() == classLaunchActivityItem) {
                        Intent raw = (Intent) FieldUtils.readField(listItem, "mIntent");
                        Intent target = raw.getParcelableExtra(AMSPHookHelper.EXTRA_TARGET_INTENT);
                        raw.setComponent(target.getComponent());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章