Android之免清单注册启动Activity

在此立志:我要努力大学毕业进BAT


实习目标:Activity不需要注册在清单即可通过intent启动。

  • 有些文章叫做hook技术。大致内容为监听方法或者的调用或触发,期间修改方法参数或者返回值达到无须需改app源码即可修改app。如Xpose有插件可防止qq撤销消息。
  • 我们今天监听activity的启动然后进行方法修改,期间会用动态代理和大量的反射

Activity启动流程分析 第一章

首先学习activity启动流程:
假设我们界面有个按钮 点击后触发activityonclick方法

//activity
public void onClick(View view) {
        Intent intent = new Intent(this,SecondActivity.class);
        startActivity(intent);
    }
复制代码

按照我们的理解接下来的事情自然就是界面跳转了。我主要研究的startActivity内部实现的调用过程到启动一个activity流程。
这个方法的实现是在ContextImpl.java 中然后我们再看继承关系图:
这里写图片描述
结果方法继承关系里面并没有ContextImpl.java 。他其实被包裹在ContextWrapper.java

//ContextWrapper.java
public class ContextWrapper extends Context {
    Context mBase;
    ...省略其他代码
 @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }
    ...省略其他代码  
    }
复制代码

可以看到里面有个mBase属性对象。在调用startActivity的时候会调用mBasestartActivity方法。这个mBase就是ContextImpl.java

现在来分析ContextImpl.java
我们看看这个类的startActivity的方法的实现

//ContextImpl.java
    @Override
    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }
复制代码

直接看startActivity(intent, null); 方法,前一个方法是用于进程检测的。

 @Override
    public void startActivity(Intent intent, Bundle options) {
      //...省略代码 只看方法的重点
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
复制代码

可以看到此方法调用 mMainThread.getInstrumentation() 获取一个对象然后调用对象的方法啊execStartActivity。其中 mMainThread对象的声明为final ActivityThread mMainThread;

mMainThread.getInstrumentation() 查看实现如下

//ActivtyThread.java
 public Instrumentation getInstrumentation()
    {
        return mInstrumentation;
    }
复制代码

所以我们继续查看Instrumentation 对象的execStartActivity方法

//Instrumentation.java
 public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
                 //...省略方法其他方法
 int result = ActivityManagerNative.getDefault().startActivity(一堆参数);

//...     
        return null;
    }
复制代码

上面的代码就是传入的参数特别的参数太多。所以用四个字代替。我们看到ActivityManagerNative.getDefault() 获取一个对象然后调用startActivity()

ActivityManagerNative.getDefault() 方法探究

//ActivityManagerNative.java
  static public IActivityManager getDefault() {
        return gDefault.get();
    }
复制代码

gDefaultActivityManagerNative.java属性对象

声明如下:

//ActivityManagerNative.java
  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
复制代码

Singleton又是什么?这个是一个单例工具类,当你第一次调用此类的get() 会回调使用则自己实现的抽象方法create() 进而进行单例操作

此类完整代码:

//Singleton.java
package android.util;
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;
        }
    }
}
复制代码

这类我们以后反射其拿到IActivityManager 大家可以简单可以

大家再回过头看看实现抽象的方法create()

//ActivityManagerNative.java
  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            //从系统服务管理器获取activityMnagerServer(这是一个系统服务,所以需要AIDL进行进程间通信)
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
           //利用IBinder 对象转化为对应实例化接口
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
复制代码

从上面的例子可以看到,看到ActivityManagerNative.getDefault() 返回了远程服务对象IActivityManager 接口。

再回头看看Instrumentation 对象的execStartActivity方法

//Instrumentation.java
 public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
                 //...省略方法其他方法
 int result = ActivityManagerNative.getDefault().startActivity(一堆参数);

//...     
        return null;
    }
复制代码

