一、背景

如果有人问你,未在配置文件中注册的Activity能够启动吗。可能你一开始会答复不行,然而细细思考,你会发现,应用Android Hook等技术启动未注册的Activity也是能够的,这也是Android Hook 插件化技术原理的根底。

应用Android Hook 技术启动未注册的Activity,须要理解Java的反射机制 和Android App启动流程十分相熟。

上面,咱们从两点来解说Android Hook 技术启动未注册的Activity:

  • 通过对Instrumentation进行Hook
  • 通过对AMN进行Hook

二、 对startActivity办法进行Hook

通过查阅startActivity的源码,咱们能够看到startActivity最终都会走到startActivityFoResult()办法中,源码如下:

public void startActivityForResult(Intent intent, int requestCode, Bundle options) {    if(this.mParent == null) {        ActivityResult ar = this.mInstrumentation.execStartActivity(this, this.mMainThread.getApplicationThread(), this.mToken, this, intent, requestCode, options);        if(ar != null) {            this.mMainThread.sendActivityResult(this.mToken, this.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());        }        if(requestCode >= 0) {            this.mStartedActivity = true;        }    } else if(options != null) {        this.mParent.startActivityFromChild(this, intent, requestCode, options);    } else {        this.mParent.startActivityFromChild(this, intent, requestCode);    }}

接下来,咱们再看一下mInstrumentation.execStartActivity()办法。

public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {    IApplicationThread whoThread = (IApplicationThread)contextThread;    if(this.mActivityMonitors != null) {        Object e = this.mSync;        synchronized(this.mSync) {            int N = this.mActivityMonitors.size();            for(int i = 0; i < N; ++i) {                Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);                if(am.match(who, (Activity)null, intent)) {                    ++am.mHits;                    if(am.isBlocking()) {                        return requestCode >= 0?am.getResult():null;                    }                    break;                }            }        }    }    try {        intent.setAllowFds(false);        intent.migrateExtraStreamToClipData();        int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);        checkStartActivityResult(var16, intent);    } catch (RemoteException var14) {        ;    }    return null;}

而execStartActivity()办法最终又会走到checkStartActivityResult()办法。所以,如果咱们想要对startActivity办法进行Hook么,那么就须要在checkStartActivityResult()办法之前进行Hook。

对mInstrumentation进行Hook

接下来,咱们应用一个简略的例子:打印日志来阐明如果应用mInstrumentation进行Hook

首先,关上Activity.class类,在外面咱们能够Activity.class类中定义了公有变量Instrumentation。

private Instrumentation mInstrumentation;

咱们要做的就是批改这个公有变量的值,在执行execStartActivity()办法前打印一行日志。首先,咱们通过反射来获取这一公有变量。

Instrumentation instrumentation = (Instrumentation) Reflex.getFieldObject(Activity.class,MainActivity.this,"mInstrumentation");

而后,将这个Instrumentation替换成咱们本人的Instrumentation,所以上面咱们新建MyInstrumentation继承自Instrumentation,并且MyInstrumentation的execStartActivity办法不变。

public class MyInstrumentation extends Instrumentation {    private Instrumentation instrumentation;    public MyInstrumentation(Instrumentation instrumentation) {        this.instrumentation = instrumentation;    }    public  ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {        Log.d("MyInstrumentation","Instrumentation Hook11111");        Class[] classes = {Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class, Bundle.class};        Object[] objects = {who,contextThread,token,target,intent,requestCode,options};        Log.d("MyInstrumentation","Instrumentation Hook22222");        return (ActivityResult) ReflexUtil.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects);    }}

相熟Java反射的同学都晓得,咱们能够应用Class.forName(name)来获取类名,也能够应用getDeclaredMethod来获取类的参数。为了方便使用,咱们对这些罕用的反射进行了封装。

