Hook启动未注册的Activity

132 阅读1分钟

1. Hook点选择

了解了Activity的启动流程后可得知,Activity的启动会走Intrumentation类的execStartActivitynewActivity,而检测是否注册是在execStartActivity之后,真正到达构建Activity的方法newActivity 时对是否有注册无需关注,所以可以考虑Hook点为Intrumentation,可以直接在ActivityThread类中将Insturmentation的对象mInstrumentation 替换成代理对象InsturmentationProxy

2. 具体实施

  • 使用已注册的PlaceholderActivity进行占坑,实际启动的是未注册的TargetActivity

2.1 编写InsturmentationProxy类

/**
 * InstrumentationProxy代理类对Instrumentation进行Hook
 *
 *@authorLTP  2023/5/16
 */
public class InstrumentationProxy extends Instrumentation {

    private final Instrumentation mInstrumentation;
    private final PackageManager mPackageManager;

    private static final StringPLACEHOLDER_ACTIVITY_PACKAGE_NAME= "com.btpj.hook.PlaceholderActivity";

    public InstrumentationProxy(Instrumentation mInstrumentation, PackageManager mPackageManager) {
        this.mInstrumentation = mInstrumentation;
        this.mPackageManager = mPackageManager;
    }

/**
* 直接在execStartActivity中将要启动的TargetActivity(未注册)替换为占坑的PlaceholderActivity
*/
@SuppressLint("QueryPermissionsNeeded")
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        List<ResolveInfo> resolveInfoList = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (resolveInfoList == null || resolveInfoList.size() == 0) {
            // resolveInfoList为空表示启动的Activity未注册
            intent.putExtra(HookHelper.TARGET_ACTIVITY_NAME, intent.getComponent().getClassName());
            intent.setClassName(who,PLACEHOLDER_ACTIVITY_PACKAGE_NAME);
        }

        try {
            Method execMethod = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);
            return (ActivityResult) execMethod.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

/**
* 创建时当匹配到启动的为PlaceholderActivity时又转化为TargetActivity
*/
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String targetActivityName = intent.getStringExtra(HookHelper.TARGET_ACTIVITY_NAME);
        if (className.equals(PLACEHOLDER_ACTIVITY_PACKAGE_NAME) && !TextUtils.isEmpty(targetActivityName)) {
            return super.newActivity(cl, targetActivityName, intent);
        }
        return super.newActivity(cl, className, intent);
    }
}

2.2 创建HookHelper帮助类

/**
 * Hook帮助类
 * @authorLTP  2023/5/16
 */
public class HookHelper {
    public static final StringTARGET_ACTIVITY_NAME= "target_activity_name";

    public static void hookInstrumentation(Context context) throws Exception {
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        // 寻找ActivityThread类中的sCurrentActivityThread属性
        Field activityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
        activityThreadField.setAccessible(true);
        Object activityThread = activityThreadField.get(context);

        // 寻找ActivityThread类中的mInstrumentation属性
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);

        LogUtil.INSTANCE.d("Hook Instrumentation进ActivityThread成功");
        // 将mInstrumentation属性赋值为InstrumentationProxy对象
        mInstrumentationField.set(activityThread, new InstrumentationProxy((Instrumentation) mInstrumentationField.get(activityThread), context.getPackageManager()));
    }
}

2.3 在APP的自定义Application中调用

/**
 *@authorLTP  2023/5/18
 */
class App : Application() {

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        try {
            HookHelper.hookInstrumentation(base)
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

2.4 调用启动方法即可

startActivity(TargetActivity.newIntent(this@HookActivity))