可以得出以下结论:获取一个远程服务对象接口IActivityManager(并且是单例)然后调用IActivityManagerstartActivity方法。此时会回调到远程服务ActivityManagerServer的真正startActivity接口方法。里面会检查你传入的Intent对象的Activity是否在清单内,如果你的activity不在清单内,就是一个异常.但是我们不需要再看ActivityManagerServer源码,因为它是另一个app进程,所以无法干涉。如果可以干涉的话,Android系统就太不稳定了,肯定会有流氓开发者,让你按上一个app干扰其他app那不是奔了?所以我们无法hook到远程服务,但是IActivityManager我们却是可以用手脚的。因为那是服务端存在客户端一个代理接口对象、(这一块需要大家简单了解下AIDL的知识。如果不理解没关系,只需记住它做些手脚)

看完上面的很乱吧,看下图来屡屡思路:
(大家可以右键保存看大图)
这是个图片
这时候读者一定会问?既然ActivityManagerServer启动activity我们无法干涉那么后面干嘛?

我们仔细看上面的图IActivityManager调用的时候会把要启动的ActivityIntent去给ActivityManagerServer,假设我们此时用动态代理在IActivityManager调用远程服务之前,把在一个在清单文件注册过的ActivityIntent替换不就通过校验了?当然这时候你又会想既然替换通过了验证,但是我们真正要启动的Activity都被替了哪还有什么意义?这里过验证后系统没有真正启动Activity,因为这个ActivityManagerServer最终回调到 * * 启动的 。这里大家先不要着急,先通过验证再说。

第一个小目标 实现替换

假设我们我们有3个Activity第一个为MainActivity,第二个PlaceholdActivity 第三个为SecondActivity

  • MainActivity 在清单文件注册过,继承哪个Activity都无所谓
  • PlaceholdActivity 在清单文件注册过,继承哪个Activity都无所谓
  • SecondActivity没有清单文件注册过,并且继承Activity。(不要继承不然会失败AppCompatActivity)

其中我们在MainActivity 点击一个按钮触发其onclick方法调用startActivity跳转到SecondActivity

//MainActivity.java 
    public void onClick(View view) {
        Intent intent = new Intent(this,SecondActivity.class);
        startActivity(intent);
    }
复制代码

按照常理因为SecondActivity没有在清单文件注册过所以会失败,我们的目的把这个意图换成启动PlaceholdActivity 的意图

步骤1

创建一个Application类名为APP.java(记得在清单文件中关联哦)
重写attachBaseContext方法

  • attachBaseContext方法介绍:
    此方法会在oncreate方法前调用,还记得我们说的ContextWrapperContextImpl吗?再回过头看看吧(application也是继承ContextWrapper的)。
public class ContextWrapper extends Context {
    Context mBase;

    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
复制代码

attachBaseContext是在传入mBase对象赋值的回调的。此时你其实就可以使用上下文了。

此时我们的代码如下:

//APP.java
public class APP extends Application {
    //后面有用,用于保存原目标Activity的Intent的KEY 大家先看看
    private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
    private static final String TAG = "APP";

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
    }
}
复制代码

我们前面分析知道想要在过Activity的清单文件校验,就必须在IActivityManager调用startActivity方法之前进行Intent意图替换。
所以我们直接用动态代理IActivityManagerIActivityManager

首先要动态代理你必须拿到IActivityManager对象。而此对象保存在ActivityManagerNative的属性对象gDefault中。我们调用属性对象gDefault(Singleton类)的get方法或者得到其内部属性对象mInstance

mInstance是用于保存一个单例对象的实例。很混乱?再回头看看Singleton

//Singleton.java
public abstract class Singleton<T> {
      //保存对象实例
    private T mInstance;
    //Singleton对象为空会调用此方法得到实例
    protected abstract T create();

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

好了先获取gDefault对象吧,我们看看ActivityManagerNative对其的声明

//ActivityManagerNative.java
  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
复制代码

我们发现是个静态类,所以我们应该笑了,静态类通过反射机制很容易得到对象实例

