Android 插件化实现方式(Hook)

一、首先我们要找到Hook的点

1. 分析
  • 我们先大概看下activity的启动流程(图片来自Android 插件化开发指南)


    Android 插件化实现方式(Hook)_第1张图片
    image
  • 当我们调用startActivity的时候,AMS对我们要启动的Activity进行检查,是否在AndroidManifest中声明过,如果没有就报没有在AndroidManifest的错误。这个时候需要欺骗AMS,我们需要hook,要它去检查一个我们预配置的Activity,通过AMS的检查。
  • 为什么不能直接hook掉AMS,AMS是系统的进程,管理者所有app的启动,不仅仅只是我们自己的app。
2. 看看源码怎么启动的(主要就是拦截这个方法startActivity)
 //通过ActivityManagerNative.getDefault()获取一个对象,开始启动新的Activity
            int result = ActivityManagerNative.getDefault().startActivity(whoThread,
            
            who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),
            
            token, target != null ? target.mEmbeddedID : null,
            requestCode, 0, null, options);
3. 看看代码的实现

    private Context mContext;
    private Class mProxyClass;

    private String TAG = HookUtil.class.getName();
    private final String EXTRA_ORIGIN_INTENT = "EXTRA_ORIGIN_INTENT";

    public HookUtil(Context context, Class proxyClass) {
        this.mContext = context.getApplicationContext();
        this.mProxyClass = proxyClass;
    }
    
    public void hookStartActivity() throws Exception {
        //1. 通过反射,拿到IActivityManager对象;
        Class amnClass = Class.forName("android.app.ActivityManagerNative");
        //2. 获得指定的私有属性
        Field gDefaultField = amnClass.getDeclaredField("gDefault");
        gDefaultField.setAccessible(true);
        // 获取字段上面的值传递null  证明是属性是static的,此处返回的是
        // new Singleton()
        Object gDefault = gDefaultField.get(null);


        Class singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceField = singletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        //不是static的方法 ,需要传入当前使用的对象  此处返回的是IActivityManager
        Object iamInstance = mInstanceField.get(gDefault);

        Class iamClass = Class.forName("android.app.IActivityManager");
        Object proxyInstance = Proxy.newProxyInstance(HookUtil.class.getClassLoader(),
                new Class[]{iamClass},
                // InvocationHandler 必须执行者,谁去执行
                new StartActivityInvocationHandler(iamInstance));

        //f.set(obj, "刘德华");
        mInstanceField.set(gDefault, proxyInstance);

    }
    
    
    
        private class StartActivityInvocationHandler implements InvocationHandler {
        // 方法执行者
        private Object mObject;

        public StartActivityInvocationHandler(Object object) {
            this.mObject = object;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 替换Intent,过AndroidManifest.xml检测
            if (method.getName().equals("startActivity")) {
                Log.e(TAG,"Activity已经开始启动");
                Log.e(TAG,"小弟到此一游!!!");
                // 1.首先获取原来的Intent
                Intent originIntent = (Intent) args[2];

                // 2.创建一个安全的
                Intent safeIntent = new Intent(mContext, mProxyClass);
                args[2] = safeIntent;

                // 3.绑定原来的Intent
                safeIntent.putExtra(EXTRA_ORIGIN_INTENT, originIntent);
            }
            return method.invoke(mObject, args);
        }
    }

4. 初始化插件
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ButterKnife.bind(this);
        mButton.setText("test");


        //初始化插件
        HookUtil hookUtil =
                new HookUtil(this, ProxyActivity.class);
        try {
            hookUtil.hookStartActivity();
        } catch (Exception e) {
            e.printStackTrace();
        }
  }
  
  
   @OnClick(R.id.btn)
   public void btnOnclick() {
        Intent intent = new Intent(MainActivity.this, Main3Activity.class
        );
        startActivity(intent);
    }
5. 打印结果(没有报错,打印了当前activity的stop方法,证明通过AMS的检查了)
com.dhcc.net.plug.HookUtil: Activity已经开始启动
com.dhcc.net.plug.HookUtil: 小弟到此一游!!!

二、启动时替换成我们自己的Actvity

1. 当AMS加载activty完成以后,就要启动activity了,这个时候他是通过ActivityThread类中的Handler去处理的。我们首先看看Handler是怎么分发消息的(我们处理msg.callback,将优先级最大化)
/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
2. 替换成为自己的Activity
 public void hookLaunchActivity() throws Exception{
        // 3.4.1 获取ActivityThread实例
        Class atClass = Class.forName("android.app.ActivityThread");
        Field scatField = atClass.getDeclaredField("sCurrentActivityThread");
        scatField.setAccessible(true);
        Object sCurrentActivityThread = scatField.get(null);
        
        
        // 3.4.2 获取ActivityThread中的mH
        Field mhField = atClass.getDeclaredField("mH");
        mhField.setAccessible(true);
        Object mHandler = mhField.get(sCurrentActivityThread);
        
        
        // 3.4 设置当前对象(也就是ActivityThread)的mH的成员变量
        Class handlerClass = Class.forName("android.os.Handler");
        Field mCallbackField = handlerClass.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        mCallbackField.set(mHandler,new HandlerCallBack());
    }
    
    
    private class HandlerCallBack implements Handler.Callback{

        @Override
        public boolean handleMessage(Message msg) {
            Log.e(TAG,"handleMessage");
            // 每发一个消息都会走一次这个CallBack发放
            if(msg.what == 100){
                handleLaunchActivity(msg);
            }
            return false;
        }

        /**
         * 开始启动创建Activity拦截
         * @param msg
         */
        private void handleLaunchActivity(Message msg) {
            try {
               Object record = msg.obj;
                // 1.从record 获取过安检的Intent
                Field intentField = record.getClass().getDeclaredField("intent");
                intentField.setAccessible(true);
                Intent proxyInent = (Intent) intentField.get(record);

                // 2.从safeIntent中获取原来的originIntent
                Intent realIntent = proxyInent.getParcelableExtra(EXTRA_ORIGIN_INTENT);

                // 3.重新设置回去
                if(realIntent != null){
                    Log.e(TAG,"启动我们自己的activity了");
                    intentField.set(record,realIntent);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
3. 修改一下OnCreate方法
   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ButterKnife.bind(this);
        mButton.setText("test");


        //初始化插件
        HookUtil hookUtil =new HookUtil(this, ProxyActivity.class);
        try {
            hookUtil.hookStartActivity();
            hookUtil.hookLaunchActivity();
        } catch (Exception e) {
            e.printStackTrace();
        }
  }
4. 看看打印结果
com.dhcc.net.plug.HookUtil: Activity已经开始启动
com.dhcc.net.plug.HookUtil: 小弟到此一游!!!
com.dhcc.net.plug.HookUtil: 启动我们自己的activity了
com.dhcc.net E/Main3Activity: 我是没有注册的Activity

你可能感兴趣的:(Android 插件化实现方式(Hook))