public class ReflexUtil {    /**     * 获取无参构造函数     * @param className     * @return     */    public static Object createObject(String className) {        Class[] pareTyples = new Class[]{};        Object[] pareVaules = new Object[]{};        try {            Class r = Class.forName(className);            return createObject(r, pareTyples, pareVaules);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return null;    }    /**     * 获取无参构造方法     * @param clazz     * @return     */    public static Object createObject(Class clazz) {        Class[] pareTyple = new Class[]{};        Object[] pareVaules = new Object[]{};        return createObject(clazz, pareTyple, pareVaules);    }    /**     * 获取一个参数的构造函数  已知className     *     * @param className     * @param pareTyple     * @param pareVaule     * @return     */    public static Object createObject(String className, Class pareTyple, Object pareVaule) {        Class[] pareTyples = new Class[]{pareTyple};        Object[] pareVaules = new Object[]{pareVaule};        try {            Class r = Class.forName(className);            return createObject(r, pareTyples, pareVaules);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return null;    }    /**     * 获取单个参数的构造方法 已知类     *     * @param clazz     * @param pareTyple     * @param pareVaule     * @return     */    public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) {        Class[] pareTyples = new Class[]{pareTyple};        Object[] pareVaules = new Object[]{pareVaule};        return createObject(clazz, pareTyples, pareVaules);    }    /**     * 获取多个参数的构造方法 已知className     * @param className     * @param pareTyples     * @param pareVaules     * @return     */    public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) {        try {            Class r = Class.forName(className);            return createObject(r, pareTyples, pareVaules);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return null;    }    /**     * 获取构造方法     *     * @param clazz     * @param pareTyples     * @param pareVaules     * @return     */    public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) {        try {            Constructor ctor = clazz.getDeclaredConstructor(pareTyples);            ctor.setAccessible(true);            return ctor.newInstance(pareVaules);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 获取多个参数的办法     * @param obj     * @param methodName     * @param pareTyples     * @param pareVaules     * @return     */    public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) {        if (obj == null) {            return null;        }        try {            //调用一个private办法 //在指定类中获取指定的办法            Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);            method.setAccessible(true);            return method.invoke(obj, pareVaules);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 获取一个参数的办法     * @param obj     * @param methodName     * @param pareTyple     * @param pareVaule     * @return     */    public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) {        Class[] pareTyples = {pareTyple};        Object[] pareVaules = {pareVaule};        return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);    }    /**     * 获取无参办法     * @param obj     * @param methodName     * @return     */    public static Object invokeInstanceMethod(Object obj, String methodName) {        Class[] pareTyples = new Class[]{};        Object[] pareVaules = new Object[]{};        return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);    }    /**     * 无参静态方法     * @param className     * @param method_name     * @return     */    public static Object invokeStaticMethod(String className, String method_name) {        Class[] pareTyples = new Class[]{};        Object[] pareVaules = new Object[]{};        return invokeStaticMethod(className, method_name, pareTyples, pareVaules);    }    /**     * 获取一个参数的静态方法     * @param className     * @param method_name     * @param pareTyple     * @param pareVaule     * @return     */    public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) {        Class[] pareTyples = new Class[]{pareTyple};        Object[] pareVaules = new Object[]{pareVaule};        return invokeStaticMethod(className, method_name, pareTyples, pareVaules);    }    /**     * 获取多个参数的静态方法     * @param className     * @param method_name     * @param pareTyples     * @param pareVaules     * @return     */    public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) {        try {            Class obj_class = Class.forName(className);            return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 无参静态方法     * @param method_name     * @return     */    public static Object invokeStaticMethod(Class clazz, String method_name) {        Class[] pareTyples = new Class[]{};        Object[] pareVaules = new Object[]{};        return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);    }    /**     * 一个参数静态方法     * @param clazz     * @param method_name     * @param classType     * @param pareVaule     * @return     */    public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) {        Class[] classTypes = new Class[]{classType};        Object[] pareVaules = new Object[]{pareVaule};        return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);    }    /**     * 多个参数的静态方法     * @param clazz     * @param method_name     * @param pareTyples     * @param pareVaules     * @return     */    public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules) {        try {            Method method = clazz.getDeclaredMethod(method_name, pareTyples);            method.setAccessible(true);            return method.invoke(null, pareVaules);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    public static Object getFieldObject(String className, Object obj, String filedName) {        try {            Class obj_class = Class.forName(className);            return getFieldObject(obj_class, obj, filedName);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return null;    }    public static Object getFieldObject(Class clazz, Object obj, String filedName) {        try {            Field field = clazz.getDeclaredField(filedName);            field.setAccessible(true);            return field.get(obj);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule) {        try {            Field field = clazz.getDeclaredField(filedName);            field.setAccessible(true);            field.set(obj, filedVaule);        } catch (Exception e) {            e.printStackTrace();        }    }    public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule) {        try {            Class obj_class = Class.forName(className);            setFieldObject(obj_class, obj, filedName, filedVaule);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }    public static Object getStaticFieldObject(String className, String filedName) {        return getFieldObject(className, null, filedName);    }    public static Object getStaticFieldObject(Class clazz, String filedName) {        return getFieldObject(clazz, null, filedName);    }    public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) {        setFieldObject(classname, null, filedName, filedVaule);    }    public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) {        setFieldObject(clazz, null, filedName, filedVaule);    }

能够看到,在MyInstrumentation类中,咱们间接反射execStartActivity办法来和默认的办法保持一致。

(ActivityResult) Reflex.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects)

而后,再应用咱们自定义的MyInstrumentation替换原来的Instrumentation即可。残缺代码如下:

Instrumentation instrumentation = (Instrumentation) ReflexUtil.getFieldObject(Activity.class,this,"mInstrumentation");MyInstrumentation instrumentation1 = new MyInstrumentation(instrumentation);ReflexUtil.setFieldObject(Activity.class,this,"mInstrumentation",instrumentation1);

2.2 对AMN进行Hook

如果大家去看execStartActivity()办法的源码,就可以看失去,execStartActivity()办法最终会走到ActivityManagerNative.getDefault().startActivity()办法。

try {    intent.setAllowFds(false);    intent.migrateExtraStreamToClipData();    int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);    checkStartActivityResult(var16, intent);} catch (RemoteException var14) {    ;}

持续看ActivityManagerNative的getDefault()办法。

public static IActivityManager getDefault() {    return (IActivityManager)gDefault.get();}public final T get() {    synchronized(this) {        if(this.mInstance == null) {            this.mInstance = this.create();        }        return this.mInstance;    }}

能够看出IActivityManager是一个接口,gDefault.get()返回的是一个泛型,如果间接应用反射是无奈动手的,所以咱们这里要用动静代理计划。

首先,咱们定义一个AmsHookHelperUtils类,在AmsHookHelperUtils类中解决反射代码。

public class AMNInvocationHandler implements InvocationHandler {    private String actionName = "startActivity";    private Object target;    public AMNInvocationHandler(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if (method.getName().equals(actionName)) {            Log.d("AMNInvocationHandler", "I Am AMN Hook");            return method.invoke(target, args);        }        return method.invoke(target, args);    }}

所有的代理类都要实现InvocationHandler接口,在invoke办法中method.invoke(target,args);示意的就是执行被代理对象所对应的办法。

而后,咱们将IActivityManager接口中gDefault字段替换为咱们的代理类,如下。

ReflexUtil.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);

咱们定义一个AmsHookHelperUtil类,而后增加一个hook办法,外面应用代理的形式进行Hook。

public class AmsHookHelperUtil {    public static void hookAmn() throws ClassNotFoundException {        Object gDefault = ReflexUtil.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");        Object mInstance = ReflexUtil.getFieldObject("android.util.Singleton",gDefault,"mInstance");        Class<?> classInterface = Class.forName("android.app.IActivityManager");        Object proxy = Proxy.newProxyInstance(classInterface.getClassLoader(),                new Class<?>[]{classInterface},new AMNInvocationHandler(mInstance));        ReflexUtil.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);    }}

三、如何启动一个未注册的Activity

如何启动一个未注册的Activity,首先咱们理解Activity的启动流程,如果还不理解Activity启动流程的,能够参考:Android Activity启动流程剖析。

假如,当初MainActivity,Main2Activity,Main3Activity,其中Main3Activity未注册,咱们在MainActivity中启动Main3Activity,当启动Main3Activity的时候,AMS会在配置文件中查看,是否有Main3Activity的配置信息如果不存在则报错,存在则启动Main3Activity,这是咱们曾经晓得的惯例流程。

所以,如果要启动未注册的Activity,那么咱们能够将要启动的Activity在发送给AMS之前,替换未曾经注册Activity Main2Activity,这样AMS就能够测验通过,当AMS要启动指标Activity的时候再将Main2Activity替换为真正要启动的Activity即可,也是很多热修复空间的的Hook的原理。

咱们依照下面逻辑先对startActivity办法进行Hook,这里采纳对AMN Hook的形式。和上述代码一样,不一样的中央在于mInstance的代理类不同。首先, 新建一个AMNInvocationHanlder对象同样继承自InvocationHandler,只拦挡startActivity办法。

if (method.getName().equals(actionName)){}

在这里咱们要做的就是将要启动的Main3Activity替换为Main2Activity,这样能绕过AMS的测验,首先咱们从指标办法中取出指标Activity。

Intent intent;int index = 0;for (int i = 0;i<args.length;i++){    if (args[i] instanceof Intent){        index = i;        break;    }}

你可能会问,怎么晓得args中肯定有intent类的参数。因为Java反射的invoke办法中最终会执行上面的代码:

return method.invoke(target,args);

而Android的startActivity()办法的源码如下。

int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);

所以说,args中必定有个intent类型的参数,获取实在指标Activity之后,咱们就能够获取指标的包名。

intent = (Intent) args[index];String packageName = intent.getComponent().getPackageName();

接下来,咱们新建一个Intent ,而后将intent设置为Main2Activity的替换者。

Intent newIntent = new Intent();ComponentName componentName = new ComponentName(packageName,Main2Activity.class.getName());newIntent.setComponent(componentName);args[index] = newIntent;

这样指标Activity就胜利替换了Main2Activity,不过这个替换者还要将本来的指标携带过来,期待真正关上的时候再替换回来,否则就真的启动这个替换者了。

newIntent.putExtra(AmsHookHelperUtils.TUREINTENT,intent);startActivity(new Intent(this,Main3Activity.class));

接下来,咱们要做的就是,如何将假冒者再从新替换为指标者。咱们能够应用ActivityThread通过mH发消息给AMS实现替换。

synchronized(this) {    Message msg = Message.obtain();    msg.what = what;    msg.obj = obj;    msg.arg1 = arg1;    msg.arg2 = arg2;    this.mH.sendMessage(msg);}

而后,AMS收到音讯后进行解决。

public void handleMessage(Message msg) {    ActivityThread.ActivityClientRecord data;    switch(msg.what) {    case 100:        Trace.traceBegin(64L, "activityStart");        data = (ActivityThread.ActivityClientRecord)msg.obj;        data.packageInfo = ActivityThread.this.getPackageInfoNoCheck(data.activityInfo.applicationInfo, data.compatInfo);        ActivityThread.this.handleLaunchActivity(data, (Intent)null);        Trace.traceEnd(64L);

mH是Handler类型的音讯解决类,所以sendMessage办法会调用callback。新建hookActivityThread办法,首先咱们获取以后的ActivityThread对象,而后获取对象的mH对象,将mH替换为咱们的本人自定义的MyCallback。

Object currentActivityThread = Reflex.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread");Handler mH = (Handler) Reflex.getFieldObject(currentActivityThread, "mH");ReflexUtil.setFieldObject(Handler.class, mH, "mCallback", new MyCallback(mH));

自定义MyCallback须要解决Handler.Callback接口,而后解决handleMessage办法。

@Overridepublic boolean handleMessage(Message msg) {    switch (msg.what) {              case 100:            handleLaunchActivity(msg);            break;        default:            break;    }    mBase.handleMessage(msg);    return true;}

而后,获取传递过去的指标对象,从指标对象中取出携带过去的实在对象,并将intent批改为实在指标对象的信息,这样就能够启动实在的指标Activity。

Object obj = msg.obj;Intent intent = (Intent) ReflexUtil.getFieldObject(obj, "intent");Intent targetIntent = intent.getParcelableExtra(AmsHookHelperUtils.TUREINTENT);intent.setComponent(targetIntent.getComponent());

以下是MyCallbackt的残缺代码:

public class MyCallback implements Handler.Callback {    Handler mBase;    public MyCallback(Handler base) {        mBase = base;    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            case 100:                handleLaunchActivity(msg);                break;            default:                break;        }        mBase.handleMessage(msg);        return true;    }    private void handleLaunchActivity(Message msg) {        Object obj = msg.obj;        Intent intent = (Intent) ReflexUtil.getFieldObject(obj, "intent");        Intent targetIntent = intent.getParcelableExtra(AmsHookHelperUtils.TUREINTENT);        intent.setComponent(targetIntent.getComponent());    }}

而后,再启动未注册的Main3Activity就能够胜利启动了,是不是很简略。