  • 所以得到如下代码:
public class APP extends Application {
    private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
    private static final String TAG = "APP";

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        try {
            Class<?> ams_class = Class.forName("android.app.ActivityManagerNative");
            Field gDefault = ams_class.getDeclaredField("gDefault");
            //因为gDefault是private所以用发射机制打破访问限制
            gDefault.setAccessible(true);
            //得到gDefault实例对象 因为是stati属性所以get传入null即可得到
            Object gDefault_instance = gDefault.get(null);
            //单例工具类class
            Class<?> singleton_class = Class.forName("android.util.Singleton");
            //单例工具类的一个属性对象保存的是IActivityManager对象实例
            Field mInstance = singleton_class.getDeclaredField("mInstance");
            //打破封装访问
            mInstance.setAccessible(true);
            //传入gDefault对象实例得到IActivityManager对象实例
            final Object mInstance_instance = mInstance.get(gDefault_instance);
            //动态代理,这个不会我真不知道讲下去。此方法会返回一个实现IActivityManager接口的实例对象
            //拿着这个对象 替换单例工具类gDefault 的mInstance即可实现
            Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), mInstance_instance.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                   // IActivityManager接口方法太多 我们只关心它启动activity的接口方法
                    if (method.getName().equals("startActivity")) {
                        //获取调用此方法传入的参数 我们这里只要Intent替换,所以只需要Intent替换
                        for (int i = 0; i < args.length; i++) {
                            Object arg = args[i];
                            if (arg instanceof Intent) {
                                Intent intent = new Intent();
                                //PlaceholdActivity因为在清单文件注册过所以 创建一个新的Intent替换原来的SecondActivity的意图。后面用到
                                // 并将其保存到新的Intent中
                                ComponentName componentName = new ComponentName("com.example.fmy.myapplication", PlaceholdActivity.class.getName());
                                intent.setComponent(componentName);
                                intent.putExtra(KEY_EXTRA_TARGET_INTENT, ((Intent) arg));
                                args[i] = intent;
                                return method.invoke(mInstance_instance, args);
                            }
                        }

                    }

                    return method.invoke(mInstance_instance, args);
                }
            });
//         替换单例工具类gDefault 的mInstance实现动态代理
            mInstance.set(gDefault_instance, proxyInstance);
        } catch (Exception e) {
            e.printStackTrace();
        }
     }
}
复制代码

你运行代码后发现不管startActivity填入什么意图都是启动PlaceholdActivity就证明你成功了

Activity启动流程分析之Handler源码分析 第二章

这里只会简单讲讲不会过多。不然后脱离主旨
Looper循环器,不断从对应线程MassgaeQueue中死循环拿取mesaage然后发送到对应的handler

handler有消息的会调用如下方法:

    //Handler.java
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //如果mCallback 不为空并且处理后返回true 那么不会调用我们自己写handlerMessage处理消息。
            handleMessage(msg);
        }
    }
复制代码
  • 我们后面会用此mCallback 就行再次替换Intent

我们知道在学习javase的时候我们程序是从main方法进入,那么安卓的app入门方法在哪?ActivityThread.java中存在的main方法就是

//ActivityThread.java
public static void main(String[] args) {

        //...代码省略
        //Looper类一些初始化,会从ThreadLocal对象拿取当前线程的Looper
        //关于ThreadLocal不做过多讲述,简单就是用线程资源拷贝。
        //假设你有个变量a 你可以拷贝三份到ThreadLocal中不同的线程 
        Looper.prepareMainLooper();
        //...代码省略 
        ActivityThread thread = new ActivityThread();
         //开启一个子线程,又可以叫binder线程用于ams(ActivityManagerServer通AIDL来进程间通信,如果有信息那么从子线程发送message给主线程handler进行处理)
        thread.attach(false);

       //...代码省略
           //开启一个死循环遍历messageQueue(里面存放messager),如果有就,所以前面才开启一个子线程,不然怎么跟AMS通信?
        Looper.loop();

       //...代码省略
    }
复制代码

看看 thread.attach(false); 方法

//ActivityThread.java
  private void attach(boolean system) {
        sCurrentActivityThread = this;
          //...忽略代码
      }
复制代码

我们只看到方法 把当前线程对象(ActivityThread)保存到sCurrentActivityThread 我们后面会用到所以这里讲解。

