插件化原理之hook系统函数

插件化原理之hook系统函数

插件化主要问题之一是如startActivity一个未在注册表里面注册的acitivity

我们都知道开启一个activity是涉及到app进程和系统服务进程的交互过程,其中验证要打开的acitivity是否在清单文件中也是在系统服务进程进行的,那么”如何”欺骗系统服务进程?

方案一是设置一个代理ProxyActivity,这个ProxyActivity在清单文件中注册过,然后在该ProxyActivity里面注入一个真实的Activity,ProxyActivity所有的生命周期方法里面都回调真实的Activity生命周期方法。关于这个方案有个框架实现的挺好的,可以看动态代理框架。这个方案调用startyActivity是要特别处理,首先要把真实的activity信息隐藏在intent里面,然后在代理ProxyActivity里面在解析出真正的activity信息并且实例化,然后回调真正的activity生命周期方法。

github地址:https://github.com/singwhatiwanna/dynamic-load-apk

方案二 是直接hook appstartActivity方法,这里先梳理一下startActivity的逻辑。startActivity虽然只有一行代码,但里面涉及的调用却很复杂,app持有一个AMSBinder引用,在app端最终调用ActivityManagerNative.getDefault().startActivity后,系统服务进程开始做一些准备处理,而ActivityManagerNative.getDefault()是个单例模式,我们可以在这里传递一个注册表里面注册过的activity过去,这样系统服务进程验证的时候就会通过,然后回调给ApplicationThreadNative,ApplicationThreadNative再通过一个内部类H发送消息到ActivityThread,这样消息发送过来的时候我们再换回真实的activity,如此一来app就会认为这个activity已经被系统验证过了,生命周期的调用和其他acitivity的生命周期方法调用过程一模一样。查看startActivity调用流程时,发现ActivityManagerNative.getDefault()是个单例,我们可以反射重新设置,ActivityThread里面所有AMS发送过来的消息都会通过内部类H发送到主线程,查看Handler的源码我们发现,Hander在处理消息的方法是会先查看内部一个 变量Callback是否存在,如果存在则先处理Callback的方法,

HandlerdispatchMessage方法源码,我们通常是复写handleMessage,如果我们要拦截哪个方法,我们看一看设置HandlerCallback 并且设置handleMessage返回true,这样就总不会走HandlerhandleMessage方法了。

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

我们可以在ActivityThreadH里面的mCallback里面拦截startActivity发出来的消息,然后在这里把真实的activity替换。

本文讲解的是第二种方案,当然这种方案要对startActivity具体流程有个清楚的认识,不认识的可以查看我的另一篇博客

startActivity说起 https://blog.csdn.net/mr_lu_/article/details/80137617

主要方法如

public void hookSystem() throws Exception{

// 1 加载ActivityManagerNative类信息
        Class<?>

activityManagerNative=Class.forName("android.app.ActivityManagerNative");
        //2 获取gDefault字段属性
       Field field= getField(activityManagerNative,"gDefault");
        //3 获取一个对象。。。。Singleton
       Object o= field.get(null);
        DebugLog.d(o.toString());

//4获取Singleton类信息
        Class<?> singleton=Class.forName("android.util.Singleton");
        //5 获取mInstance 字段信息
        Field field2= getField(singleton,"mInstance");

//6 获取该第三步对象里面的变量对象
        Object mInstance=field2.get(o);
        DebugLog.d(mInstance.toString());
        MyInvocationHandler myInvocationHandler=new MyInvocationHandler(mInstance);
        //7 生成代理类
        Object proxy= Proxy.newProxyInstance(mInstance.getClass().getClassLoader(),mInstance.getClass().getInterfaces(),myInvocationHandler);
//        //8 替换Singleton类里面的mInstance属性
        field2.set(o,proxy);

 

//9获取ActivityThread类信息
       activityThreadClass=Class.forName("android.app.ActivityThread");

//10获取mH字段信息,该变量是个Handler对象
        Field mHField=getField(activityThreadClass,"mH");

//11 ActivityThread类里面有个唯一对象,就是sCurrentActivityThread属性
        Field

sCurrentActivityThread=getField(activityThreadClass,"sCurrentActivityThread");

        //12 获取到ActivityThread对象
        activityThread=sCurrentActivityThread.get(null);
        //13 获取ActivityThread对象 mH对象
        Object mH= mHField.get(activityThread);
//14 获取Handler类里面的mCallback字段信息
        Field field1=getField(Handler.class,"mCallback");

        //15 设置mH对面里面的callback为我们自己写的callback
        field1.set(mH,myCallback);
    }

