hook 系统api启动未注册Activity

闲来无事,研究一下activity的启动,顺便尝试了一下hook系统api启动一个未注册Activity的方案。 本方案采用的是插桩的方案。主要hook了ActivityThread中的Instrumentation的newActivity方法和 Activity中的Instrumentation的execStratActivity方法。 我们知道启动一个未注册的Activity,系统会报ClassNotFound的异常,这是因为在Ams中的StartActivty中有对intent的检测,所以我们可以启动一个代理类ProxyActivty,绕过系统的检测,然后在newActivity中还原为我们真正要启动的Activity.

第一步:hook newActivity,在Application中添加如下的方法

 public void replaceActivityThreadInstrumentation(){
        try {
            Class<?> clazz = Class.forName("android.app.ActivityThread");
            Field activityThread = clazz.getDeclaredField("sCurrentActivityThread");
            activityThread.setAccessible(true);
            Object object = activityThread.get(Object.class);
            Field mInstrumentation = clazz.getDeclaredField("mInstrumentation");
            mInstrumentation.setAccessible(true);
            Instrumentation instrumentation = (Instrumentation)mInstrumentation.get(object);
            Instrumentation instrumentationProxy = new InstrumentationProxy(instrumentation);
            mInstrumentation.set(object,instrumentationProxy);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
复制代码

获取ActivityThread中的sCurrentActivityThread,然后交到我们自己的InstrumentationProxy做代理。这里的方案参考了Android插件化之startActivity hook的几种姿势

public class InstrumentationProxy extends Instrumentation {

    private static final String TAG = "InstrumentationProxy";
    private Instrumentation mBase;

    public InstrumentationProxy(Instrumentation instrumentation) {
        mBase = instrumentation;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        // Hook之前, XXX到此一游!
        Log.e(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +
                "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                "\ntarget = [" + target + "], \nintent = [" + intent +
                "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");

        Intent intent1 = new Intent(target,ProxyActivity.class);

        // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
        // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
        try {

            //这里通过反射找到原始Instrumentation类的execStartActivity方法
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mBase, who,
                    contextThread, token, target, intent1, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("do not support!!! pls adapt it");
        }
    }

    
    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        Log.e(TAG,"before hook"+ className);
        if(className.equals("com.edu.myapplication.ProxyActivity")){
            className = "com.edu.myapplication.SecondActivity";
        }
        Log.e(TAG,"after hook"+ className);
        return (Activity)cl.loadClass(className).newInstance();
    }
}
复制代码

在newActivity中根据代理类的包名判断是否是我们需要hook的时机,这个代理类同时也hook了execStartActivity。

第二步:在我们调用startActivity的Activity,添加如下方法:

 public static void replaceInstrumentation(Activity activity){
        Class<?> k = Activity.class;
        try {
            //通过Activity.class 拿到 mInstrumentation字段
            Field field = k.getDeclaredField("mInstrumentation");
            field.setAccessible(true);
            //根据activity内mInstrumentation字段 获取Instrumentation对象
            Instrumentation instrumentation = (Instrumentation)field.get(activity);
            //创建代理对象
            Instrumentation instrumentationProxy = new InstrumentationProxy(instrumentation);
            //进行替换
            field.set(activity,instrumentationProxy);
        } catch (IllegalAccessException e){
            e.printStackTrace();
        }catch (NoSuchFieldException e){
            e.printStackTrace();
        }
    }
复制代码

然后调用startActivity,SecondActivity是未注册的Activity.

 replaceInstrumentation(this);
 startActivity(new Intent(this,SecondActivity.class));
复制代码

这样就启动了SecondActivity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值