我们前面第一章分析中到通过IActivityManager发送startActivity方法到ActivityManagerServer中,然后我们知道知道在ActivityThread类中main方法死循环前开启了个子线程,这个线程会接收ActivityManagerServer回馈的信息,信息封装在Message中然后添加主线程MeassgeQueue中,当主线程的Loop遍历到有信息的时候交给主线程的Handler处理。

  • ActivityManagerServer收到startActivity信息的信息的时候,会发送一个messageHandler处理。其中message.what=LAUNCH_ACTIVITYLAUNCH_ACTIVITY=100

我们看看ActivityHandler在哪

public class AcitvityThread{
    final H mH = new H();
    private class H extends Handler {
    //。。。代码省略
    }
}
复制代码

我们在看看mH这个Handler怎么处理private class H extends Handler 的事件

//ActivityThread.java
class H extends Handler{
//.....
public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                   //从msg拿出ActivityClientRecord 对象,里面包含启动activity的Intent
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                            //带着ActivityClientRecord 对象启动activity
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                }
    }
}
复制代码

大致可简单的理解从msg获取ActivityClientRecord 对象,内包含启动Intent启动意图等信息,还记得我们前面保存一个Intent到一个新的Intent上吗?这时候其实你可以从这个Intent取出来了。调用handleLaunchActivity去启动,最后大家想生命周期可以自己往下深究。

//ActivityThread.java
    static final class ActivityClientRecord {
        IBinder token;
        int ident;
        Intent intent;
        String referrer;
        //....
        }
复制代码

先来整理回调流程:
(因为懒省略很多细节)

这里写图片描述

所以我们可以handlermCallback赋值,此时handler回调lancherActivity的时候,我们知道mCallback不为空的时候,先会回调mCallback再根据其返回值在判断要不要对其自己实现的handlerMessagetrue的时候不会调用自己实现的方法)

所以先获取handler对象,而handler对象存在ActivityThread中,再来看看ActivityThread.java对其的声明

//ActivityThread.java
public final class ActivityThread {
 final H mH = new H();
 }
复制代码

因为只mH不是静态类型,所以先要获取ActivityThread 对象才能获取mH实例。
还记得我们前面分析ActivityThreadmain方法吗?
内部调用

public static void main(String[] args) {
//...
 ActivityThread thread = new ActivityThread();
        thread.attach(false);
    //...
}
复制代码

再看看 thread.attach(false); 方法

//ActivityThread.java
private void attach(boolean system) {
        sCurrentActivityThread = this;
        //....
        }
复制代码

可以看到ActivityThread对象保存在ActivityThread的属性对象sCurrentActivityThread

//ActivityThread.java
public final class ActivityThread {
 //...
  private static ActivityThread sCurrentActivityThread;
//...
}
复制代码

静态属性证明你懂的,你可以利用反射机制获取对象

所以综上代码综合可得:

//APP.java
package com.example.fmy.myapplication;

public class APP extends Application {
    private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
    private static final String TAG = "APP";

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

            //过检验代码省略
            Class<?> activityThread_class = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThread_field = activityThread_class.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThread_field.setAccessible(true);
            //获取静态属性对象实例
            Object activityThread_instance = sCurrentActivityThread_field.get(null);

            Field mH_field = activityThread_class.getDeclaredField("mH");

            mH_field.setAccessible(true);
            //获取handler对象实例
            Object mH_insance = mH_field.get(activityThread_instance);

            Field mCallback_field = Handler.class.getDeclaredField("mCallback");