第二步我们就获取到ActivityManagerNative类里面的gDefault属性了

 

由于该变量是个static 所以第三步我们调用get传入null参数就可以获取该对象了,然后通过动态代理生成一个代理对象,由于代理对象的方法都会调用我们设置的MyInvocationHandler对象的invoke方法,故我们可以在这里拦截startActivity方法,我们把真实的activity随便替换一个在注册表里面注册过的activity信息传输到AMS那里。

查看Singleton源代码我们可以知道,该get方法返回的就是create方法创建的对象,也是内部的一个变量,我们只要把这个对象替换成我们的对象就可以了。

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

到第八步的时候我们已经把ActivityManagerNative里面的gDefault变量里面的mInstance变量换成我们自己的代理对象,这样AcitivityManagerNative.getDefault对象也就是我们的代理对象了。

12步的时候我们已经获取到当前ActivityThread类的唯一对象,这个和我们在自定义Application返回Application实例一样。

 

13步的我们已经获取到ActivityThread里面mH这个变量

 

15步的时候我们已经把ActivityThread里面的mH对象里面的mCallback设置成我们自己的callback,这样AMS发送给APP的消息,我们这里都能进行拦截。


MyInvocationHandler类代码如下

这里我们拦截了APPstartActivity方法,把要开启的Activity信息保存起来,替换一个新注册过的Activity信息放在里面

class MyInvocationHandler implements InvocationHandler {
        Object target;
        public MyInvocationHandler(Object o){
            this.target=o;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("startActivity")){
                int index=-1;
                DebugLog.d("invoke:"+method.getName());
                for (Object o:args){
                    index++;
                    if (o instanceof Intent){
//                        intent.setComponent(componentName);
                        Intent oldIntent= (Intent) o;
                        //把原来的intent的跳转类信息保存起来
//                        Intent intent=new Intent();
                        DebugLog.d("原来要跳转的getAction:"+oldIntent.getAction());
                        DebugLog.d("原来要跳转的getPackage:"+oldIntent.getPackage());
                        DebugLog.d("原来要跳转的Component():"+(oldIntent.getComponent()==null?"null":oldIntent.getComponent().toString()));
                        //替换
                        ComponentName componentName=new ComponentName(context,TestAidlActivity.class);
                        oldIntent.putExtra("realComponentName",oldIntent.getComponent());
                        oldIntent.setComponent(componentName);
                        break;
                    }
                }
//                if (index!=-1){
//                    args[index]=
//                }
            }
            return method.invoke(target,args);
        }
    }

我们自己的callback

Handler.Callback myCallback=new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what==100){
            DebugLog.d("handleMessage");
            handleStartActivity(msg);
        }
        return false;
    };
};

查看H源码我们知道开启一个activity消息是100.所以我们这里处理msg.what==100的情况,看下面知道所有的生命周期回调都有对应的消息,我们都能进行拦截处理。目前我们暂时处理开启activity的消息

 

这个方法就好理解了,获取msg里面的intent对象,然后把里面的Component设置成我们保持的那个Component信息

private void handleStartActivity(Message msg){
    Object activityClientRecord= msg.obj;
    try {

        isReplaceOncreate=false;
        Field field=getField(activityClientRecord.getClass(),"intent");

        //拿到intent对象。
        Intent intent= (Intent) field.get(activityClientRecord);
        ComponentName componentName=intent.getParcelableExtra("realComponentName");

       String className=componentName.getClassName();

        Class<?> clazz=Class.forName(className);
        if (clazz.newInstance() instanceof AppCompatActivity){
            DebugLog.d("AppCompatActivity............");
            isReplaceOncreate=true;
        }
        //重新替换过来
        intent.setComponent(componentName);

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

Manifest.xml信息如下

 

TestAidi2Activity我并没有在清单文件中注册。运行后打印信息如下

 

所有代码上次到github:https://github.com/helloworld777/hello-jni

猜你喜欢

转载自blog.csdn.net/mr_lu_/article/details/80151366