            mCallback_field.setAccessible(true);
            //给handler添加mCallback
            mCallback_field.set(mH_insance, new Handler.Callback() {

                @Override
                public boolean handleMessage(Message msg) {
                    if (msg.what == 100) {
                        //得到ActivityClientRecord
                        Object obj = msg.obj;
                        try {
                            //得到Intent对象
                            Field intent_field = obj.getClass().getDeclaredField("intent");
                            intent_field.setAccessible(true);
                            Intent intent = (Intent) intent_field.get(obj);
                            //取出我们前面存在Intent里的原本没有注册在清单文件的Activity的Intent
                            Intent target_intent = intent.getParcelableExtra(KEY_EXTRA_TARGET_INTENT);

                            if (target_intent != null) {
                                intent.setComponent(target_intent.getComponent());
                            }

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

                    }
                    return false;

                }
            });

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


    }

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
复制代码

最后整合下过验证的代码如下:

//APP.java
package com.example.fmy.myapplication;

public class APP extends Application {
    private static final String KEY_EXTRA_TARGET_INTENT = "EXTRA_TARGET_INTENT";
    private static final String TAG = "APP";

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        try {
            Class<?> ams_class = Class.forName("android.app.ActivityManagerNative");
            Field gDefault = ams_class.getDeclaredField("gDefault");
            //因为gDefault是private所以用发射机制打破访问限制
            gDefault.setAccessible(true);
            //得到gDefault实例对象 因为是stati属性所以get传入null即可得到
            Object gDefault_instance = gDefault.get(null);
            //单例工具类class
            Class<?> singleton_class = Class.forName("android.util.Singleton");
            //单例工具类的一个属性对象保存的是IActivityManager对象实例
            Field mInstance = singleton_class.getDeclaredField("mInstance");
            //打破封装访问
            mInstance.setAccessible(true);
            //传入gDefault对象实例得到IActivityManager对象实例
            final Object mInstance_instance = mInstance.get(gDefault_instance);
            //动态代理,这个不会我真不知道讲下去。此方法会返回一个实现IActivityManager接口的实例对象
            //拿着这个对象 替换单例工具类gDefault 的mInstance即可实现
            Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), mInstance_instance.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // IActivityManager接口方法太多 我们只关心它启动activity的接口方法
                    if (method.getName().equals("startActivity")) {
                        //获取调用此方法传入的参数 我们这里只要Intent替换,所以只需要Intent替换
                        for (int i = 0; i < args.length; i++) {
                            Object arg = args[i];
                            if (arg instanceof Intent) {
                                Intent intent = new Intent();
                                //PlaceholdActivity因为在清单文件注册过所以 创建一个新的Intent替换原来的SecondActivity的意图。后面用到
                                // 并将其保存到新的Intent中
                                ComponentName componentName = new ComponentName("com.example.fmy.myapplication", PlaceholdActivity.class.getName());
                                intent.setComponent(componentName);
                                intent.putExtra(KEY_EXTRA_TARGET_INTENT, ((Intent) arg));
                                args[i] = intent;
                                return method.invoke(mInstance_instance, args);
                            }
                        }

                    }

                    return method.invoke(mInstance_instance, args);
                }
            });
//         替换单例工具类gDefault 的mInstance实现动态代理
            mInstance.set(gDefault_instance, proxyInstance);

            //过检验结束
            Class<?> activityThread_class = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThread_field = activityThread_class.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThread_field.setAccessible(true);
            //获取静态属性对象实例
            Object activityThread_instance = sCurrentActivityThread_field.get(null);

            Field mH_field = activityThread_class.getDeclaredField("mH");

            mH_field.setAccessible(true);
            //获取handler对象实例
            Object mH_insance = mH_field.get(activityThread_instance);

            Field mCallback_field = Handler.class.getDeclaredField("mCallback");


            mCallback_field.setAccessible(true);
            //给handler添加mCallback
            mCallback_field.set(mH_insance, new Handler.Callback() {

                @Override
                public boolean handleMessage(Message msg) {
                    if (msg.what == 100) {
                        //得到ActivityClientRecord
                        Object obj = msg.obj;
                        try {
                            //得到Intent对象
                            Field intent_field = obj.getClass().getDeclaredField("intent");
                            intent_field.setAccessible(true);
                            Intent intent = (Intent) intent_field.get(obj);
                            //取出我们前面存在Intent里的原本没有注册在清单文件的Activity的Intent
                            Intent target_intent = intent.getParcelableExtra(KEY_EXTRA_TARGET_INTENT);

                            if (target_intent != null) {
                                intent.setComponent(target_intent.getComponent());
                            }

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

                    }
                    return false;

                }
            });

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


    }

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
复制代码

GIT源码附上源码连接

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享